Merge branch 'release/2026.02.12' into feature/CASH-1790-refactor-fixes

This commit is contained in:
Adam Caouette 2026-02-03 11:05:08 -05:00
commit 85ab8462f9
12 changed files with 201 additions and 125 deletions

View file

@ -10,7 +10,6 @@ module.exports = {
}, },
transformIgnorePatterns: ["'/node_modules/(?!vee-validate)"], transformIgnorePatterns: ["'/node_modules/(?!vee-validate)"],
moduleFileExtensions: ["js", "vue"], moduleFileExtensions: ["js", "vue"],
modulePathIgnorePatterns: ["vin-lookup"],
collectCoverageFrom: [ collectCoverageFrom: [
"src/**/*.{js,vue}", "src/**/*.{js,vue}",
"!src/main.js", "!src/main.js",

View file

@ -36,6 +36,9 @@ const experimentSettings = {
SHOW_PM_MOBILE_DAYS: "Show_PMmobileDays", SHOW_PM_MOBILE_DAYS: "Show_PMmobileDays",
SHOW_NO_PM_MOBILE_DAYS: "Show_NoPMmobileDays", SHOW_NO_PM_MOBILE_DAYS: "Show_NoPMmobileDays",
SHOW_NO_MOBILE_AVAILABLE_DAYS: "Show_NoMobileAvailableDays", SHOW_NO_MOBILE_AVAILABLE_DAYS: "Show_NoMobileAvailableDays",
SHOW_MOBILE_FIRST_POPUP_REPAIR: "Show_Mobile_First_Popup_Repair",
SHOW_MOBILE_FIRST_POPUP_REPLACE_WITHOUT_MSR: "Show_Mobile_First_Popup_Replace_Without_MSR",
SHOW_MOBILE_FIRST_POPUP_REPLACE_WITH_MSR: "Show_Mobile_First_Popup_Replace_With_MSR",
OFFER_OEM: "OfferOem", OFFER_OEM: "OfferOem",
USE_ADYEN_PAYMENT: "UseAdyenPayment", USE_ADYEN_PAYMENT: "UseAdyenPayment",
}; };

View file

@ -289,7 +289,7 @@ export default {
if (event.screenX === 0 && event.screenY === 0) { if (event.screenX === 0 && event.screenY === 0) {
return; return;
} }
this.$emit("date-clicked", date); this.$emit("date-selected", date);
}, },
getWeekStartDate(dateString) { getWeekStartDate(dateString) {
const date = convertDateStringToDate(dateString); const date = convertDateStringToDate(dateString);
@ -332,9 +332,9 @@ export default {
let preSelectedDateMonthEnd = this.getMonthEnd( let preSelectedDateMonthEnd = this.getMonthEnd(
preSelectedDate.replace("-mobile", "") preSelectedDate.replace("-mobile", "")
); );
let weekIncludesPreSelectedMonthEnd = false; let monthEndsByThisWeek = false;
let i = 0; let i = 0;
while (!weekIncludesPreSelectedMonthEnd) { while (!monthEndsByThisWeek && i < 52) {
if (i > 0) { if (i > 0) {
weekStartDate = this.getNextWeekSunday(weekEndDate); weekStartDate = this.getNextWeekSunday(weekEndDate);
weekEndDate = this.getWeekEndDate(weekStartDate); weekEndDate = this.getWeekEndDate(weekStartDate);
@ -346,7 +346,12 @@ export default {
preSelectedDateMonthEnd === weekEndDate preSelectedDateMonthEnd === weekEndDate
) { ) {
weekEndDate = preSelectedDateMonthEnd; weekEndDate = preSelectedDateMonthEnd;
weekIncludesPreSelectedMonthEnd = true; monthEndsByThisWeek = true;
} else if (preSelectedDateMonthEnd < weekEndDate) {
// Additional case: if month ends before this week (but not necessarily during it),
// still cut the loop here, but don't truncate the week.
monthEndsByThisWeek = true;
} }
} }
weeks.push({ weeks.push({

View file

@ -138,10 +138,7 @@ export default {
} }
// Pass data to Salesforce chat if available // Pass data to Salesforce chat if available
const transfer = event.detail; const transfer = event.detail;
if ( if (window.embeddedservice_bootstrap && window.embeddedservice_bootstrap.prechatAPI) {
window.embeddedservice_bootstrap &&
typeof window.embeddedservice_bootstrap.bootstrapEmbeddedService === "function"
) {
if ( if (
transfer && transfer &&
transfer.data && transfer.data &&

View file

@ -246,7 +246,7 @@ export default {
console.log(`Price = ${this.amountDue}`); console.log(`Price = ${this.amountDue}`);
console.log(`Adyen Price = ${this.adyenPriceTotal}`); console.log(`Adyen Price = ${this.adyenPriceTotal}`);
const requestBody = this.adyenInitRequestInfo; const requestBody = this.getAdyenInitRequestInfo();
console.log(`Calling with:`); console.log(`Calling with:`);
console.log(requestBody); console.log(requestBody);
@ -294,6 +294,15 @@ export default {
console.log(checkout); console.log(checkout);
const expiryTime = new Date(checkout.options.expiresAt);
const expiryInterval = expiryTime.getTime() - new Date().getTime();
const handleTimeout = () => {
this.resetAdyenDropin();
};
const timeout = setTimeout(handleTimeout, expiryInterval);
const configuration = { const configuration = {
paymentMethodsConfiguration: { paymentMethodsConfiguration: {
ideal: { ideal: {
@ -496,7 +505,7 @@ export default {
await baseMixin.methods.dispatchStoreAction(storeActions.CREATE_SUBMITTED_STATE); await baseMixin.methods.dispatchStoreAction(storeActions.CREATE_SUBMITTED_STATE);
this.$router.navigateWithoutSaving( this.$router.navigateWithoutSaving(
this.navigationScenarios.CLICKED_FORWARD, this.navigationScenarios.CLICKED_PAY_LATER,
this.pageName this.pageName
); );
} catch (error) { } catch (error) {
@ -505,14 +514,26 @@ export default {
return; return;
} }
}, },
},
computed: { async resetAdyenDropin() {
piaType() { this?.dropinComponent?.unmount();
return this.$store.getters.order.payment.piaType; await this.initializeAdyen();
}, },
adyenInitRequestInfo() { // Now a Method so it is always freshly called and not cached.
getIdempotencyKey() {
const currentDateTime = new Date();
const currentHour = currentDateTime.getUTCHours();
const currentDate = currentDateTime.getUTCDate();
const id = this?.$store?.getters?.order?.referralCorrelationId;
const system = this.sourceSystem;
const total = this.adyenPriceTotal;
return `${id}-${system}-${currentDate}-${currentHour}-${total}`;
},
// Now a Method so it is always freshly called and not cached.
getAdyenInitRequestInfo() {
return { return {
sourceSystem: this.sourceSystem, sourceSystem: this.sourceSystem,
referralSequenceNumber: this.$store.getters.order.referralSequenceNumber, referralSequenceNumber: this.$store.getters.order.referralSequenceNumber,
@ -529,9 +550,15 @@ export default {
IP: "127.0.0.1", // TODO IP: "127.0.0.1", // TODO
firstName: this.$store.getters.order.customer.firstName, firstName: this.$store.getters.order.customer.firstName,
lastName: this.$store.getters.order.customer.lastName, lastName: this.$store.getters.order.customer.lastName,
idempotencyKey: this.idempotencyKey, idempotencyKey: this.getIdempotencyKey(),
}; };
}, },
},
computed: {
piaType() {
return this.$store.getters.order.payment.piaType;
},
workOrderNumberLastSixDigits() { workOrderNumberLastSixDigits() {
const workOrderNumber = this.$store.getters.order.workOrderNumber ?? ""; const workOrderNumber = this.$store.getters.order.workOrderNumber ?? "";
@ -588,10 +615,6 @@ export default {
}; };
}, },
idempotencyKey() {
return `${this.$store.getters.order.referralCorrelationId}-${this.sourceSystem}`;
},
sourceSystem() { sourceSystem() {
return "FMG-2.0"; return "FMG-2.0";
}, },
@ -601,7 +624,7 @@ export default {
}, },
adyenPriceTotal() { adyenPriceTotal() {
return this.amountDue * 100; return Math.round(this.amountDue * 100);
}, },
// Cart info // Cart info

View file

@ -28,6 +28,7 @@
cmsWidgetName="ScheduleYourServiceWidget" cmsWidgetName="ScheduleYourServiceWidget"
id="schedule-your-service" /> id="schedule-your-service" />
<appointmentTypeQuestion <appointmentTypeQuestion
v-if="hasSelectableDatesLoaded"
v-model="appointmentTypeFromAppointmentTypeQuestion" v-model="appointmentTypeFromAppointmentTypeQuestion"
v-show="isAppointmentTypeDisplayed" v-show="isAppointmentTypeDisplayed"
:isServiceableMobile="isServiceableMobile" :isServiceableMobile="isServiceableMobile"
@ -97,15 +98,11 @@
linkWidgetName="ServiceZipLinkWidget" linkWidgetName="ServiceZipLinkWidget"
modalWidgetName="ServiceZipModalWidget" /> modalWidgetName="ServiceZipModalWidget" />
</div> </div>
<div <div class="row your-shop-location" v-if="showInshopComponent">
class="row your-shop-location"
v-if="appointmentType && appointmentType !== appointmentTypeStrings.MOBILE">
<div class="col-12 col-md-10 col-lg-8 col-xl-7"> <div class="col-12 col-md-10 col-lg-8 col-xl-7">
<div> <div>
<shopLocation <shopLocation
v-if=" v-if="showInshopComponent"
appointmentType && appointmentType !== appointmentTypeStrings.MOBILE
"
:selectedShop="selectedShopAnswer" :selectedShop="selectedShopAnswer"
:selectedProviderNumberFromParent="selectedProvider?.providerNumber" :selectedProviderNumberFromParent="selectedProvider?.providerNumber"
:shopProviderDataFromParent="shopProviderData" :shopProviderDataFromParent="shopProviderData"
@ -139,8 +136,6 @@
:isOvernightDropoff="isOvernightDropoff" /> :isOvernightDropoff="isOvernightDropoff" />
</div> </div>
<datePicker <datePicker
:currentZip="zipCode"
:currentProviderNumber="selectedProvider?.providerNumber"
v-show="appointmentType" v-show="appointmentType"
customComponentId="dateQuestion" customComponentId="dateQuestion"
selectableDatesSetting="custom" selectableDatesSetting="custom"
@ -150,7 +145,7 @@
class="text-link-small" class="text-link-small"
:getMoreDatesCallback="getMoreScheduleData" :getMoreDatesCallback="getMoreScheduleData"
validationRules="date-required" validationRules="date-required"
@date-clicked="handleDateClicked" @date-selected="handleDateSelected"
:pricingByDayBasePrice="pricingByDayBasePrice" :pricingByDayBasePrice="pricingByDayBasePrice"
:pricingByDayUpcharge="pricingByDayUpcharge" :pricingByDayUpcharge="pricingByDayUpcharge"
:showPricingByDay="showPricingByDay" :showPricingByDay="showPricingByDay"
@ -638,6 +633,21 @@ export default {
this.handleZipCodeChange(newValue); this.handleZipCodeChange(newValue);
}, },
}, },
showInshopComponent() {
// If we get no inshop or mobile data, then show the shoplocation component so the user is not
// looking at a half blank screen with no way to try another zip/location.
if (
(this.inshopTimeSlotsData === undefined &&
this.mobileTimeSlotsData === undefined) ||
(this.inshopTimeSlotsData.days.length === 0 &&
this.mobileTimeSlotsData.days.length === 0) ||
(this.appointmentType && this.appointmentType !== AppointmentTypeStrings.MOBILE)
) {
return true;
}
return false;
},
isMobileSelected() { isMobileSelected() {
return ( return (
this.appointmentType == AppointmentTypeStrings.MOBILE || this.appointmentType == AppointmentTypeStrings.MOBILE ||
@ -660,12 +670,12 @@ export default {
showTimeSlotQuestion() { showTimeSlotQuestion() {
if ( if (
this.selectedDate && this.selectedDate &&
this.selectedDate.includes("mobile") && this.appointmentTypeFromAppointmentTypeQuestion &&
!this.isMobileSelected this.timeSlotsForSelectedDate
) { ) {
this.setSelectedDateToFirstAvailable(); return true;
} }
return this.selectedDate && this.appointmentTypeFromAppointmentTypeQuestion; return false;
}, },
isMobileStaticRecalibrationApplicable() { isMobileStaticRecalibrationApplicable() {
return ( return (
@ -940,6 +950,13 @@ export default {
} }
return false; return false;
}, },
hasSelectableDatesLoaded() {
if (this.isMobileSelected) {
return this.selectableDatesMobile?.days?.length > 0;
} else {
return this.selectableDatesInshop?.days?.length > 0;
}
},
}, },
methods: { methods: {
splitCopyOnCMSPlaceHolder, splitCopyOnCMSPlaceHolder,
@ -1261,8 +1278,6 @@ export default {
async initializeDatePicker() { async initializeDatePicker() {
this.isShowMobileFirstAppt && this.showLoadingModal(); this.isShowMobileFirstAppt && this.showLoadingModal();
this.selectedDate = null;
const includeMobileTimeSlots = this.isServiceableMobile; const includeMobileTimeSlots = this.isServiceableMobile;
const includeInshopTimeSlots = this.isServiceableInshop || this.isServiceableDropoff; const includeInshopTimeSlots = this.isServiceableInshop || this.isServiceableDropoff;
const datePickerInitialData = await this.$refs.datePicker.loadInitialData({ const datePickerInitialData = await this.$refs.datePicker.loadInitialData({
@ -1637,12 +1652,12 @@ export default {
handleWaitListRequested(value) { handleWaitListRequested(value) {
this.waitListRequested = value; this.waitListRequested = value;
}, },
handleDateClicked(date) { handleDateSelected(date) {
// do something to mark this as upcharge day or not... const previousDate = this.selectedDate;
if (date.isPricingByDayUpchargeDay) { // check if date actually changed
this.includePricingByDayUpcharge = true; if (previousDate !== date.dateString) {
} else { this.selectedTimeSlotInfo = this.getEmptyTimeSlot();
this.includePricingByDayUpcharge = false; this.updateFooterButtonText(this.selectedTimeSlotInfo);
} }
// TODO: REMOVE AS PART OF CASH-1634 // TODO: REMOVE AS PART OF CASH-1634
@ -1669,17 +1684,7 @@ export default {
updateTimeSlot(timeSlotObj) { updateTimeSlot(timeSlotObj) {
if (!timeSlotObj?.routeCode) { if (!timeSlotObj?.routeCode) {
this.appointmentType = AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF; this.appointmentType = AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF;
this.selectedTimeSlotInfo = { this.selectedTimeSlotInfo = this.getEmptyTimeSlot();
timeSlot: {
date: null,
routeCode: null,
startTime: null,
endTime: null,
jobMaxMinutes: null,
jobMinMinutes: null,
},
isPremiumAppointment: null,
};
return; return;
} }
const timeSlot = this.timeSlotsForSelectedDate?.timeSlots?.find( const timeSlot = this.timeSlotsForSelectedDate?.timeSlots?.find(
@ -1801,6 +1806,9 @@ export default {
}, },
handleAppointmentTypeChange(newAppointmentType) { handleAppointmentTypeChange(newAppointmentType) {
this.updateFooterButtonText(); this.updateFooterButtonText();
// if time appointment type changes, clear any selected time slot
this.selectedTimeSlotInfo = this.getEmptyTimeSlot();
if (newAppointmentType === AppointmentTypeStrings.MOBILE) { if (newAppointmentType === AppointmentTypeStrings.MOBILE) {
// Remember last shop selected if previous selection was inshop/dropoff // Remember last shop selected if previous selection was inshop/dropoff
if ( if (
@ -1809,17 +1817,6 @@ export default {
AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF) && AppointmentTypeStrings.IN_SHOP_OR_DROP_OFF) &&
this.selectedProvider this.selectedProvider
) { ) {
this.selectedTimeSlotInfo = {
timeSlot: {
date: null,
routeCode: null,
startTime: null,
endTime: null,
jobMaxMinutes: null,
jobMinMinutes: null,
},
isPremiumAppointment: null,
};
this.lastSelectedInshopOrDropoffProvider = this.selectedProvider; this.lastSelectedInshopOrDropoffProvider = this.selectedProvider;
} }
this.appointmentType = AppointmentTypeStrings.MOBILE; this.appointmentType = AppointmentTypeStrings.MOBILE;
@ -1846,7 +1843,8 @@ export default {
} else { } else {
this.appointmentType = null; this.appointmentType = null;
} }
this.setSelectedDateToFirstAvailable(); this.setSelectedDateToFirstAvailable(); // v-if on appointmentTypeQuestion ensures dates have been loaded by now
this.setDisplayWaitList(); // v-if on appointmentTypeQuestion ensures dates have been loaded by now
}, },
setSelectedDateToFirstAvailable() { setSelectedDateToFirstAvailable() {
this.selectedDate = this.getFirstAvailableDate(); this.selectedDate = this.getFirstAvailableDate();
@ -1888,6 +1886,20 @@ export default {
const noMobileAvailableDays = experimentMixin.methods.getSettingValue( const noMobileAvailableDays = experimentMixin.methods.getSettingValue(
experimentSettings.SHOW_NO_MOBILE_AVAILABLE_DAYS experimentSettings.SHOW_NO_MOBILE_AVAILABLE_DAYS
); );
const mobileFirstPopupRepairSetting = experimentMixin.methods.getSettingValue(
experimentSettings.SHOW_MOBILE_FIRST_POPUP_REPAIR
);
const mobileFirstPopupReplaceWithoutMsrSetting =
experimentMixin.methods.getSettingValue(
experimentSettings.SHOW_MOBILE_FIRST_POPUP_REPLACE_WITHOUT_MSR
);
const mobileFirstPopupReplaceWithMsrSetting =
experimentMixin.methods.getSettingValue(
experimentSettings.SHOW_MOBILE_FIRST_POPUP_REPLACE_WITH_MSR
);
const order = this.$store.getters.order;
const isRepair = order.damage.isRepair;
let preSelectedMobileAppointment = null; let preSelectedMobileAppointment = null;
const todaysDate = getTodayDate(); const todaysDate = getTodayDate();
@ -1904,12 +1916,63 @@ export default {
(new Date(firstMobilePMDate) - todaysDate) / (1000 * 60 * 60 * 24); (new Date(firstMobilePMDate) - todaysDate) / (1000 * 60 * 60 * 24);
const shouldExposeMobileFirstAppointment = () => { const shouldExposeMobileFirstAppointment = () => {
return ( const mobileFirstDaysCheck =
(firstMobileAMDate && (firstMobileAMDate &&
numberOfDaysToFirstMobileAMDate <= noMobileAvailableDays) || numberOfDaysToFirstMobileAMDate <= noMobileAvailableDays) ||
(firstMobilePMDate && (firstMobilePMDate &&
numberOfDaysToFirstMobilePMDate <= noMobileAvailableDays) numberOfDaysToFirstMobilePMDate <= noMobileAvailableDays);
); debugLog("::: Mobile First Days Check:", mobileFirstDaysCheck);
if (
mobileFirstPopupRepairSetting === "false" &&
mobileFirstPopupReplaceWithoutMsrSetting === "false" &&
mobileFirstPopupReplaceWithMsrSetting === "false"
) {
debugLog(
"::: Mobile First Damage Popup settings are all false, using default check."
);
return mobileFirstDaysCheck;
}
if (isRepair) {
if (mobileFirstPopupRepairSetting === "true") {
debugLog(
"::: Mobile First Popup Repair setting is true, showing mobile first appointment for repair."
);
return mobileFirstDaysCheck;
} else {
debugLog(
"::: Mobile First Popup Repair setting is false, not showing mobile first appointment for repair."
);
return false;
}
}
if (this.isMobileStaticRecalibrationApplicable) {
if (mobileFirstPopupReplaceWithMsrSetting === "true") {
debugLog(
"::: Mobile First Popup Replace with MSR setting is true, showing mobile first appointment for replace with MSR."
);
return mobileFirstDaysCheck;
} else {
debugLog(
"::: Mobile First Popup Replace with MSR setting is false, not showing mobile first appointment for replace with MSR."
);
return false;
}
}
if (mobileFirstPopupReplaceWithoutMsrSetting === "true") {
debugLog(
"::: Mobile First Popup Replace without MSR setting is true, showing mobile first appointment for replace without MSR."
);
return mobileFirstDaysCheck;
} else {
debugLog(
"::: Mobile First Popup Replace without MSR setting is false, not showing mobile first appointment for replace without MSR."
);
return false;
}
}; };
if (shouldExposeMobileFirstAppointment()) { if (shouldExposeMobileFirstAppointment()) {
@ -2008,35 +2071,9 @@ export default {
watch: { watch: {
appointmentTypeFromAppointmentTypeQuestion: { appointmentTypeFromAppointmentTypeQuestion: {
handler(newValue, oldValue) { handler(newValue, oldValue) {
this.handleAppointmentTypeChange(newValue); this.handleAppointmentTypeChange(newValue); // v-if on appointmentTypeQuestion ensures dates have been loaded by now
}, },
}, },
selectedDate(newValue, oldValue) {
// Clear time slot selection if date selected changes
const selectedDate = this.getSelectedDateFromStore();
if (oldValue && this.appointmentType === AppointmentTypeStrings.MOBILE) {
oldValue = `${oldValue}-mobile`;
}
const hasValueChanged = newValue !== oldValue;
const isDateDifferent = (newValue || oldValue) !== selectedDate;
if (hasValueChanged && isDateDifferent && !this.selectedMobileFirstAppointment) {
this.selectedTimeSlotInfo = {
timeSlot: {
date: null,
routeCode: null,
startTime: null,
endTime: null,
jobMaxMinutes: null,
jobMinMinutes: null,
},
isPremiumAppointment: null,
};
}
},
selectedTimeSlotInfo(newValue) {
this.updateFooterButtonText(newValue);
},
}, },
components: { components: {
funnelHeader, funnelHeader,

View file

@ -1,6 +1,7 @@
<template> <template>
<div class="time-slots-question"> <div class="time-slots-question">
<buttonQuestion <buttonQuestion
ref="chooseDropOffOrInshop"
v-if="isDropOffAppointmentAvailable" v-if="isDropOffAppointmentAvailable"
v-model="selectedAnswerForDropOffOrInshop" v-model="selectedAnswerForDropOffOrInshop"
@update:modelValue="dropOffSelectionChanged" @update:modelValue="dropOffSelectionChanged"
@ -23,8 +24,9 @@
</div> </div>
</buttonQuestion> </buttonQuestion>
<buttonQuestion <buttonQuestion
ref="buttonQuestion" ref="chooseTimeSlot"
v-if="shouldDisplayTimeSlotQuestion" v-if="displayTimeSlotQuestion"
v-model="selectedAnswerForTimeSlots"
@update:modelValue="timeSlotSelectionChanged" @update:modelValue="timeSlotSelectionChanged"
buttonTypeString="timeSlotModalListButton" buttonTypeString="timeSlotModalListButton"
:buttonTypeObject="timeSlotModalListButton" :buttonTypeObject="timeSlotModalListButton"
@ -40,7 +42,6 @@
}" }"
groupName="chooseTimeSlot" groupName="chooseTimeSlot"
textPosition="text-center" textPosition="text-center"
v-model="selectedAnswerForTimeSlots"
questionText="Available times:" questionText="Available times:"
isRequired isRequired
validationRules="time-slot-required" /> validationRules="time-slot-required" />
@ -393,7 +394,7 @@ export default {
this.answersForDropOffQuestion[0].value !== PICK_A_TIME_BUTTON_VALUE this.answersForDropOffQuestion[0].value !== PICK_A_TIME_BUTTON_VALUE
); );
}, },
shouldDisplayTimeSlotQuestion() { displayTimeSlotQuestion() {
return ( return (
this.selectedAnswerForDropOffOrInshop == PICK_A_TIME_BUTTON_VALUE || this.selectedAnswerForDropOffOrInshop == PICK_A_TIME_BUTTON_VALUE ||
!this.isDropOffAppointmentAvailable !this.isDropOffAppointmentAvailable

View file

@ -80,6 +80,7 @@ export default {
}, },
set: function (newValue) { set: function (newValue) {
this.$emit("update:modelValue", newValue); this.$emit("update:modelValue", newValue);
this.$emit("handle-appointment-type-change", newValue);
}, },
}, },
isMobileOnly() { isMobileOnly() {

View file

@ -95,9 +95,8 @@ describe("vin-lookup.vue", () => {
it("Should call navigateForward() if the store carId does not match the vin response carId but does match previously enterted carId and forward button is clicked", async () => { it("Should call navigateForward() if the store carId does not match the vin response carId but does match previously enterted carId and forward button is clicked", async () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
mockOutPromises({ carId: "C11111" }); mockOutPromises({ carId: "C11111" }); //this line in above tests affects this test, so adding to here too till we figure out how to isolate the calls
wrapper.vm.lookupVehicle = jest.fn().mockImplementation(() => vinPromise);
wrapper.vm.navigateForward = jest.fn(); wrapper.vm.navigateForward = jest.fn();
wrapper.vm.previouslyEnteredCarId = "C11111"; wrapper.vm.previouslyEnteredCarId = "C11111";
@ -111,6 +110,7 @@ describe("vin-lookup.vue", () => {
it("Should not call navigateForward() if zip service returns a non-serviceable flag", async () => { it("Should not call navigateForward() if zip service returns a non-serviceable flag", async () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
mockOutPromises({ carId: "C11111" }); //this line in above tests affects this test, so adding to here too till we figure out how to isolate the calls
const zipValidationApiResponse = { const zipValidationApiResponse = {
data: { data: {
isServiceable: false, isServiceable: false,
@ -135,6 +135,8 @@ describe("vin-lookup.vue", () => {
it("Should not call navigateForward() when forward button is clicked but lookupVehicle errors out.", async () => { it("Should not call navigateForward() when forward button is clicked but lookupVehicle errors out.", async () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
mockOutPromises({ carId: "C11111" }); //this line in above tests affects this test, so adding to here too till we figure out how to isolate the calls
wrapper.vm.vinTouched = true; wrapper.vm.vinTouched = true;
wrapper.vm.vin = "foo"; wrapper.vm.vin = "foo";
wrapper.vm.initialVin = "!foo"; wrapper.vm.initialVin = "!foo";
@ -164,6 +166,7 @@ describe("vin-lookup.vue", () => {
isCarIdDifferent: true, isCarIdDifferent: true,
isSelectedGlassAvailableForVehicle: false, isSelectedGlassAvailableForVehicle: false,
}); });
wrapper.vm.pageName = "vin-lookup";
// Act // Act
await wrapper.vm.navigateForward(); await wrapper.vm.navigateForward();
@ -172,9 +175,7 @@ describe("vin-lookup.vue", () => {
expect(wrapper.vm.$router.navigateWithSaving).toBeCalledTimes(1); expect(wrapper.vm.$router.navigateWithSaving).toBeCalledTimes(1);
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith( expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(
navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS, navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
wrapper.vm.$route, "vin-lookup"
expect.anything(),
expect.anything()
); );
}); });
@ -251,12 +252,14 @@ describe("vin-lookup.vue", () => {
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
// Act // Act
const vinLookup = wrapper.findComponent('[data-test="vin-lookup-component"]'); const vinLookup = wrapper.findComponent({ ref: "vinLookupQuestion" });
vinLookup.trigger("imageLookupError"); vinLookup.trigger("image-lookup-error");
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
// Assert // Assert
expect(wrapper.findAllComponents({ name: "alert" }).length).toBe(1); expect(wrapper.vm.displayVinScanFailedAlert).toBe(true);
//TODO - Fix original check for alert box but should show if displayVinScanFailedAlert is true which I'm checking
//expect(wrapper.findAllComponents({ cmsWidgetName: "AlertVinScanFailed" }).length).toBe(1);
}); });
test("should hide the AlertNoService when displayNoServiceAlert is false", async () => { test("should hide the AlertNoService when displayNoServiceAlert is false", async () => {
@ -277,6 +280,7 @@ describe("vin-lookup.vue", () => {
}); });
}); });
/*
describe("getVinFromImage", () => { describe("getVinFromImage", () => {
test("GetVinFromImage resolves with first valid VIN when any vins are returned.", async () => { test("GetVinFromImage resolves with first valid VIN when any vins are returned.", async () => {
// Arrange // Arrange
@ -289,7 +293,7 @@ describe("vin-lookup.vue", () => {
const storeMixin = { const storeMixin = {
methods: { methods: {
dispatchStoreAction: lookup, dispatchStoreActionWithLogging: lookup,
}, },
}; };
@ -354,6 +358,7 @@ describe("vin-lookup.vue", () => {
await expect(promise).rejects.toEqual("An error occurred during the lookup."); await expect(promise).rejects.toEqual("An error occurred during the lookup.");
}); });
}); });
*/
}); });
function setupMocks({ customMountOptions }) { function setupMocks({ customMountOptions }) {

View file

@ -393,21 +393,18 @@ export default {
// Check if Service Zip entered is serviceable then save the ZIP info // Check if Service Zip entered is serviceable then save the ZIP info
if (zipCodeData.isServiceable) { if (zipCodeData.isServiceable) {
//Only save the zipCode, state, and zipCodeCtu if the zip changed or we lack zipCodeCtu //Always save the service zip info even if it was not changed;
if ( // if it didn't change it doesn't alter other values and this makes it more consistent
this.$store.getters.order.serviceLocation.zipCode != this.serviceZipCode || await this.dispatchStoreAction(
!this.$store.getters.order.serviceLocation.zipCodeCtu storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
) { {
await this.dispatchStoreAction( state: zipCodeData.state,
storeActions.SAVE_SERVICE_ZIP_CODE_INFO, zipCode: this.serviceZipCode,
{ zipCodeCtu: zipCodeData.zipCodeCtu,
state: zipCodeData.state, },
zipCode: this.serviceZipCode, false
zipCodeCtu: zipCodeData.zipCodeCtu, );
},
false
);
}
// if no value due to field being optional, blank both phone and email address // if no value due to field being optional, blank both phone and email address
if (!this.emailOrSms) { if (!this.emailOrSms) {
await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, "", false); await this.dispatchStoreAction(storeActions.SAVE_PHONE_NUMBER, "", false);

View file

@ -569,6 +569,10 @@ const routingTable = function () {
piaError: "true", piaError: "true",
}, },
}, },
{
scenario: navigationScenarios.CLICKED_PAY_LATER,
destinationPageData: routeData.CONFIRMATION,
},
], ],
}, },
{ {
@ -589,6 +593,10 @@ const routingTable = function () {
piaError: "true", piaError: "true",
}, },
}, },
{
scenario: navigationScenarios.CLICKED_PAY_LATER,
destinationPageData: routeData.CONFIRMATION,
},
], ],
}, },
{ {

View file

@ -27,7 +27,7 @@ export async function vehicleBeforeEnter(to, from) {
if (zipData.isValid) { if (zipData.isValid) {
store.dispatch(storeActions.SAVE_SERVICE_ZIP_CODE_INFO, { store.dispatch(storeActions.SAVE_SERVICE_ZIP_CODE_INFO, {
zipCode: newZipFromQuerystring, zipCode: newZipFromQuerystring,
state: zipData.state.state, state: zipData.state,
zipCodeCtu: zipData.zipCodeCtu, zipCodeCtu: zipData.zipCodeCtu,
}); });
} }