From aaba3ce7c1dae2ca9c65c802a82a9b0353069736 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Wed, 26 Jul 2023 17:26:52 -0400 Subject: [PATCH 01/38] Extract logic for use in review component --- .../schedule/helpers/schedule-helper.js | 29 +++++++++++++ .../time-slot-modal-question.vue | 41 ++++--------------- 2 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/layouts/schedule/helpers/schedule-helper.js b/src/layouts/schedule/helpers/schedule-helper.js index 82c756b5b..930a5ee6e 100644 --- a/src/layouts/schedule/helpers/schedule-helper.js +++ b/src/layouts/schedule/helpers/schedule-helper.js @@ -45,3 +45,32 @@ export function sumDateString(dateString, daysToAdd) { date.setDate(date.getDate() + daysToAdd); return convertDateToDateString(date); } + +export function militaryToTwelveHourTime(timeString) { + // Expected input: "HH:MM" + let hours = parseInt(timeString.split(":")[0]); + const minutes = timeString.split(":")[1]; + const meridianNotation = hours > 11 ? "PM" : "AM"; + + if (hours > 12) { + hours -= 12; + } + + return `${hours}:${minutes} ${meridianNotation}`; +} + +export function getDisplayTextForDurationLength(durationMinimum, durationMaximum) { + const isLongAppointment = durationMaximum >= 120; + const isDurationRange = durationMinimum !== durationMaximum; + + const adjustedMinimum = isLongAppointment ? durationMinimum / 60 : durationMinimum; + const adjustedMaximum = isLongAppointment ? durationMaximum / 60 : durationMaximum; + + const durationText = isDurationRange + ? `${adjustedMinimum} - ${adjustedMaximum}` + : adjustedMinimum; + + const unitText = isLongAppointment ? "hours" : "minutes"; + + return `${durationText} ${unitText}`; +} diff --git a/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue b/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue index 13f33ae05..4ff62cc4d 100644 --- a/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue +++ b/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue @@ -47,7 +47,11 @@ import buttonQuestion from "@/digital-components/button-question/button-question import timeSlotModalListButton from "./time-slot-modal-list-button/time-slot-modal-list-button"; // Helpers -import { convertDateStringToDate } from "@/layouts/schedule/helpers/schedule-helper"; +import { + convertDateStringToDate, + militaryToTwelveHourTime, + getDisplayTextForDurationLength, +} from "@/layouts/schedule/helpers/schedule-helper"; import { deepClone } from "@/helpers/object-helper"; // Validation - TODO: Move this somewhere more global? @@ -263,7 +267,7 @@ export default { cmsWidgetFieldMappings.DURATION ); - const inshopDurationTime = this.getDisplayTextForDurationLength( + const inshopDurationTime = getDisplayTextForDurationLength( this.estimatedServiceMinutesMinimum, this.estimatedServiceMinutesMaximum ); @@ -346,33 +350,6 @@ export default { this.$emit("update:modelValue", this.selectedValue); this.closeModal(); }, - getDisplayTextForMilitaryTime(militaryTimeInput) { - // Expected input: "HH:MM" - let hours = parseInt(militaryTimeInput.split(":")[0]); - const minutes = militaryTimeInput.split(":")[1]; - const meridianNotation = hours > 11 ? "PM" : "AM"; - - if (hours > 12) { - hours -= 12; - } - - return `${hours}:${minutes} ${meridianNotation}`; - }, - getDisplayTextForDurationLength(durationMinimum, durationMaximum) { - const isLongAppointment = durationMaximum >= 120; - const isDurationRange = durationMinimum !== durationMaximum; - - const adjustedMinimum = isLongAppointment ? durationMinimum / 60 : durationMinimum; - const adjustedMaximum = isLongAppointment ? durationMaximum / 60 : durationMaximum; - - const durationText = isDurationRange - ? `${adjustedMinimum} - ${adjustedMaximum}` - : adjustedMinimum; - - const unitText = isLongAppointment ? "hours" : "minutes"; - - return `${durationText} ${unitText}`; - }, getRelevantDropOffCmsWidgetNameForSelectedTimeSlot( selectedTimeSlotId, isSameDayRelevant = false @@ -387,7 +364,7 @@ export default { }, getAvailableTimeSlotsForInshop(timeSlotsForSelectedDate) { return timeSlotsForSelectedDate.map((timeSlot) => { - const readableTime = this.getDisplayTextForMilitaryTime(timeSlot.startTime); + const readableTime = militaryToTwelveHourTime(timeSlot.startTime); return { value: timeSlot.id, buttonLabel: readableTime, @@ -415,9 +392,9 @@ export default { }, getAvailableTimeSlotsForMobile(timeSlotsForSelectedDate) { const availableTimeSlots = timeSlotsForSelectedDate.map((timeSlot) => { - const readableTime = `${this.getDisplayTextForMilitaryTime( + const readableTime = `${militaryToTwelveHourTime( timeSlot.startTime - )} - ${this.getDisplayTextForMilitaryTime(timeSlot.endTime)}`; + )} - ${militaryToTwelveHourTime(timeSlot.endTime)}`; return { value: timeSlot.id, buttonLabel: readableTime, From 6728235ed48d731acda42c43224a9d9cb763ac0d Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Wed, 26 Jul 2023 17:27:09 -0400 Subject: [PATCH 02/38] Schedule review first draft --- .../schedule-review/schedule-review.vue | 90 +++++++++++++++++++ src/layouts/review/review.vue | 16 ++++ 2 files changed, 106 insertions(+) create mode 100644 src/layouts/review/review-sections/schedule-review/schedule-review.vue diff --git a/src/layouts/review/review-sections/schedule-review/schedule-review.vue b/src/layouts/review/review-sections/schedule-review/schedule-review.vue new file mode 100644 index 000000000..65d922821 --- /dev/null +++ b/src/layouts/review/review-sections/schedule-review/schedule-review.vue @@ -0,0 +1,90 @@ + + + diff --git a/src/layouts/review/review.vue b/src/layouts/review/review.vue index 8780e75a7..34b64a88b 100644 --- a/src/layouts/review/review.vue +++ b/src/layouts/review/review.vue @@ -69,6 +69,14 @@ cmsWidgetName="ServiceLocationTitleWidget" :serviceLocation="serviceLocationInfo" @edit-clicked="editServiceLocation" /> + +
+ +
@@ -94,6 +102,7 @@ import vehicleReview from "@/layouts/review/review-sections/vehicle-review/vehic import damageReview from "@/layouts/review/review-sections/damage-review/damage-review"; import servicePackageReview from "@/layouts/review/review-sections/service-package-review/service-package-review"; import serviceLocationReview from "@/layouts/review/review-sections/service-location-review/service-location-review"; +import scheduleReview from "@/layouts/review/review-sections/schedule-review/schedule-review"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { settleAllPromises } from "@/helpers/layout-helper"; @@ -189,6 +198,12 @@ export default { serviceLocationInfo() { return this.$store.getters.order.serviceLocation; }, + appointmentType() { + return this.$store.getters.order.serviceLocation.appointmentType; + }, + scheduleInfo() { + return this.$store.getters.order.schedule; + }, }, components: { funnelHeader, @@ -200,6 +215,7 @@ export default { damageReview, servicePackageReview, serviceLocationReview, + scheduleReview, }, }; From b5edeb96325823ca1ccb4b25a3e0c45a35213a46 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Thu, 27 Jul 2023 09:30:12 -0400 Subject: [PATCH 03/38] Add back-navigation --- src/layouts/review/review.vue | 6 ++++++ src/router/router-constants/navigation-scenarios.js | 1 + src/router/router-constants/routing-table.js | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/src/layouts/review/review.vue b/src/layouts/review/review.vue index 34b64a88b..6a81dcbac 100644 --- a/src/layouts/review/review.vue +++ b/src/layouts/review/review.vue @@ -175,6 +175,12 @@ export default { this.$route ); }, + editSchedule() { + this.$router.navigateWithoutSaving( + this.navigationScenarios.CLICKED_SCHEDULE_EDIT, + this.$route + ); + }, }, computed: { subHeaderTitle() { diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js index ae1c880ae..ce2f32134 100644 --- a/src/router/router-constants/navigation-scenarios.js +++ b/src/router/router-constants/navigation-scenarios.js @@ -51,6 +51,7 @@ const navigationScenarios = { CLICKED_DAMAGE_EDIT: "CLICKED_DAMAGE_EDIT", CLICKED_SERVICE_PACKAGE_EDIT: "CLICKED_SERVICE_PACKAGE_EDIT", CLICKED_SERVICE_LOCATION_EDIT: "CLICKED_SERVICE_LOCATION_EDIT", + CLICKED_SCHEDULE_EDIT: "CLICKED_SCHEDULE_EDIT", }; export { navigationScenarios }; diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js index 017e1372a..f1f83b445 100644 --- a/src/router/router-constants/routing-table.js +++ b/src/router/router-constants/routing-table.js @@ -484,6 +484,10 @@ const routingTable = function (store) { scenario: navigationScenarios.CLICKED_SERVICE_LOCATION_EDIT, destinationFmgPageValue: fmgPageValues.SERVICE_LOCATION, }, + { + scenario: navigationScenarios.CLICKED_SCHEDULE_EDIT, + destinationFmgPageValue: fmgPageValues.SCHEDULE, + }, ], }, ]; From 8bbbdf909a4c8cbbfd56580b36f43d239307b995 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Thu, 27 Jul 2023 09:30:23 -0400 Subject: [PATCH 04/38] Formatting --- .../review-sections/schedule-review/schedule-review.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/layouts/review/review-sections/schedule-review/schedule-review.vue b/src/layouts/review/review-sections/schedule-review/schedule-review.vue index 65d922821..3436cb7c2 100644 --- a/src/layouts/review/review-sections/schedule-review/schedule-review.vue +++ b/src/layouts/review/review-sections/schedule-review/schedule-review.vue @@ -80,7 +80,9 @@ export default { ); }, renderedMobileWindow() { - return `${militaryToTwelveHourTime(this.schedule?.startTime)} - ${militaryToTwelveHourTime(this.schedule?.endTime)}`; + return `${militaryToTwelveHourTime( + this.schedule?.startTime + )} - ${militaryToTwelveHourTime(this.schedule?.endTime)}`; }, }, components: { From a4785fa1c5374bf3b5b1ff2667f353d5ac699fac Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Thu, 27 Jul 2023 13:56:58 -0400 Subject: [PATCH 05/38] Add jobMinMinutes to store and use on review page --- .../review-sections/schedule-review/schedule-review.vue | 2 +- src/layouts/schedule/schedule.vue | 1 + .../time-slot-modal-question/time-slot-modal-question.vue | 3 +++ src/store/index.js | 5 +++++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/layouts/review/review-sections/schedule-review/schedule-review.vue b/src/layouts/review/review-sections/schedule-review/schedule-review.vue index 3436cb7c2..1cdfdea22 100644 --- a/src/layouts/review/review-sections/schedule-review/schedule-review.vue +++ b/src/layouts/review/review-sections/schedule-review/schedule-review.vue @@ -75,7 +75,7 @@ export default { }, renderedEstimatedDuration() { return getDisplayTextForDurationLength( - this.schedule?.jobMaxMinutes, + this.schedule?.jobMinMinutes, this.schedule?.jobMaxMinutes ); }, diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index d15e9acec..02fab79a9 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -463,6 +463,7 @@ export default { startTime: null, endTime: null, jobMaxMinutes: null, + jobMinMinutes: null, }; } }, diff --git a/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue b/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue index 4ff62cc4d..8b2e63fd3 100644 --- a/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue +++ b/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.vue @@ -92,6 +92,7 @@ export default { startTime: null, endTime: null, jobMaxMinutes: null, + jobMinMinutes: null, }), }, cmsWidgetName: String, @@ -449,6 +450,7 @@ export default { startTime: timeSlot.startTime, endTime: timeSlot.endTime, jobMaxMinutes: this.estimatedServiceMinutesMaximum.toString(), + jobMinMinutes: this.estimatedServiceMinutesMinimum.toString(), }; } @@ -458,6 +460,7 @@ export default { endTime: null, routeCode: null, jobMaxMinutes: null, + jobMinMinutes: null, }; }, }, diff --git a/src/store/index.js b/src/store/index.js index fb86b61c3..ec0a324c0 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -91,6 +91,7 @@ const getDefaultState = () => { endTime: null, routeCode: null, jobMaxMinutes: null, + jobMinMinutes: null, }, referralNumber: null, referralDate: null, @@ -279,6 +280,7 @@ export const mutations = { state.order.schedule.endTime = scheduleInfo.endTime; state.order.schedule.routeCode = scheduleInfo.routeCode; state.order.schedule.jobMaxMinutes = scheduleInfo.jobMaxMinutes; + state.order.schedule.jobMinMinutes = scheduleInfo.jobMinMinutes; } }, updateCustomerDetails(state, detailsInfo) { @@ -365,6 +367,7 @@ export const mutations = { state.order.schedule.endTime = null; state.order.schedule.routeCode = null; state.order.schedule.jobMaxMinutes = null; + state.order.schedule.jobMinMinutes = null; //premium appointment fee used on schedule page also needs reset when schedule is reset const supportingItems = state.order.lineItems.supportingItems; @@ -506,6 +509,7 @@ export const mutations = { state.order.schedule.endTime = sessionInformation.order.schedule?.endTime; state.order.schedule.routeCode = sessionInformation.order.schedule?.routeCode; state.order.schedule.jobMaxMinutes = sessionInformation.order.schedule?.jobMaxMinutes; + state.order.schedule.jobMinMinutes = sessionInformation.order.schedule?.jobMinMinutes; }, updateExperiments(state, experiments) { state.applicationUser.experiments = experiments; @@ -1464,6 +1468,7 @@ export const actions = { endTime: order.schedule?.endTime, routeCode: order.schedule?.routeCode, jobMaxMinutes: order.schedule?.jobMaxMinutes, + jobMinMinutes: order.schedule?.jobMinMinutes, }, existingPromoCode: null, referralCorrelationId: order.referralCorrelationId, From 2a746bd7be971a875dfc94e2fe2fecaf82352a3a Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Fri, 28 Jul 2023 13:23:27 -0400 Subject: [PATCH 06/38] Move copy structure to cms + state getters. --- .../schedule-review/schedule-review.vue | 61 ++----------------- src/layouts/review/review.vue | 6 +- .../schedule/helpers/schedule-helper.js | 1 + src/store/index.js | 32 ++++++++++ 4 files changed, 42 insertions(+), 58 deletions(-) diff --git a/src/layouts/review/review-sections/schedule-review/schedule-review.vue b/src/layouts/review/review-sections/schedule-review/schedule-review.vue index 1cdfdea22..654676829 100644 --- a/src/layouts/review/review-sections/schedule-review/schedule-review.vue +++ b/src/layouts/review/review-sections/schedule-review/schedule-review.vue @@ -1,22 +1,17 @@ + + diff --git a/src/router/index.js b/src/router/index.js index e3dc5ab2c..6d812f847 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -29,12 +29,18 @@ import { experimentTriggers } from "../constants/experiments"; import { applicationConfig } from "../constants/application-config"; import review from "@/layouts/review/review"; +import paymentMethod from "@/layouts/payment-method/payment-method"; const routes = [ { path: "/review", // This is a temporary route for testing. name: "review", component: review, }, + { + path: "/payment-method", // This is a temporary route for testing. + name: "payment-method", + component: paymentMethod, + }, { path: "/", name: "root", diff --git a/src/router/router-constants/fmgPage-values.js b/src/router/router-constants/fmgPage-values.js index 1ce76d5e8..fc8841c13 100644 --- a/src/router/router-constants/fmgPage-values.js +++ b/src/router/router-constants/fmgPage-values.js @@ -20,6 +20,7 @@ const fmgPageValues = { SCHEDULE: "schedule", CUSTOMER_DETAILS: "customer-details", REVIEW: "review", + PAYMENT_METHOD: "payment-method", }; export { fmgPageValues }; From dc3aab18a7405c2e6995274c542066c812727860 Mon Sep 17 00:00:00 2001 From: Leah Schumann Date: Fri, 4 Aug 2023 08:05:06 -0400 Subject: [PATCH 23/38] CSR-1381 - Prettified --- src/layouts/payment-method/payment-method.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/payment-method/payment-method.spec.js b/src/layouts/payment-method/payment-method.spec.js index 66012402c..3d0843e10 100644 --- a/src/layouts/payment-method/payment-method.spec.js +++ b/src/layouts/payment-method/payment-method.spec.js @@ -1 +1 @@ -test.todo("some test to be written in the future"); \ No newline at end of file +test.todo("some test to be written in the future"); From 792c379080daa30c920b4ba9da216b7d48ce76d4 Mon Sep 17 00:00:00 2001 From: CarlNation Date: Fri, 4 Aug 2023 11:49:13 -0400 Subject: [PATCH 24/38] CSR-1588 persist referral seq number and send to analytics --- src/constants/store-mutations.js | 1 + .../heritage-integration/order-helper.js | 1 + src/mixins/analytics-mixin.js | 4 ++-- src/store/index.js | 19 +++++++++++++------ src/store/store.spec.js | 1 + 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index d4e07cf0a..2cf59db27 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -40,6 +40,7 @@ const storeMutations = { // ORDER MUTATIONS UPDATE_REFERRAL_NUMBER: "updateReferralNumber", + UPDATE_REFERRAL_SEQUENCE_NUMBER: "updateReferralSequenceNumber", UPDATE_REFERRAL_DATE: "updateReferralDate", UPDATE_REFERRAL_CORRELATION_ID: "updateReferralCorrelationId", UPDATE_PARENT_ACCT_NUMBER: "updateParentAcctNumber", diff --git a/src/helpers/heritage-integration/order-helper.js b/src/helpers/heritage-integration/order-helper.js index 010c39ec9..01f5a5d67 100644 --- a/src/helpers/heritage-integration/order-helper.js +++ b/src/helpers/heritage-integration/order-helper.js @@ -114,6 +114,7 @@ async function saveSessionHelper() { storeActions.UPDATE_STORE_WITH_SAVE_SESSION_RESPONSE, { referralNumber: savedSessionInfo.data.referralNumber?.toString(), + referralSequenceNumber: savedSessionInfo.data.referralSequenceNumber, referralCorrelationId: savedSessionInfo.data.referralCorrelationId, referralDate: savedSessionInfo.data.referralDate, parentAccountNumber: savedSessionInfo.data.parentAccountNumber?.toString(), diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 3d0f61104..8637cfa64 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -38,7 +38,7 @@ export default { event: pageEvent, shouldUseSessionId: false, experimentsForUser: store.getters.applicationUser.experiments, - referralNumber: store.getters.order.referralNumber, + referralSequenceNumber: store.getters.order.referralSequenceNumber ?? store.getters.order.referralNumber, parentAccountNumber: store.getters.order.payment.parentAccountNumber, }; @@ -59,7 +59,7 @@ export default { value: value, shouldUseSessionId: false, experimentsForUser: store.getters.applicationUser.experiments, - referralNumber: store.getters.order.referralNumber, + referralSequenceNumber: store.getters.order.referralSequenceNumber ?? store.getters.order.referralNumber, parentAccountNumber: store.getters.order.payment.parentAccountNumber, }; diff --git a/src/store/index.js b/src/store/index.js index 99dc5bdaa..e91962a11 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -93,6 +93,7 @@ const getDefaultState = () => { jobMaxMinutes: null, }, referralNumber: null, + referralSequenceNumber: null, referralDate: null, referralCorrelationId: null, eon: null, @@ -185,6 +186,9 @@ export const mutations = { updateReferralNumber(state, referralNumber) { state.order.referralNumber = referralNumber; }, + updateReferralSequenceNumber(state, referralSequenceNumber) { + state.order.referralSequenceNumber = referralSequenceNumber; + }, updateReferralDate(state, referralDate) { state.order.referralDate = referralDate; }, @@ -398,6 +402,7 @@ export const mutations = { // Misc Mutations updateStateWithOrderInformation(state, sessionInformation) { state.order.referralNumber = sessionInformation.order.referralNumber; + state.order.referralSequenceNumber = sessionInformation.order.referralSequenceNumber; state.order.referralDate = sessionInformation.order.referralDate; state.order.referralCorrelationId = sessionInformation.order.referralCorrelationId; state.order.eon = sessionInformation.order.eon; @@ -863,6 +868,7 @@ export const actions = { context, { referralNumber, + referralSequenceNumber, referralDate, referralCorrelationId, eon, @@ -872,6 +878,7 @@ export const actions = { } ) { context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber); + context.commit(storeMutations.UPDATE_REFERRAL_SEQUENCE_NUMBER, referralSequenceNumber); context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate); context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId); context.commit(storeMutations.UPDATE_EON, eon); @@ -891,7 +898,7 @@ export const actions = { event, shouldUseSessionId, experimentsForUser, - referralNumber, + referralSequenceNumber, parentAccountNumber, } ) { @@ -905,7 +912,7 @@ export const actions = { event: event, shouldUseSessionId: shouldUseSessionId, experimentsForUser: experimentsForUser, - referralNumber: referralNumber, + referralSequenceNumber: referralSequenceNumber, parentAccountNumber: parentAccountNumber, }; @@ -939,7 +946,7 @@ export const actions = { value, shouldUseSessionId, experimentsForUser, - referralNumber, + referralSequenceNumber, parentAccountNumber, } ) { @@ -955,7 +962,7 @@ export const actions = { value: value, shouldUseSessionId: shouldUseSessionId, experimentsForUser: experimentsForUser, - referralNumber: referralNumber, + referralSequenceNumber: referralSequenceNumber, parentAccountNumber: parentAccountNumber, }; @@ -1456,8 +1463,8 @@ export const actions = { existingPromoCode: null, referralCorrelationId: order.referralCorrelationId, referralDate: order.referralDate, - referralNumber: order.referralNumber?.toString(), // TODO It'd be nice to save these as strings in the first place - referralSequenceNumber: order.referralNumber?.toString(), // TODO Pass the referralSequence number once insurance flow creates it + referralNumber: order.referralNumber?.toString(), + referralSequenceNumber: order.referralSequenceNumber, eon: order.eon, }, }, diff --git a/src/store/store.spec.js b/src/store/store.spec.js index f56a7ca49..71411b0af 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -802,6 +802,7 @@ describe("Actions", () => { // Act actions.updateStoreWithSaveSessionResponse(context, { referralNumber: "123", + referralSequenceNumber: 123, referralDate: new Date().toUTCString(), referralCorrelationId: "xxx-xxx-xxx", parentAccountNumber: "167132", From 1f32f91398fe16272d3062c701c7a629cd7c408d Mon Sep 17 00:00:00 2001 From: CarlNation Date: Fri, 4 Aug 2023 12:56:11 -0400 Subject: [PATCH 25/38] CSR-1588 prettier --- src/mixins/analytics-mixin.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 8637cfa64..5836112cf 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -38,7 +38,9 @@ export default { event: pageEvent, shouldUseSessionId: false, experimentsForUser: store.getters.applicationUser.experiments, - referralSequenceNumber: store.getters.order.referralSequenceNumber ?? store.getters.order.referralNumber, + referralSequenceNumber: + store.getters.order.referralSequenceNumber ?? + store.getters.order.referralNumber, parentAccountNumber: store.getters.order.payment.parentAccountNumber, }; @@ -59,7 +61,9 @@ export default { value: value, shouldUseSessionId: false, experimentsForUser: store.getters.applicationUser.experiments, - referralSequenceNumber: store.getters.order.referralSequenceNumber ?? store.getters.order.referralNumber, + referralSequenceNumber: + store.getters.order.referralSequenceNumber ?? + store.getters.order.referralNumber, parentAccountNumber: store.getters.order.payment.parentAccountNumber, }; From 14c3fe0663388a4c8e839d6fa44d18c47eaf469d Mon Sep 17 00:00:00 2001 From: CarlNation Date: Fri, 4 Aug 2023 14:21:08 -0400 Subject: [PATCH 26/38] CSR-1588 Seq number should always be returned by safelite api so no need to fall back to referral number --- src/mixins/analytics-mixin.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 5836112cf..37615f68b 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -38,9 +38,7 @@ export default { event: pageEvent, shouldUseSessionId: false, experimentsForUser: store.getters.applicationUser.experiments, - referralSequenceNumber: - store.getters.order.referralSequenceNumber ?? - store.getters.order.referralNumber, + referralSequenceNumber: store.getters.order.referralSequenceNumber, parentAccountNumber: store.getters.order.payment.parentAccountNumber, }; @@ -61,9 +59,7 @@ export default { value: value, shouldUseSessionId: false, experimentsForUser: store.getters.applicationUser.experiments, - referralSequenceNumber: - store.getters.order.referralSequenceNumber ?? - store.getters.order.referralNumber, + referralSequenceNumber: store.getters.order.referralSequenceNumber, parentAccountNumber: store.getters.order.payment.parentAccountNumber, }; From 576e96afaed90e71953a55ad6970d729dc7c0a98 Mon Sep 17 00:00:00 2001 From: CarlNation <32103961+CarlNation@users.noreply.github.com> Date: Fri, 28 Jul 2023 13:34:43 -0400 Subject: [PATCH 27/38] Revert "Merge pull request #1278 from Safelite/feature/CSR-1582" This reverts commit 5c67a0859e4700049f6ab86ba5c6424e02e51117. --- .../heritage-integration/navigation-helper.js | 2 +- src/router/index.js | 16 +--------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/helpers/heritage-integration/navigation-helper.js b/src/helpers/heritage-integration/navigation-helper.js index d90562791..913fbcb1b 100644 --- a/src/helpers/heritage-integration/navigation-helper.js +++ b/src/helpers/heritage-integration/navigation-helper.js @@ -98,7 +98,7 @@ export async function skipVinLookupNotRepair() { /* Logic for getting the last "valid" page a user visited. */ -export async function getLatestPageForRedirection() { +async function getLatestPageForRedirection() { // If this is a non-CTA navigation, determine where to send the user based on page prerequisites. // This also works if a user has a 'fmg' start_type query string but no current order. // That shouldn't happen, but it's possible. diff --git a/src/router/index.js b/src/router/index.js index 2f28e06d5..11efa6080 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -19,7 +19,6 @@ import { loadSessionIfPresent, saveSession } from "@/helpers/heritage-integratio import { getPageToRouteExistingOrderTo, navigateToHeritageFunnel, - getLatestPageForRedirection, } from "@/helpers/heritage-integration/navigation-helper"; import baseMixin from "@/mixins/base-mixin"; @@ -125,21 +124,8 @@ const routes = [ .filter((x) => x.name === routeData[0].name)[0] .components.default(); - // The save quote link via heritage default.aspx... will land here. - // Heritage will attempt to send the customer to the lastest page in NextGen possible. - // If the link was created prior to reaching the end of NextGen, the save quote link - // was going to GoToFunnelStartOn404. This will send as deep into NextGen as possible. if (!arePagePrerequisitesValid(nextComponent)) { - var pageToRedirectTo = await getLatestPageForRedirection(); - if (pageToRedirectTo) { - return next({ - path: "/", - query: { fmgPage: pageToRedirectTo }, - params: to.params, - }); - } else { - await GoToFunnelStartOn404(next); - } + await GoToFunnelStartOn404(next); } // Assign current query string parameters, as well as our fmgPage one. From 1f2560f0a972557eede63447ca28d57a348c75fd Mon Sep 17 00:00:00 2001 From: Sneha Date: Wed, 9 Aug 2023 15:36:19 +0530 Subject: [PATCH 28/38] CSR-1418 --- src/constants/store-actions.js | 2 +- .../vehicle-banner/vehicle-banner.vue | 9 +- src/layouts/vehicle/vehicle.spec.js | 93 ++++++------------- src/layouts/vehicle/vehicle.vue | 42 +++++---- src/store/index.js | 22 ++--- src/store/store.spec.js | 23 ++--- 6 files changed, 78 insertions(+), 113 deletions(-) diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index fc0daf323..c4efe9d45 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -9,7 +9,7 @@ const storeActions = { GET_VEHICLE_MAKES: "getVehicleMakes", GET_VEHICLE_MODELS: "getVehicleModels", GET_VEHICLE_STYLES: "getVehicleStyles", - SET_VEHICLE: "setVehicle", + GET_VEHICLE: "getVehicle", GET_DAMAGE_OPTIONS: "getDamageOptions", GET_EVOX_IMAGE: "getEvoxImage", IS_VIN_OPTIONAL_VEHICLE: "isVinOptionalVehicle", diff --git a/src/fmg-components/vehicle-banner/vehicle-banner.vue b/src/fmg-components/vehicle-banner/vehicle-banner.vue index 8f7be0179..a9c8569ae 100644 --- a/src/fmg-components/vehicle-banner/vehicle-banner.vue +++ b/src/fmg-components/vehicle-banner/vehicle-banner.vue @@ -13,13 +13,20 @@ export default { required: true, }, cmsWidgetName: String, + displayVehicleImage: { + type: String, + required: false, + default: null, + }, }, computed: { vehicleImageToDisplay() { if (this.displayGenericVehicleImage) { return this.genericVehicleImage; } - + if (this.displayVehicleImage !== null) { + return this.displayVehicleImage; + } if ( this.$store.getters.vehicle.imageUrl === null || this.$store.getters.vehicle.imageUrl === "NULL" diff --git a/src/layouts/vehicle/vehicle.spec.js b/src/layouts/vehicle/vehicle.spec.js index 210281423..56b46292d 100644 --- a/src/layouts/vehicle/vehicle.spec.js +++ b/src/layouts/vehicle/vehicle.spec.js @@ -1,19 +1,17 @@ // Components import vehicle from "@/layouts/vehicle/vehicle.vue"; -// Supporting files +// Supporting Files +import { shallowMount } from "@vue/test-utils"; import { nextTick } from "vue"; -import { mount } from "@vue/test-utils"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; + +// Mock our module for promises. +jest.mock("@/helpers/layout-helper.js", () => ({ + settleAllPromises: jest.fn(), +})); describe("vehicle.vue", () => { - test('"Continue" button is enabled after YMMS is selected.', async () => { - const { wrapper } = setupMocks(); - - const continueButton = wrapper.get('[data-test-id="funnel-footer-main-button"]'); - - expect(continueButton.attributes()["aria-disabled"]).toBe("false"); - }); - test("arePagePrerequisitesValid should be true ", async () => { //Arrange const { wrapper } = setupMocks(); @@ -26,66 +24,33 @@ describe("vehicle.vue", () => { expect(arePagePrerequisitesValid).toBe(true); }); }); +describe("vehicle.vue", () => { + describe("navigation", () => { + test("if the continue button is clicked, navigate forward", async () => { + // Arrange + const { wrapper } = setupMocks(); -describe("navigation", () => { - test("forwardButtonAction should trigger navigateForward", async () => { - // Arrange - const { wrapper } = setupMocks(); - wrapper.vm.navigateForward = jest.fn(); + // Act + await wrapper.vm.forwardButtonAction(); - // Act - await wrapper.vm.forwardButtonAction(); - - //Assert - expect(wrapper.vm.navigateForward).toHaveBeenCalled(); + // Assert + expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled(); + }); }); }); -const FunnelFooterWidgetMockData = { - ForwardButtonText: "Continue", -}; - function setupMocks() { - const mockRoute = { - query: { - fmgPage: "vehicle", - }, - }; - - const mockRouter = { - navigate: jest.fn(), - }; - - const wrapper = mount(vehicle, { - global: { - mixins: [ - { - methods: { - getCmsContent: jest.fn((cmsWidgetName, fieldName) => { - if (cmsWidgetName === "FunnelFooterWidget") { - if (fieldName === "ForwardButtonText") { - return FunnelFooterWidgetMockData.ForwardButtonText; - } - } - return ""; - }), - getFooterInfoBoxHeight: jest.fn(() => 80), - }, - }, - ], - - mocks: { - $route: mockRoute, - $router: mockRouter, + const wrapper = shallowMount( + vehicle, + getMountOptions({ + router: { + navigate: jest.fn(), + navigate: jest.fn(), + navigateWithSaving: jest.fn(), + navigateWithoutSaving: jest.fn(), }, + }) + ); - stubs: { - FunnelHeader: true, - FunnelSubHeader: true, - VehicleBanner: true, - }, - }, - }); - - return { mockRoute, mockRouter, wrapper }; + return { wrapper }; } diff --git a/src/layouts/vehicle/vehicle.vue b/src/layouts/vehicle/vehicle.vue index 24ae0d215..13001e2fd 100644 --- a/src/layouts/vehicle/vehicle.vue +++ b/src/layouts/vehicle/vehicle.vue @@ -58,6 +58,7 @@ @@ -109,6 +110,13 @@ export default { selectedMake: this.selectedMakefromStore(), selectedModel: this.selectedModelfromStore(), selectedStyle: this.selectedStylefromStore(), + vehicle: { + carId: null, + category: null, + imageUrl: null, + imageVifNumber: null, + imageVifColor: null, + }, yearOptions: [], makeOptions: [], modelOptions: [], @@ -116,11 +124,6 @@ export default { }; }, - props: { - cmsWidgetName: String, - validationRules: String, - }, - async beforeRouteEnter(to, from, next) { // Call APIs @@ -244,7 +247,8 @@ export default { const result = await this.getModelOptions(this.selectedYear, make); this.modelOptions = result?.data; if (this.selectedMakefromStore() !== make) { - this.selectedModel = null; + if (this.modelOptions.length === 1) this.selectedModel = this.modelOptions[0]; + else this.selectedModel = null; this.selectedStyle = null; } } else { @@ -260,36 +264,40 @@ export default { ); this.styleOptions = result?.data; if (this.selectedModelfromStore() !== model) { - this.selectedStyle = null; + if (this.styleOptions.length === 1) this.selectedStyle = this.styleOptions[0]; + else this.selectedStyle = null; } } else { this.styleOptions = []; } }, - selectedStyle(style) { - this.setVehicle(this.selectedYear, this.selectedMake, this.selectedModel, style); + async selectedStyle(style) { + if (style) { + const result = await this.getVehicle( + this.selectedYear, + this.selectedMake, + this.selectedModel, + style + ); + this.vehicle = result?.data; + } }, }, methods: { - setVehicle(year, make, model, style) { - return this.dispatchStoreAction(this.storeActions.SET_VEHICLE, { + getVehicle(year, make, model, style) { + return this.dispatchStoreAction(this.storeActions.GET_VEHICLE, { year: year, make: make, model: model, style: style, }); }, - arePagePrerequisitesValid() { return true; }, async forwardButtonAction() { - return this.navigateForward(); - }, - - navigateForward() { this.dispatchStoreAction( storeActions.SAVE_VEHICLE, { @@ -297,12 +305,12 @@ export default { make: this.selectedMake, model: this.selectedModel, style: this.selectedStyle, + vehicle: this.vehicle, }, false ); this.$router.navigateWithSaving(this.navigationScenarios.CLICKED_FORWARD, this.$route); }, - initializeYearComponent(initialData) { this.yearOptions = initialData; }, diff --git a/src/store/index.js b/src/store/index.js index 2d5a2e898..3bddb4eb0 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -739,7 +739,7 @@ export const actions = { }); }, - setVehicle(context, { year, make, model, style }) { + getVehicle(context, { year, make, model, style }) { return globalMethods .callHttpClient({ methods: endpoints.GetVehicle.method, @@ -747,17 +747,6 @@ export const actions = { payload: {}, }) .then((response) => { - context.commit(storeMutations.UPDATE_CAR_ID, response.data.carId); - context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, response.data.category); - context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, response.data.imageUrl); - context.commit( - storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, - response.data.imageVifNumber - ); - context.commit( - storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, - response.data.imageVifColor - ); return response; }); }, @@ -1624,9 +1613,7 @@ export const actions = { } }, - saveVehicle(context, { year, make, model, style }) { - context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES); - context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES); + saveVehicle(context, { year, make, model, style, vehicle }) { context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES); if (context.state.order.vehicle.year !== year) { @@ -1641,6 +1628,11 @@ export const actions = { if (context.state.order.vehicle.style !== style) { context.commit(storeMutations.UPDATE_STYLE, style); } + context.commit(storeMutations.UPDATE_CAR_ID, vehicle.carId); + context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, vehicle.category); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, vehicle.imageUrl); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, vehicle.imageVifNumber); + context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, vehicle.imageVifColor); }, saveVehicleDamage( diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 08aa406c9..cecc0f394 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -484,12 +484,9 @@ describe("Actions", () => { expect(response.data).toEqual({ style: "4 DOOR SEDAN" }); }); - it("setVehicle action, should get vehicle data and set carId and vehicle category", async () => { + it("getVehicle action, should get vehicle data ", async () => { // Arrange const context = state; - const commit = jest.fn(); - - context.commit = commit; // Act globalMethods.callHttpClient.mockImplementation(() => { @@ -497,10 +494,8 @@ describe("Actions", () => { }); // Assert - const response = await actions.setVehicle(context, "C00000000"); + const response = await actions.getVehicle(context, "C00000000"); - expect(commit).toBeCalledWith(storeMutations.UPDATE_CAR_ID, "C00000000"); - expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE_CATEGORY, "CAR"); expect(response.data).toEqual({ carId: "C00000000", category: "CAR" }); }); @@ -1729,18 +1724,14 @@ describe("Actions", () => { make: "Honda", model: "Civic", style: "Sedan", + vehicle: { + carId: "C00000000", + category: "CAR", + }, }; actions.saveVehicle(context, payload); //Assert - expect(dispatch).toHaveBeenNthCalledWith( - 1, - storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES - ); - expect(dispatch).toHaveBeenNthCalledWith( - 2, - storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES - ); if (context.state.order.vehicle.year !== payload.year) { expect(commit).toBeCalledWith(storeMutations.UPDATE_YEAR, payload.year); } @@ -1753,6 +1744,8 @@ describe("Actions", () => { if (context.state.order.vehicle.style !== payload.style) { expect(commit).toBeCalledWith(storeMutations.UPDATE_STYLE, payload.style); } + context.commit(storeMutations.UPDATE_CAR_ID, payload.vehicle.carId); + context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, payload.vehicle.category); }); it("saveVehicleDamage, should wipe out damage if different", () => { From fe215b33c3e3b186506e90d67eed4103ca44d4e0 Mon Sep 17 00:00:00 2001 From: Leah Schumann Date: Wed, 9 Aug 2023 06:26:44 -0400 Subject: [PATCH 29/38] CSR-1381- Tech review changes A couple of things Mark found I forgot to fix --- src/fmg-components/funnel-sub-header/funnel-sub-header.vue | 4 ++-- src/layouts/payment-method/payment-method.vue | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/fmg-components/funnel-sub-header/funnel-sub-header.vue b/src/fmg-components/funnel-sub-header/funnel-sub-header.vue index e81d465a8..20e18ba1c 100644 --- a/src/fmg-components/funnel-sub-header/funnel-sub-header.vue +++ b/src/fmg-components/funnel-sub-header/funnel-sub-header.vue @@ -33,9 +33,9 @@ export default { props: { hasBackButton: Boolean, cmsWidgetName: String, - headerVerticalAlignment: { + headerColor: { type: String, - default: "center", + default: "dark-header", }, leftAlignHeader: { type: Boolean, diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 3f8357e7f..ff5cb0e35 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -54,9 +54,6 @@ export default { return {}; }, methods: { - openModalAction(modalName) { - this.$refs[modalName].openModal(); - }, arePagePrerequisitesValid() { // TODO return true; From e8a2d06692991b61a37a796507d38d7299dcd888 Mon Sep 17 00:00:00 2001 From: Sneha Date: Wed, 9 Aug 2023 18:12:57 +0530 Subject: [PATCH 30/38] Update vehicle-banner.vue --- src/fmg-components/vehicle-banner/vehicle-banner.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/fmg-components/vehicle-banner/vehicle-banner.vue b/src/fmg-components/vehicle-banner/vehicle-banner.vue index a9c8569ae..3354db13c 100644 --- a/src/fmg-components/vehicle-banner/vehicle-banner.vue +++ b/src/fmg-components/vehicle-banner/vehicle-banner.vue @@ -21,12 +21,12 @@ export default { }, computed: { vehicleImageToDisplay() { - if (this.displayGenericVehicleImage) { - return this.genericVehicleImage; - } if (this.displayVehicleImage !== null) { return this.displayVehicleImage; } + if (this.displayGenericVehicleImage) { + return this.genericVehicleImage; + } if ( this.$store.getters.vehicle.imageUrl === null || this.$store.getters.vehicle.imageUrl === "NULL" From 8608c689065ccb6ced90a8882f1322e4e3741227 Mon Sep 17 00:00:00 2001 From: Leah Schumann Date: Wed, 9 Aug 2023 10:19:58 -0400 Subject: [PATCH 31/38] CSR-1422 - Added unit tests and other tech review changes --- src/constants/store-mutations.js | 4 +- .../customer-details/customer-details.vue | 10 ++-- src/store/index.js | 8 +-- src/store/store.spec.js | 53 ++++++++++++++++--- 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index 7b57187c2..2c249750d 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -35,12 +35,10 @@ const storeMutations = { UPDATE_SERVICE_ZIP: "updateServiceZip", UPDATE_SERVICE_LOCATION: "updateServiceLocation", UPDATE_SERVICE_LOCATION_TECH_NOTES: "updateServiceLocationTechNotes", - UPDATE_TECH_NOTES: "updateTechNotes", UPDATE_SCHEDULE: "updateSchedule", - UPDATE_CUSTOMER_EMAIL_ADDRESS: "updateCustomerEmailAddress", - // CUSTOMER MUTATIONS + UPDATE_CUSTOMER_EMAIL_ADDRESS: "updateCustomerEmailAddress", UPDATE_CUSTOMER_DETAILS: "updateCustomerDetails", // ORDER MUTATIONS diff --git a/src/layouts/customer-details/customer-details.vue b/src/layouts/customer-details/customer-details.vue index be533117f..e91317ca9 100644 --- a/src/layouts/customer-details/customer-details.vue +++ b/src/layouts/customer-details/customer-details.vue @@ -32,7 +32,7 @@ + v-model="isSmsOptIn" /> { lastName: null, emailAddress: null, phoneNumber: null, - isSmsOptin: null, + isSmsOptIn: null, }, damage: { isRepair: null, @@ -291,7 +291,7 @@ export const mutations = { state.order.customer.lastName = customerDetails.lastName; state.order.customer.emailAddress = customerDetails.emailAddress; state.order.customer.phoneNumber = customerDetails.phoneNumber; - state.order.customer.isSmsOptin = customerDetails.isSmsOptin; + state.order.customer.isSmsOptIn = customerDetails.isSmsOptIn; } }, @@ -493,7 +493,7 @@ export const mutations = { state.order.customer.firstName = sessionInformation.order.customer.firstName; state.order.customer.lastName = sessionInformation.order.customer.lastName; state.order.customer.phoneNumber = sessionInformation.order.customer.phoneNumber; - state.order.customer.isSmsOptin = sessionInformation.order.customer.isSmsOptin; + state.order.customer.isSmsOptIn = sessionInformation.order.customer.isSmsOptIn; state.order.existingPromoCode = sessionInformation.order.existingPromoCode; state.applicationUser.experiments = sessionInformation.applicationUser.experiments; @@ -1406,7 +1406,7 @@ export const actions = { emailAddress: order.customer.emailAddress, firstName: order.customer.firstName, lastName: order.customer.lastName, - isSmsOptin: order.customer.isSmsOptin, + isSmsOptIn: order.customer.isSmsOptIn, phoneNumber: order.customer.phoneNumber, }, damage: { diff --git a/src/store/store.spec.js b/src/store/store.spec.js index cecc0f394..626e630eb 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -335,6 +335,28 @@ describe("Mutations", () => { expect(storeState.order.serviceLocation.state).toEqual("OH"); expect(storeState.order.serviceLocation.zipCodeCtu).toEqual("01820"); }); + + it("updateCustomerDetails, should set customer details in state", () => { + // Arrange + const storeState = state; + const customerDetails = { + firstName: "foo", + lastName: "bar", + emailAddress: "foo@bar.com", + phoneNumber: "555-555-5555", + isSmsOptIn: true, + }; + + // Act + mutations.updateCustomerDetails(storeState, customerDetails); + + // Assert + expect(state.order.customer.firstName).toEqual("foo"); + expect(state.order.customer.lastName).toEqual("bar"); + expect(state.order.customer.emailAddress).toEqual("foo@bar.com"); + expect(state.order.customer.phoneNumber).toEqual("555-555-5555"); + expect(state.order.customer.isSmsOptIn).toEqual(true); + }); }); describe("Actions", () => { @@ -938,7 +960,7 @@ describe("Actions", () => { ); }); - it("saveServiceZipCodeInfo, should call mutation and save zip code to state", () => { + it("saveServiceZipCodeInfo, should call mutation", () => { // Arrange const context = state; const commit = jest.fn(); @@ -953,7 +975,6 @@ describe("Actions", () => { // Assert expect(commit).toBeCalledWith(storeMutations.UPDATE_SERVICE_LOCATION, serviceZipCodeInfo); - expect(state.order.serviceLocation.zipCode).toEqual("43212"); }); it("saveServiceZipCodeInfo, should reset if zip code is different", () => { @@ -1022,7 +1043,7 @@ describe("Actions", () => { ); }); - it("saveServiceLocation, should call mutation and save service address to state", () => { + it("saveServiceLocation, should call mutation", () => { // Arrange const context = state; const commit = jest.fn(); @@ -1041,11 +1062,6 @@ describe("Actions", () => { // Assert expect(commit).toBeCalledWith(storeMutations.UPDATE_SERVICE_LOCATION, serviceLocation); - expect(state.order.serviceLocation.address).toEqual("123 Test Lane"); - expect(state.order.serviceLocation.city).toEqual("Columbus"); - expect(state.order.serviceLocation.zipCode).toEqual("43212"); - expect(state.order.serviceLocation.state).toEqual("OH"); - expect(state.order.serviceLocation.zipCodeCtu).toEqual("01820"); }); it("saveServiceLocation, should reset if zipcode is different", () => { @@ -1292,6 +1308,27 @@ describe("Actions", () => { expect(commit).not.toHaveBeenCalledWith(storeMutations.RESET_SCHEDULE); }); + it.only("saveCustomerDetails, should call mutation", () => { + // Arrange + const context = state; + const commit = jest.fn(); + context.commit = commit; + + const customerDetails = { + firstName: "foo", + lastName: "bar", + emailAddress: "foo@bar.com", + phoneNumber: "555-555-5555", + isSmsOptIn: true, + }; + + // Act + actions.saveCustomerDetails(context, customerDetails); + + // Assert + expect(commit).toBeCalledWith(storeMutations.UPDATE_CUSTOMER_DETAILS, customerDetails); + }); + it("saveGlassParts, should call mutation", () => { // Arrange const context = { From 4322a3f370abc280aab104aff88e6d35cd607e25 Mon Sep 17 00:00:00 2001 From: Leah Schumann Date: Wed, 9 Aug 2023 10:23:24 -0400 Subject: [PATCH 32/38] Removed .only and re-ran Unit tests --- src/store/store.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 626e630eb..82fa2109e 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -1308,7 +1308,7 @@ describe("Actions", () => { expect(commit).not.toHaveBeenCalledWith(storeMutations.RESET_SCHEDULE); }); - it.only("saveCustomerDetails, should call mutation", () => { + it("saveCustomerDetails, should call mutation", () => { // Arrange const context = state; const commit = jest.fn(); From e1b444ac4cf9520c87526cb520d438819bdf64f3 Mon Sep 17 00:00:00 2001 From: Leah Schumann Date: Wed, 9 Aug 2023 10:24:54 -0400 Subject: [PATCH 33/38] Initial Commit --- src/store/store.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 82fa2109e..e072eb164 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -1327,7 +1327,7 @@ describe("Actions", () => { // Assert expect(commit).toBeCalledWith(storeMutations.UPDATE_CUSTOMER_DETAILS, customerDetails); - }); + }); it("saveGlassParts, should call mutation", () => { // Arrange From 2e455177ae6a3c85256de0ff3a6d2d0ac8b097cf Mon Sep 17 00:00:00 2001 From: Leah Schumann Date: Wed, 9 Aug 2023 10:24:54 -0400 Subject: [PATCH 34/38] Prettified --- src/store/store.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 82fa2109e..e072eb164 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -1327,7 +1327,7 @@ describe("Actions", () => { // Assert expect(commit).toBeCalledWith(storeMutations.UPDATE_CUSTOMER_DETAILS, customerDetails); - }); + }); it("saveGlassParts, should call mutation", () => { // Arrange From 71205e5e7691771f61722642700a082b2d641117 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Thu, 10 Aug 2023 10:12:02 -0400 Subject: [PATCH 35/38] Update unit tests with jobMinMinutes --- .../time-slot-modal-question.spec.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.spec.js b/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.spec.js index 43d997892..93f8cfe01 100644 --- a/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.spec.js +++ b/src/layouts/schedule/time-slot-modal-question/time-slot-modal-question.spec.js @@ -16,6 +16,7 @@ describe("time-slot-modal-list-button-question.vue", () => { const startTime = "8:00 AM"; const endTime = "8:30 AM"; const jobMaxMinutes = 100; + const jobMinMinutes = 50; const expectedEmit = [ [ { @@ -24,6 +25,7 @@ describe("time-slot-modal-list-button-question.vue", () => { startTime: startTime, endTime: endTime, jobMaxMinutes: jobMaxMinutes.toString(), + jobMinMinutes: jobMinMinutes.toString(), }, ], ]; @@ -32,6 +34,7 @@ describe("time-slot-modal-list-button-question.vue", () => { customMountOptions: { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, + estimatedServiceMinutesMinimum: jobMinMinutes, dateAndTimeSlotData: { date: date, timeSlots: [ @@ -163,11 +166,13 @@ describe("time-slot-modal-list-button-question.vue Supplemental information", () const startTime = "8:00 AM"; const endTime = "8:30 AM"; const jobMaxMinutes = 100; + const jobMinMinutes = 50; const { wrapper } = setupMocks({ customMountOptions: { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, + estimatedServiceMinutesMinimum: jobMinMinutes, dateAndTimeSlotData: { date: date, timeSlots: [ @@ -201,11 +206,13 @@ describe("time-slot-modal-list-button-question.vue Disclaimer", () => { const startTime = "8:00 AM"; const endTime = "8:30 AM"; const jobMaxMinutes = 100; + const jobMinMinutes = 50; const { wrapper } = setupMocks({ customMountOptions: { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, + estimatedServiceMinutesMinimum: jobMinMinutes, dateAndTimeSlotData: { date: date, timeSlots: [ @@ -288,11 +295,13 @@ describe("time-slot-modal-list-button-question.vue Duration", () => { const startTime = "8:00 AM"; const endTime = "8:30 AM"; const jobMaxMinutes = 100; + const jobMinMinutes = 50; const { wrapper } = setupMocks({ customMountOptions: { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, + estimatedServiceMinutesMinimum: jobMinMinutes, dateAndTimeSlotData: { date: date, timeSlots: [ @@ -434,11 +443,13 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { const startTime = "8:00 AM"; const endTime = "8:30 AM"; const jobMaxMinutes = 100; + const jobMinMinutes = 50; const { wrapper } = setupMocks({ customMountOptions: { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, + estimatedServiceMinutesMinimum: jobMinMinutes, dateAndTimeSlotData: { date: date, timeSlots: [ @@ -470,11 +481,13 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { const startTime = "8:00 AM"; const endTime = "8:30 AM"; const jobMaxMinutes = 100; + const jobMinMinutes = 50; const { wrapper } = setupMocks({ customMountOptions: { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, + estimatedServiceMinutesMinimum: jobMinMinutes, dateAndTimeSlotData: { date: date, timeSlots: [ @@ -506,11 +519,13 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { const startTime = "8:00 AM"; const endTime = "8:30 AM"; const jobMaxMinutes = 100; + const jobMinMinutes = 50; const { wrapper } = setupMocks({ customMountOptions: { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, + estimatedServiceMinutesMinimum: jobMinMinutes, dateAndTimeSlotData: { date: date, timeSlots: [ @@ -542,11 +557,13 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { const startTime = "8:00"; const endTime = "8:30"; const jobMaxMinutes = 100; + const jobMinMinutes = 50; const { wrapper } = setupMocks({ customMountOptions: { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, + estimatedServiceMinutesMinimum: jobMinMinutes, dateAndTimeSlotData: { date: date, timeSlots: [ @@ -578,11 +595,13 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { const startTime = "8:00"; const endTime = "8:30"; const jobMaxMinutes = 100; + const jobMinMinutes = 50; const { wrapper } = setupMocks({ customMountOptions: { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, + estimatedServiceMinutesMinimum: jobMinMinutes, premiumAppointmentFee: { partType: PREMIUM_FEE_PART_TYPE, }, @@ -622,11 +641,13 @@ describe("time-slot-modal-list-button-question.vue Available Timeslots", () => { const startTime = "8:00"; const endTime = "8:30"; const jobMaxMinutes = 100; + const jobMinMinutes = 50; const { wrapper } = setupMocks({ customMountOptions: { propsData: { estimatedServiceMinutesMaximum: jobMaxMinutes, + estimatedServiceMinutesMinimum: jobMinMinutes, dateAndTimeSlotData: { date: date, timeSlots: [ From ab9407c12741113568c2ca98e58e0b15212d397f Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Thu, 10 Aug 2023 10:30:34 -0400 Subject: [PATCH 36/38] Fix text alignment for header on review page --- src/layouts/review/review.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/layouts/review/review.vue b/src/layouts/review/review.vue index 78f24c35f..fce6e4f59 100644 --- a/src/layouts/review/review.vue +++ b/src/layouts/review/review.vue @@ -10,13 +10,13 @@ Date: Mon, 14 Aug 2023 10:57:10 -0400 Subject: [PATCH 37/38] CSR-886: unit tests for Schedule page --- jest.config.js | 1 - .../address-lookup/address-lookup.spec.js | 1 - src/layouts/schedule/schedule.spec.js | 728 ++++++++++++++---- src/layouts/schedule/schedule.spec.js1 | 0 src/layouts/schedule/schedule.vue | 7 +- 5 files changed, 576 insertions(+), 161 deletions(-) create mode 100644 src/layouts/schedule/schedule.spec.js1 diff --git a/jest.config.js b/jest.config.js index 34a45f34a..633a99e67 100644 --- a/jest.config.js +++ b/jest.config.js @@ -17,7 +17,6 @@ module.exports = { "!src/ux-components/text-link/**/*.vue", "!src/layouts/vin-lookup/**/*.vue", //Temporary for Quote page testing "!src/common-components/funnel-header/menu-modal/**/*.vue", - "!src/layouts/schedule/*.vue", // Temp test exclusion while in development "!src/layouts/schedule/helpers/schedule-helper.js", // Temp test exclusion while in development "!src/layouts/review/*.vue", // Temp test exclusion while in development // END diff --git a/src/layouts/address-lookup/address-lookup.spec.js b/src/layouts/address-lookup/address-lookup.spec.js index 2c4b44950..63847588b 100644 --- a/src/layouts/address-lookup/address-lookup.spec.js +++ b/src/layouts/address-lookup/address-lookup.spec.js @@ -681,7 +681,6 @@ function setupMocks({ }, ], router: { - navigate: jest.fn(), navigate: jest.fn(), navigateWithSaving: jest.fn(), navigateWithoutSaving: jest.fn(), diff --git a/src/layouts/schedule/schedule.spec.js b/src/layouts/schedule/schedule.spec.js index 2c4bafc33..56b2ce13b 100644 --- a/src/layouts/schedule/schedule.spec.js +++ b/src/layouts/schedule/schedule.spec.js @@ -1,15 +1,77 @@ +// Components +import schedule from "@/layouts/schedule/schedule.vue"; + +// Supporting Files import { shallowMount } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; -import { applicationConfig } from "@/constants/application-config"; -import { storeActions } from "@/constants/store-actions"; -import schedule from "@/layouts/schedule/schedule.vue"; import store from "@/store"; -import * as navigateToHeritage from "@/helpers/heritage-integration/navigation-helper"; +import router from "@/router"; import { nextTick } from "vue"; +import baseMixin from "../../mixins/base-mixin"; -jest.mock("@/store", () => ({ - commit: jest.fn(), - dispatch: jest.fn(), +// Mock basemixin +jest.mock("@/mixins/base-mixin.js", () => ({ + methods: { + dispatchStoreAction: jest.fn().mockImplementation((storeAction) => { + if (storeAction === "getShopTimeSlots") { + return { + data: { + estimatedServiceMinutesMinimum: 90, + estimatedServiceMinutesMaximum: 120, + days: [ + { + date: "2023-12-01", + timeSlots: [ + { + id: "06747-01820-S-B*20424*7 AM", + startTime: "07:00", + endTime: "08:00", + offerPremium: false, + }, + ], + }, + ], + }, + }; + } + if (storeAction === "getMobilePremiumFee") { + return Promise.resolve({ + data: { + partNumber: "EARLY BIRD", + description: null, + partType: "EARLY BIRD", + laborAmount: 0, + sellingPrice: 14.99, + kitPrice: 0, + }, + }); + } + if (storeAction === "priceOrderItemsAndSaveServerData") { + return Promise.resolve([ + { + partNumber: "EARLY BIRD", + description: null, + partType: "EARLY BIRD", + laborAmount: 0, + sellingPrice: 14.99, + kitPrice: 0, + }, + ]); + } + if (storeAction === "saveSupportingItemsSuppressingStateResetting") { + return Promise.resolve([ + { + partNumber: "EARLY BIRD", + description: null, + partType: "EARLY BIRD", + laborAmount: 0, + sellingPrice: 14.99, + kitPrice: 0, + }, + ]); + } + }), + }, })); // Mock fetchCmsContentForPage @@ -18,50 +80,415 @@ jest.mock("@/helpers/cms-content-helper", () => ({ splitCopyOnCMSPlaceHolder: jest.fn(() => ["A", "B"]), })); -jest.mock( - "@/store", - () => { - return {}; - }, - { virtual: true } -); - -store.getters = { - order: { - schedule: { - date: "2020-01-01", +beforeEach(() => { + jest.restoreAllMocks(); + jest.clearAllMocks(); + store.getters = { + order: { + schedule: { + date: "2019-01-01", + startTime: "09:00", + endTime: "10:00", + routeCode: "000" + }, + lineItems: { + glassParts: [ + { + partNumber: "ABC123", + }, + ], + supportingItems: [], + }, + serviceLocation: { + appointmentType: "Inshop", + zipCode: "12345", + zipCodeCtu: "01234", + provider: { + providerNumber: "123", + }, + }, + damage: { + isRepair: false, + }, + referralNumber: "1234567", + }, + payment: { + isInsurance: true, }, lineItems: { glassParts: [], supportingItems: [], }, - serviceLocation: { - appointmentType: "Inshop", - }, - }, - lineItems: { - glassParts: [], - supportingItems: [], - }, -}; + }; +}); +afterEach(() => { + store.getters = {}; + jest.restoreAllMocks(); + jest.clearAllMocks(); +}); -describe("schedule.vue", () => { - test("Should navigateWithoutSaving", async () => { - //Arrange - const { wrapper } = setupMocks({ - customMountOptions: { - router: { - navigateWithSaving: jest.fn(), - }, - route: { schedule }, - }, +describe("schedule.vue...", () => { + describe("initial load", () => { + test("should pass arePagePrerequisitesValid with a mobile order and no providerNumber", () => { + //Arrange + const { wrapper } = setupMocks({}); + store.getters.order.serviceLocation.appointmentType = "Mobile"; + store.getters.order.serviceLocation.provider = null; + + //Act + const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); + + //Assert + expect(arePagePrerequisitesValid).toBe(true); + }); + test("should pass arePagePrerequisitesValid with a inshop order and providerNumber", () => { + //Arrange + const { wrapper } = setupMocks({}); + + //Act + const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); + + //Assert + expect(arePagePrerequisitesValid).toBe(true); + }); + test("should fail arePagePrerequisitesValid with a replace with no glass parts", async () => { + //Arrange + const { wrapper } = setupMocks({}); + store.getters.order.lineItems.glassParts = []; + + //Act + const arePagePrerequisitesValid2 = await wrapper.vm.arePagePrerequisitesValid(); + + //Assert + expect(arePagePrerequisitesValid2).toBe(false); + }); + test("should fail arePagePrerequisitesValid without isInsurance", () => { + //Arrange + const { wrapper } = setupMocks({}); + store.getters.payment.isInsurance = null; + + //Act + const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); + + //Assert + expect(arePagePrerequisitesValid).toBe(false); }); + test("should return newShopTimeSlots when getAvailableDatesMethod is called", async () => { + //Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.selectableDatesData = { + days: [], + }; + + //Act + const newShopTimeSlots = await wrapper.vm.getAvailableDatesMethod( + "2023-01-01", + "2023-01-31" + ); + + //Assert + expect(newShopTimeSlots).toStrictEqual({ + days: [ + { + date: "2023-12-01", + timeSlots: [ + { + endTime: "08:00", + id: "06747-01820-S-B*20424*7 AM", + offerPremium: false, + startTime: "07:00", + }, + ], + }, + ], + estimatedServiceMinutesMinimum: 90, + estimatedServiceMinutesMaximum: 120, + }); + }); + + test("should call API service in day ranges of 34 or less when getAvailableDatesMethod is called with large date ranges", async () => { + //Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.selectableDatesData = { + days: [], + }; + + //Act + await wrapper.vm.getAvailableDates.call( + wrapper.vm, + "2023-01-01", + "2023-03-31", + "Inshop", + "123" + ); + + //Assert + expect(baseMixin.methods.dispatchStoreAction).toHaveBeenCalledTimes(3); + expect(baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith( + "getShopTimeSlots", + expect.anything(), + expect.anything() + ); + }); + + describe("beforeRouteEnter function... ", () => { + test("should call next() and call all functions within next", async () => { + //Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.selectableDatesData = { + days: [], + }; + wrapper.vm.updateFooterButtonText = jest.fn(); + const nextFunction = jest.fn((c) => { + c(wrapper.vm); + }); + + //Act + await schedule.beforeRouteEnter.call( + wrapper.vm, + { query: { fmgPage: "schedule" } }, + undefined, + nextFunction + ); + + //Assert + expect(nextFunction).toHaveBeenCalled(); + expect(wrapper.vm.setCmsContent).toHaveBeenCalledWith("content"); + expect(wrapper.vm.$refs.datePicker.initializeComponent).toHaveBeenCalledWith( + expect.objectContaining({ + calendarViewDirection: "future", + }) + ); + expect(wrapper.vm.$refs.locationAlerts.initializeComponent).toHaveBeenCalled(); + expect(wrapper.vm.selectableDatesData).toStrictEqual( + expect.objectContaining({ + days: expect.any(Array), + estimatedServiceMinutesMaximum: expect.any(Number), + estimatedServiceMinutesMinimum: expect.any(Number) + }) + ); + expect(wrapper.vm.mobilePremiumAppointmentFee).toStrictEqual( + expect.objectContaining({ + partNumber: expect.any(String) + }) + ); + expect(wrapper.vm.updateFooterButtonText).toHaveBeenCalled(); + }); + + }); + + describe("computed properties...", () => { + + test("timeSlotsForSelectedDate should return timeslots if selected date is available", () => { + //Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.selectableDatesData = { + days: [{ + "date": "2022-11-11", + "timeSlots": [ + { + "id": "1820I-01820-M-I*20425*AM", + "startTime": "08:00", + "endTime": "12:00", + "offerPremium": true + }, + { + "id": "1820I-01820-M-I*20425*PM", + "startTime": "12:00", + "endTime": "17:00", + "offerPremium": false + } + ] + }], + }; + wrapper.setData({ + selectedDate: "2022-11-11" + }); + + //Act + const testValue = wrapper.vm.timeSlotsForSelectedDate; + + //Assert + expect(testValue).toStrictEqual( + expect.objectContaining({ + "date": "2022-11-11" + }) + ); + + }); + + test("timeSlotsForSelectedDate should be null if no date has been selected", () => { + //Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.selectableDatesData = { + days: [{ + "date": "2022-11-11", + "timeSlots": [ + { + "id": "1820I-01820-M-I*20425*AM", + "startTime": "08:00", + "endTime": "12:00", + "offerPremium": true + }, + { + "id": "1820I-01820-M-I*20425*PM", + "startTime": "12:00", + "endTime": "17:00", + "offerPremium": false + } + ] + }], + }; + wrapper.setData({ + selectedDate: undefined + }); + + //Act + const testValue = wrapper.vm.timeSlotsForSelectedDate; + + //Assert + expect(testValue).toBe(null) + + }); + + }); + }); + + describe("schedule page methods...", () => { + test("getServiceZipCtuCodeFromStore should return zipCodeCtu", () => { + //Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.selectableDatesData = { + days: [], + }; + + //Act + const testValue = wrapper.vm.getServiceZipCtuCodeFromStore(); + + //Assert + expect(testValue).toStrictEqual( + "01234" + ); + + }); + + test("openInshopTimeSlotsModal should trigger openModal method", () => { + //Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.selectableDatesData = { + days: [], + }; + + //Act + wrapper.vm.openInshopTimeSlotsModal(); + + //Assert + expect(wrapper.vm.$refs.timeSlotModalQuestion.openModal).toBeCalled(); + + }); + + test("getSelectedRouteCode should return schedule routeCode", () => { + //Arrange + const { wrapper } = setupMocks({}); + + wrapper.vm.selectableDatesData = { + days: [], + }; + + //Act + const testValue = wrapper.vm.getSelectedRouteCode(); + + //Assert + expect(testValue).toStrictEqual( + "000" + ); + + }); + + + + test("timeSlotModalClosed should null any selected date when there's no route code", () => { + //Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.selectableDatesData = { + days: [], + }; + wrapper.setData({ + selectedDate: "1980-05-05" + }); + wrapper.setData({ + selectedTimeSlot: { + date: "2019-01-01", + startTime: "09:00", + endTime: "10:00", + routeCode: null + } + }); + + //Act + wrapper.vm.timeSlotModalClosed(); + + //Assert + expect(wrapper.vm.selectedDate).toBe(null); + + }); + + + test("getDisplayTextForMilitaryTime should return the correctly formatted string", () => { + //Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.selectableDatesData = { + days: [], + }; + const timeInput1 = "15:00"; + const timeInput2 = "15:30"; + + //Act + const testOutput1 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput1); + const testOutput2 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput2); + const testOutput3 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput1, true); + const testOutput4 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput2, true); + + //Assert + expect(testOutput1).toBe("3:00 PM"); + expect(testOutput2).toBe("3:30 PM"); + expect(testOutput3).toBe("3 PM"); + expect(testOutput4).toBe("3:30 PM"); + + }); + + test("getDisplayTextForMilitaryTime should return the correctly formatted string", () => { + //Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.selectableDatesData = { + days: [], + }; + wrapper.vm.$router.navigateWithoutSaving = jest.fn(); + wrapper.vm.$route = "testRoute"; + + //Act + wrapper.vm.backButtonAction(); + + //Assert + expect(wrapper.vm.$router.navigateWithoutSaving).toBeCalledWith("CLICKED_BACK", "testRoute"); + + }); + + + }); + + test("forwardButtonAction should call route method navigateWithoutSaving", async () => { + //Arrange + const { wrapper } = setupMocks({}); wrapper.vm.dispatchStoreAction = jest.fn(() => { return { data: [], }; }); + wrapper.vm.$router.navigateWithSaving = jest.fn(() => { + return {}; + }); //Act await wrapper.vm.forwardButtonAction(); @@ -69,137 +496,125 @@ describe("schedule.vue", () => { //Assert expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled(); }); - test("should pass arePagePrerequisitesValid with a mobile order and no providerNumber", () => { + + + test("for mobile appts, updateSupportingItems should call store action to save supporting items", async () => { //Arrange + store.getters.order.serviceLocation.appointmentType = "Mobile"; + store.getters.lineItems.supportingItems = [{ + "partNumber": "EARLY BIRD", + "description": null, + "partType": "EARLY BIRD", + "laborAmount": 0, + "sellingPrice": 0, + "kitPrice": 0 + }]; const { wrapper } = setupMocks({}); - store.getters = { - order: { - schedule: { - date: "2020-01-01", - }, - serviceLocation: { - zipCode: "12345", - zipCodeCtu: "value", - appointmentType: "Mobile", - }, - damage: { - isRepair: true, - }, - referralNumber: "1234567", - }, - payment: { - isInsurance: true, - }, - lineItems: { - supportingItems: [], - }, - }; + wrapper.vm.dispatchStoreAction = jest.fn(() => { + return { + data: [], + }; + }); + wrapper.vm.mobilePremiumAppointmentFee = 14.99; + wrapper.setData({ + selectedTimeSlot: { + date: "2019-01-01", + startTime: "09:00", + endTime: "10:00", + routeCode: null, + isPremiumAppointment: true + } + }); - let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); + //Act + await wrapper.vm.updateSupportingItems(); + + //Assert + expect(wrapper.vm.dispatchStoreAction).toBeCalledWith( + "saveSupportingItemsSuppressingStateResetting", + expect.arrayContaining([ + expect.objectContaining({ + partType: "EARLY BIRD" + }) + ]), + expect.anything() + ); - expect(arePagePrerequisitesValid).toBe(true); }); - test("should pass arePagePrerequisitesValid with a inshop order and providerNumber", () => { + + test("for Inshop appts, updateSupportingItems should call store action to save supporting items WITHOUT the EARLY BIRD supporting item", async () => { //Arrange + store.getters.order.serviceLocation.appointmentType = "Inshop"; + store.getters.lineItems.supportingItems = [{ + "partNumber": "EARLY BIRD", + "description": null, + "partType": "EARLY BIRD", + "laborAmount": 0, + "sellingPrice": 0, + "kitPrice": 0 + }]; const { wrapper } = setupMocks({}); - store.getters = { - order: { - schedule: { - date: "2020-01-01", - }, - serviceLocation: { - zipCode: "12345", - zipCodeCtu: "value", - appointmentType: "Inshop", - provider: { - providerNumber: "5", - }, - }, - damage: { - isRepair: true, - }, - referralNumber: "1234567", - }, - payment: { - isInsurance: true, - }, - lineItems: { - supportingItems: [], - }, - }; + wrapper.vm.dispatchStoreAction = jest.fn(() => { + return { + data: [], + }; + }); + wrapper.vm.mobilePremiumAppointmentFee = 14.99; + wrapper.setData({ + selectedTimeSlot: { + date: "2019-01-01", + startTime: "09:00", + endTime: "10:00", + routeCode: null, + isPremiumAppointment: true + } + }); - let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); + //Act + await wrapper.vm.updateSupportingItems(); + + //Assert + expect(wrapper.vm.dispatchStoreAction).toBeCalledWith( + "saveSupportingItemsSuppressingStateResetting", + expect.not.arrayContaining([ + expect.objectContaining({ + partType: "EARLY BIRD" + }) + ]), + expect.anything() + ); - expect(arePagePrerequisitesValid).toBe(true); }); - test("should fail arePagePrerequisitesValid with a replace with no glass parts", () => { + + test("if no EARLY BIRD supporting item, then updateSupportingItems should NOT call store action", async () => { //Arrange + store.getters.order.serviceLocation.appointmentType = "Mobile"; + store.getters.lineItems.supportingItems = []; const { wrapper } = setupMocks({}); - store.getters = { - order: { - schedule: { - date: "2020-01-01", - }, - serviceLocation: { - zipCode: "12345", - zipCodeCtu: "value", - appointmentType: "Inshop", - provider: { - providerNumber: "5", - }, - }, - damage: { - isRepair: false, - }, - referralNumber: "1234567", - }, - payment: { - isInsurance: true, - }, - lineItems: { - supportingItems: [], - glassParts: [], - }, - }; + wrapper.vm.dispatchStoreAction = jest.fn(() => { + return { + data: [], + }; + }); + wrapper.vm.mobilePremiumAppointmentFee = 14.99; + wrapper.setData({ + selectedTimeSlot: { + date: "2019-01-01", + startTime: "09:00", + endTime: "10:00", + routeCode: null, + isPremiumAppointment: false + } + }); - let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); + //Act + await wrapper.vm.updateSupportingItems(); + + //Assert + expect(wrapper.vm.dispatchStoreAction).not.toBeCalled(); - expect(arePagePrerequisitesValid).toBe(false); }); - test("should fail arePagePrerequisitesValid without isInsurance", () => { - //Arrange - const { wrapper } = setupMocks({}); - store.getters = { - order: { - schedule: { - date: "2020-01-01", - }, - serviceLocation: { - zipCode: "12345", - zipCodeCtu: "value", - appointmentType: "Inshop", - provider: { - providerNumber: "5", - }, - }, - damage: { - isRepair: true, - }, - referralNumber: "1234567", - }, - payment: { - isInsurance: null, - }, - lineItems: { - supportingItems: [], - glassParts: [], - }, - }; - let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); - - expect(arePagePrerequisitesValid).toBe(false); - }); }); const mockCmsContent = {}; @@ -210,6 +625,7 @@ function setupMocks({ customMountOptions }) { }); mountOptions.global.mocks["$store"] = store; + mountOptions.global.mocks["$router"] = router; mountOptions["attachTo"] = document.body; mountOptions.mixins = [ { @@ -223,6 +639,12 @@ function setupMocks({ customMountOptions }) { ]; const wrapper = shallowMount(schedule, mountOptions); + wrapper.vm.setCmsContent = jest.fn(); + wrapper.vm.$refs.datePicker.initializeComponent = jest.fn(); + wrapper.vm.$refs.locationAlerts.initializeComponent = jest.fn(); + wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); + wrapper.vm.$refs.timeSlotModalQuestion.openModal = jest.fn(); + return { wrapper }; } diff --git a/src/layouts/schedule/schedule.spec.js1 b/src/layouts/schedule/schedule.spec.js1 new file mode 100644 index 000000000..e69de29bb diff --git a/src/layouts/schedule/schedule.vue b/src/layouts/schedule/schedule.vue index 6a42b7a92..00b3e796b 100644 --- a/src/layouts/schedule/schedule.vue +++ b/src/layouts/schedule/schedule.vue @@ -320,6 +320,7 @@ export default { ); return newShopTimeSlots; }, + getAvailableDates, getServiceZipCtuCodeFromStore() { return store.getters.order.serviceLocation.zipCodeCtu; }, @@ -338,12 +339,6 @@ export default { getSupportingItems() { return store.getters.lineItems.supportingItems; }, - isMobilePremiumFeeOnOrderInVuex() { - const supportingItemsFromVuex = store.getters.lineItems.supportingItems; - return !!supportingItemsFromVuex.filter( - (lineItem) => lineItem.partType === PREMIUM_FEE_PART_TYPE - ).length; - }, timeSlotModalClosed() { // Clear the selectedDate if no timeSlot has been selected if (this.selectedTimeSlot.routeCode == null) { From 80e7248bd9424f89567c344faf7f9cb40a22f013 Mon Sep 17 00:00:00 2001 From: Adam Caouette Date: Mon, 14 Aug 2023 11:09:05 -0400 Subject: [PATCH 38/38] CSR-886: unit tests formatting updates --- src/layouts/schedule/schedule.spec.js | 184 ++++++++++++-------------- 1 file changed, 85 insertions(+), 99 deletions(-) diff --git a/src/layouts/schedule/schedule.spec.js b/src/layouts/schedule/schedule.spec.js index 56b2ce13b..b47036c10 100644 --- a/src/layouts/schedule/schedule.spec.js +++ b/src/layouts/schedule/schedule.spec.js @@ -89,7 +89,7 @@ beforeEach(() => { date: "2019-01-01", startTime: "09:00", endTime: "10:00", - routeCode: "000" + routeCode: "000", }, lineItems: { glassParts: [ @@ -135,7 +135,7 @@ describe("schedule.vue...", () => { store.getters.order.serviceLocation.appointmentType = "Mobile"; store.getters.order.serviceLocation.provider = null; - //Act + //Act const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); //Assert @@ -265,45 +265,45 @@ describe("schedule.vue...", () => { expect.objectContaining({ days: expect.any(Array), estimatedServiceMinutesMaximum: expect.any(Number), - estimatedServiceMinutesMinimum: expect.any(Number) + estimatedServiceMinutesMinimum: expect.any(Number), }) ); expect(wrapper.vm.mobilePremiumAppointmentFee).toStrictEqual( expect.objectContaining({ - partNumber: expect.any(String) + partNumber: expect.any(String), }) ); expect(wrapper.vm.updateFooterButtonText).toHaveBeenCalled(); }); - }); describe("computed properties...", () => { - test("timeSlotsForSelectedDate should return timeslots if selected date is available", () => { //Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesData = { - days: [{ - "date": "2022-11-11", - "timeSlots": [ - { - "id": "1820I-01820-M-I*20425*AM", - "startTime": "08:00", - "endTime": "12:00", - "offerPremium": true - }, - { - "id": "1820I-01820-M-I*20425*PM", - "startTime": "12:00", - "endTime": "17:00", - "offerPremium": false - } - ] - }], + days: [ + { + date: "2022-11-11", + timeSlots: [ + { + id: "1820I-01820-M-I*20425*AM", + startTime: "08:00", + endTime: "12:00", + offerPremium: true, + }, + { + id: "1820I-01820-M-I*20425*PM", + startTime: "12:00", + endTime: "17:00", + offerPremium: false, + }, + ], + }, + ], }; wrapper.setData({ - selectedDate: "2022-11-11" + selectedDate: "2022-11-11", }); //Act @@ -312,46 +312,45 @@ describe("schedule.vue...", () => { //Assert expect(testValue).toStrictEqual( expect.objectContaining({ - "date": "2022-11-11" + date: "2022-11-11", }) ); - }); test("timeSlotsForSelectedDate should be null if no date has been selected", () => { //Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesData = { - days: [{ - "date": "2022-11-11", - "timeSlots": [ - { - "id": "1820I-01820-M-I*20425*AM", - "startTime": "08:00", - "endTime": "12:00", - "offerPremium": true - }, - { - "id": "1820I-01820-M-I*20425*PM", - "startTime": "12:00", - "endTime": "17:00", - "offerPremium": false - } - ] - }], + days: [ + { + date: "2022-11-11", + timeSlots: [ + { + id: "1820I-01820-M-I*20425*AM", + startTime: "08:00", + endTime: "12:00", + offerPremium: true, + }, + { + id: "1820I-01820-M-I*20425*PM", + startTime: "12:00", + endTime: "17:00", + offerPremium: false, + }, + ], + }, + ], }; wrapper.setData({ - selectedDate: undefined + selectedDate: undefined, }); //Act const testValue = wrapper.vm.timeSlotsForSelectedDate; //Assert - expect(testValue).toBe(null) - + expect(testValue).toBe(null); }); - }); }); @@ -367,10 +366,7 @@ describe("schedule.vue...", () => { const testValue = wrapper.vm.getServiceZipCtuCodeFromStore(); //Assert - expect(testValue).toStrictEqual( - "01234" - ); - + expect(testValue).toStrictEqual("01234"); }); test("openInshopTimeSlotsModal should trigger openModal method", () => { @@ -385,7 +381,6 @@ describe("schedule.vue...", () => { //Assert expect(wrapper.vm.$refs.timeSlotModalQuestion.openModal).toBeCalled(); - }); test("getSelectedRouteCode should return schedule routeCode", () => { @@ -400,14 +395,9 @@ describe("schedule.vue...", () => { const testValue = wrapper.vm.getSelectedRouteCode(); //Assert - expect(testValue).toStrictEqual( - "000" - ); - + expect(testValue).toStrictEqual("000"); }); - - test("timeSlotModalClosed should null any selected date when there's no route code", () => { //Arrange const { wrapper } = setupMocks({}); @@ -415,15 +405,15 @@ describe("schedule.vue...", () => { days: [], }; wrapper.setData({ - selectedDate: "1980-05-05" + selectedDate: "1980-05-05", }); wrapper.setData({ selectedTimeSlot: { date: "2019-01-01", startTime: "09:00", endTime: "10:00", - routeCode: null - } + routeCode: null, + }, }); //Act @@ -431,10 +421,8 @@ describe("schedule.vue...", () => { //Assert expect(wrapper.vm.selectedDate).toBe(null); - }); - test("getDisplayTextForMilitaryTime should return the correctly formatted string", () => { //Arrange const { wrapper } = setupMocks({}); @@ -455,7 +443,6 @@ describe("schedule.vue...", () => { expect(testOutput2).toBe("3:30 PM"); expect(testOutput3).toBe("3 PM"); expect(testOutput4).toBe("3:30 PM"); - }); test("getDisplayTextForMilitaryTime should return the correctly formatted string", () => { @@ -471,11 +458,11 @@ describe("schedule.vue...", () => { wrapper.vm.backButtonAction(); //Assert - expect(wrapper.vm.$router.navigateWithoutSaving).toBeCalledWith("CLICKED_BACK", "testRoute"); - + expect(wrapper.vm.$router.navigateWithoutSaving).toBeCalledWith( + "CLICKED_BACK", + "testRoute" + ); }); - - }); test("forwardButtonAction should call route method navigateWithoutSaving", async () => { @@ -497,18 +484,19 @@ describe("schedule.vue...", () => { expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled(); }); - test("for mobile appts, updateSupportingItems should call store action to save supporting items", async () => { //Arrange store.getters.order.serviceLocation.appointmentType = "Mobile"; - store.getters.lineItems.supportingItems = [{ - "partNumber": "EARLY BIRD", - "description": null, - "partType": "EARLY BIRD", - "laborAmount": 0, - "sellingPrice": 0, - "kitPrice": 0 - }]; + store.getters.lineItems.supportingItems = [ + { + partNumber: "EARLY BIRD", + description: null, + partType: "EARLY BIRD", + laborAmount: 0, + sellingPrice: 0, + kitPrice: 0, + }, + ]; const { wrapper } = setupMocks({}); wrapper.vm.dispatchStoreAction = jest.fn(() => { return { @@ -522,8 +510,8 @@ describe("schedule.vue...", () => { startTime: "09:00", endTime: "10:00", routeCode: null, - isPremiumAppointment: true - } + isPremiumAppointment: true, + }, }); //Act @@ -534,25 +522,26 @@ describe("schedule.vue...", () => { "saveSupportingItemsSuppressingStateResetting", expect.arrayContaining([ expect.objectContaining({ - partType: "EARLY BIRD" - }) + partType: "EARLY BIRD", + }), ]), expect.anything() ); - }); test("for Inshop appts, updateSupportingItems should call store action to save supporting items WITHOUT the EARLY BIRD supporting item", async () => { //Arrange store.getters.order.serviceLocation.appointmentType = "Inshop"; - store.getters.lineItems.supportingItems = [{ - "partNumber": "EARLY BIRD", - "description": null, - "partType": "EARLY BIRD", - "laborAmount": 0, - "sellingPrice": 0, - "kitPrice": 0 - }]; + store.getters.lineItems.supportingItems = [ + { + partNumber: "EARLY BIRD", + description: null, + partType: "EARLY BIRD", + laborAmount: 0, + sellingPrice: 0, + kitPrice: 0, + }, + ]; const { wrapper } = setupMocks({}); wrapper.vm.dispatchStoreAction = jest.fn(() => { return { @@ -566,8 +555,8 @@ describe("schedule.vue...", () => { startTime: "09:00", endTime: "10:00", routeCode: null, - isPremiumAppointment: true - } + isPremiumAppointment: true, + }, }); //Act @@ -578,12 +567,11 @@ describe("schedule.vue...", () => { "saveSupportingItemsSuppressingStateResetting", expect.not.arrayContaining([ expect.objectContaining({ - partType: "EARLY BIRD" - }) + partType: "EARLY BIRD", + }), ]), expect.anything() ); - }); test("if no EARLY BIRD supporting item, then updateSupportingItems should NOT call store action", async () => { @@ -603,8 +591,8 @@ describe("schedule.vue...", () => { startTime: "09:00", endTime: "10:00", routeCode: null, - isPremiumAppointment: false - } + isPremiumAppointment: false, + }, }); //Act @@ -612,9 +600,7 @@ describe("schedule.vue...", () => { //Assert expect(wrapper.vm.dispatchStoreAction).not.toBeCalled(); - }); - }); const mockCmsContent = {};