Merge branch 'develop' into feature/CSR-803-refactoring-2

This commit is contained in:
Adam Caouette 2022-11-16 16:49:50 -05:00
commit fc8605cbba
39 changed files with 1766 additions and 241 deletions

View file

@ -17,10 +17,8 @@ module.exports = {
"!src/layouts/capability-questions/**/*.vue", "!src/layouts/capability-questions/**/*.vue",
"!src/layouts/part-questions/**/*.vue", "!src/layouts/part-questions/**/*.vue",
"!src/layouts/reveal/**/*.vue", "!src/layouts/reveal/**/*.vue",
"!src/layouts/quote/**/*.vue",
"!src/ux-components/text-link/**/*.vue", "!src/ux-components/text-link/**/*.vue",
"!src/common-components/question-chain/**/*.vue", "!src/common-components/question-chain/**/*.vue",
"!src/layouts/quote/**/*.vue", // Temporary
"!src/layouts/vin-lookup/**/*.vue", //Temporary for Quote page testing "!src/layouts/vin-lookup/**/*.vue", //Temporary for Quote page testing
"!src/common-components/funnel-header/menu-modal/**/*.vue", "!src/common-components/funnel-header/menu-modal/**/*.vue",
// END // END

View file

@ -45,6 +45,7 @@ export default {
}; };
}, },
mounted() { mounted() {
// Need this here and in the watch for vehicle-parts, unless there'a better way
if (this.isChecked) { if (this.isChecked) {
this.handleChange(this.modelValue); this.handleChange(this.modelValue);
} }
@ -188,6 +189,13 @@ export default {
fieldOptions, // only need to expose this for unit test purposes fieldOptions, // only need to expose this for unit test purposes
}; };
}, },
watch: {
isChecked(isChecked) {
if (isChecked) {
this.handleChange(this.modelValue);
}
},
},
}; };
</script> </script>

View file

@ -1,4 +1,4 @@
import { shallowMount } from "@vue/test-utils"; import { shallowMount, mount } from "@vue/test-utils";
import buttonQuestion from "@/common-components/button-question/button-question"; import buttonQuestion from "@/common-components/button-question/button-question";
import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { getMountOptions } from "@/helpers/unit-test-helper.js";
@ -115,6 +115,38 @@ describe("buttonQuestion.vue", () => {
"2020", "2020",
]); ]);
}); });
test("should accept an imported component for buttonTypeObject and successfully add it to components", async () => {
// Arrange
const mockComponent = {
name: "mockComponent",
methods: {
mockComponentMethod() {
return "mockComponentMethod return value";
},
},
};
const wrapper = await shallowMount(
buttonQuestion,
setupMocks({
propsData: {
buttonTypeString: "mockComponent",
buttonTypeObject: mockComponent,
},
})
);
// Act
const componentList = wrapper.vm.$options.components;
const componentExists = !!componentList.mockComponent;
const componentNameMatches = componentList.mockComponent.name === mockComponent.name;
const componentMethodMatches =
componentList.mockComponent.methods.mockComponentMethod() ===
mockComponent.methods.mockComponentMethod();
// Assert
expect(componentExists && componentNameMatches && componentMethodMatches).toBe(true);
});
}); });
describe("buttonsInfo", () => { describe("buttonsInfo", () => {

View file

@ -15,6 +15,10 @@
data-bs-target="#footerModal" data-bs-target="#footerModal"
data-bs-dismiss="modal" /> data-bs-dismiss="modal" />
</div> </div>
<!-- TODO KO DELETE FOR QUOTE MVP -->
<div class="col-auto">
<button @click="$emit('tempButtonClicked')">Heritage</button>
</div>
<div v-if="!isBackButtonHidden" class="col-auto link-col py-1 text-break"> <div v-if="!isBackButtonHidden" class="col-auto link-col py-1 text-break">
<textLink <textLink
linkType="navigation" linkType="navigation"

View file

@ -6,6 +6,10 @@ describe("modal.vue", () => {
// Act // Act
const wrapper = shallowMount(Modal, { const wrapper = shallowMount(Modal, {
mixins: [mockMixin], mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
}); });
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["HeaderText"])); expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["HeaderText"]));
}); });
@ -14,6 +18,10 @@ describe("modal.vue", () => {
// Act // Act
const wrapper = shallowMount(Modal, { const wrapper = shallowMount(Modal, {
mixins: [mockMixin], mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
}); });
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["SubheaderText"])); expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["SubheaderText"]));
}); });
@ -22,6 +30,10 @@ describe("modal.vue", () => {
// Act // Act
const wrapper = shallowMount(Modal, { const wrapper = shallowMount(Modal, {
mixins: [mockMixin], mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
}); });
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["Image"])); expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["Image"]));
}); });
@ -30,6 +42,10 @@ describe("modal.vue", () => {
// Act // Act
const wrapper = shallowMount(Modal, { const wrapper = shallowMount(Modal, {
mixins: [mockMixin], mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
}); });
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["BodyText"])); expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["BodyText"]));
}); });

View file

@ -25,7 +25,7 @@
<buttonMain <buttonMain
class="w-100" class="w-100"
ref="buttonMain" ref="buttonMain"
isPrimary suppressLoader
:buttonText="ModalCloseButtonText" :buttonText="ModalCloseButtonText"
@click-event="buttonClick" @click-event="buttonClick"
data-bs-dismiss="modal" /> data-bs-dismiss="modal" />
@ -60,6 +60,13 @@ export default {
return this.getCmsContent(this.cmsWidgetName, "FooterText"); return this.getCmsContent(this.cmsWidgetName, "FooterText");
}, },
}, },
mounted() {
console.log("#1", document);
const modal = document.querySelector("#" + this.cmsWidgetName);
modal.addEventListener("hidden.bs.modal", (event) => {
this.$refs.buttonMain.resetButtonStyle();
});
},
components: { components: {
buttonMain, buttonMain,
}, },

View file

@ -1,6 +1,5 @@
<template> <template>
<div :class="`page-container-grouped-styles questions-page`"> <div :class="`page-container-grouped-styles questions-page`">
<loadingModal ref="loadingModal" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="false" /> <vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="false" />
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" /> <funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
@ -41,7 +40,6 @@ import alert from "@/ux-components/alert/alert";
import questionChain from "@/common-components/question-chain/question-chain"; import questionChain from "@/common-components/question-chain/question-chain";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import funnelFooter from "@/common-components/funnel-footer/funnel-footer"; import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
import loadingModal from "@/common-components/loading-modal/loading-modal.vue";
export default { export default {
name: "questions-page", name: "questions-page",
@ -77,9 +75,6 @@ export default {
handleBackButtonAction() { handleBackButtonAction() {
this.$emit("backButtonAction"); this.$emit("backButtonAction");
}, },
showLoadingModal() {
this.$refs.loadingModal.showModal();
},
}, },
components: { components: {
funnelHeader, funnelHeader,
@ -88,7 +83,6 @@ export default {
questionChain, questionChain,
funnelSubHeader, funnelSubHeader,
funnelFooter, funnelFooter,
loadingModal,
}, },
}; };
</script> </script>

View file

@ -76,7 +76,7 @@ const endpoints = {
}, },
LoadSession: { LoadSession: {
url: "/order/api/v1/order/load-session", url: "/order/api/v1/order/load-session",
method: "POST", method: "GET",
}, },
ValidateZip: { ValidateZip: {
url: "/location/api/v1/location/zip", url: "/location/api/v1/location/zip",

View file

@ -21,6 +21,7 @@ export function updateOrCreateFunnelCookie() {
ReferralParentAccountNumber: store.getters.order.accountNumber, ReferralParentAccountNumber: store.getters.order.accountNumber,
HasDelayedClaimRegistration: wasClaimRegistrationDelayed, HasDelayedClaimRegistration: wasClaimRegistrationDelayed,
SuppressConceptFunnel: shouldSuppressConceptFunnel, SuppressConceptFunnel: shouldSuppressConceptFunnel,
SavedSessionId: store.getters.applicationUser.savedSessionId,
}); });
} }

View file

@ -17,12 +17,8 @@ import { storeMutations } from "@/constants/store-mutations";
export async function loadSessionIfPresent() { export async function loadSessionIfPresent() {
const funnelCookie = getFunnelCookie(); const funnelCookie = getFunnelCookie();
// Do nothing if there is no cookie, correlation id, or referral number. // Do nothing if there is no cookie or session to use for loading.
if ( if (funnelCookie == null || funnelCookie.SavedSessionId == null) {
funnelCookie == null ||
funnelCookie.ReferralCorrelationId == null ||
!funnelCookie.ReferralNumber
) {
return null; return null;
} }
@ -33,15 +29,8 @@ export async function loadSessionIfPresent() {
return null; return null;
} }
// Load referral if there is a cookie, and it doesn't indicate it needs a state reset. // Loads session including referral if there is a cookie, and it doesn't indicate it needs a state reset.
return ( return (await loadSession(funnelCookie.SavedSessionId)).data;
await loadSession(
funnelCookie.ReferralNumber,
funnelCookie.ReferralDate,
funnelCookie.ReferralCorrelationId,
funnelCookie.ReferralParentAccountNumber
)
).data;
} }
/* /*
@ -72,16 +61,13 @@ export async function saveSession() {
Calls API to load session given the referral number, referralDate, and referralCorrelationId Calls API to load session given the referral number, referralDate, and referralCorrelationId
and returns the response. and returns the response.
*/ */
async function loadSession(referralNumber, referralDate, referralCorrelationId, accountNumber) { async function loadSession(savedSessionId) {
// await the saveSessionPromise in the store to make sure we're loading up to date information // await the saveSessionPromise in the store to make sure we're loading up to date information
await store.getters.applicationUser.saveSessionPromise; await store.getters.applicationUser.saveSessionPromise;
const response = await baseMixin.methods.dispatchStoreAction( const response = await baseMixin.methods.dispatchStoreAction(
storeActions.LOAD_SESSION, storeActions.LOAD_SESSION,
{ {
referralNumber: referralNumber.toString(), savedSessionId: savedSessionId?.toString(),
referralDate: referralDate,
referralCorrelationId: referralCorrelationId,
accountNumber: accountNumber?.toString(),
}, },
false false
); );

View file

@ -24,6 +24,7 @@ describe("loadSessionIfPresent", () => {
ShouldResetState: testShouldResetState, ShouldResetState: testShouldResetState,
ReferralCorrelationId: "xxx", ReferralCorrelationId: "xxx",
ReferralNumber: "12345", ReferralNumber: "12345",
SavedSessionId: "aa115a05-e268-4eb4-a095-1d54b44f4a99",
}; };
document.cookie = `${cookieNames.FUNNEL_SESSION_INFO}=${JSON.stringify( document.cookie = `${cookieNames.FUNNEL_SESSION_INFO}=${JSON.stringify(
@ -46,6 +47,7 @@ describe("loadSessionIfPresent", () => {
ShouldResetState: true, ShouldResetState: true,
ReferralCorrelationId: "xxx-xxx-xxx", ReferralCorrelationId: "xxx-xxx-xxx",
ReferralNumber: "12345", ReferralNumber: "12345",
SavedSessionId: "aa115a05-e268-4eb4-a095-1d54b44f4a99",
}); });
const mockData = { const mockData = {
@ -103,6 +105,7 @@ describe("loadSessionIfPresent", () => {
ReferralNumber: 123456, ReferralNumber: 123456,
ReferralCorrelationId: "yyy-yyy-yyyy", ReferralCorrelationId: "yyy-yyy-yyyy",
ReferralDate: new Date(), ReferralDate: new Date(),
SavedSessionId: "aa115a05-e268-4eb4-a095-1d54b44f4a99",
}); });
const mockData = { const mockData = {

View file

@ -724,7 +724,6 @@ function setupMocks({
wrapper.vm.setCmsContent = jest.fn(); wrapper.vm.setCmsContent = jest.fn();
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn();
wrapper.vm.$refs.loadingModal.showModal = jest.fn();
return { wrapper }; return { wrapper };
} }

View file

@ -6,7 +6,6 @@
v-slot="{ meta }" v-slot="{ meta }"
autocomplete="off"> autocomplete="off">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<loadingModal ref="loadingModal" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
<vehicleBanner <vehicleBanner
cmsWidgetName="VehicleBannerWidget" cmsWidgetName="VehicleBannerWidget"
@ -89,7 +88,6 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he
import customerQuestions from "@/layouts/address-lookup/customer-questions/customer-questions"; import customerQuestions from "@/layouts/address-lookup/customer-questions/customer-questions";
import alert from "@/ux-components/alert/alert"; import alert from "@/ux-components/alert/alert";
import textboxQuestion from "@/common-components/textbox-question/textbox-question"; import textboxQuestion from "@/common-components/textbox-question/textbox-question";
import loadingModal from "@/common-components/loading-modal/loading-modal.vue";
import { Form, defineRule } from "vee-validate"; import { Form, defineRule } from "vee-validate";
import { required, regex } from "@/helpers/validation-rules"; import { required, regex } from "@/helpers/validation-rules";
@ -458,7 +456,6 @@ export default {
customerQuestions, customerQuestions,
textboxQuestion, textboxQuestion,
alert, alert,
loadingModal,
Form, Form,
}, },
}; };

View file

@ -149,7 +149,6 @@ describe("addressVehicles.vue", () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
wrapper.vm.$refs.loadingModal.showModal = jest.fn();
navigateToHeritage.navigateToHeritageFunnel = jest.fn(); navigateToHeritage.navigateToHeritageFunnel = jest.fn();
// Act // Act

View file

@ -1,7 +1,6 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<loadingModal ref="loadingModal" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<vehicleBanner <vehicleBanner
cmsWidgetName="VehicleBannerWidget" cmsWidgetName="VehicleBannerWidget"
@ -53,7 +52,6 @@ import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import alert from "@/ux-components/alert/alert"; import alert from "@/ux-components/alert/alert";
import addressVehiclesQuestion from "@/layouts/address-vehicles/address-vehicles-question/address-vehicles-question"; import addressVehiclesQuestion from "@/layouts/address-vehicles/address-vehicles-question/address-vehicles-question";
import loadingModal from "@/common-components/loading-modal/loading-modal.vue";
// Supporting files // Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
@ -239,7 +237,6 @@ export default {
alert, alert,
funnelFooter, funnelFooter,
addressVehiclesQuestion, addressVehiclesQuestion,
loadingModal,
}, },
}; };
</script> </script>

View file

@ -161,7 +161,9 @@ export default {
).parts = partFromCapabilityQuestionAnswer; ).parts = partFromCapabilityQuestionAnswer;
} }
this.navigateForward(partsOrQuestions); // this.navigateForward(partsOrQuestions);
// TODO KO delete after quote MVP
this.navigateForward(partsOrQuestions, null, this.shouldGoToHeritageQuote);
}, },
}, },
components: { components: {

View file

@ -39,32 +39,59 @@ describe("estimate.vue", () => {
]); ]);
}); });
test("After selecting provide my home address on ForwardButtonAction triggers a router.navigateWithSaving", async () => { describe("move forward", () => {
//Arrange test("After selecting provide my home address on ForwardButtonAction triggers a router.navigateWithSaving", async () => {
const { wrapper } = setupMocks({}); //Arrange
await wrapper.setData({ const { wrapper } = setupMocks({});
selectedVinLookupMethod: vinLookupMethodSelections.HOMEADDRESS, await wrapper.setData({
selectedVinLookupMethod: vinLookupMethodSelections.HOMEADDRESS,
});
//Act
wrapper.vm.forwardButtonAction();
//Assert
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled();
}); });
//Act test("After selecting provide my manual vin on ForwardButtonAction triggers a router.navigateWithSaving", async () => {
wrapper.vm.forwardButtonAction(); //Arrange
const { wrapper } = setupMocks({});
await wrapper.setData({
selectedVinLookupMethod: vinLookupMethodSelections.MANUALVIN,
});
//Assert //Act
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled(); await wrapper.vm.forwardButtonAction();
});
test("After selecting provide my manual vin on ForwardButtonAction triggers a router.navigateWithSaving", async () => { //Assert
//Arrange expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled();
const { wrapper } = setupMocks({});
await wrapper.setData({
selectedVinLookupMethod: vinLookupMethodSelections.MANUALVIN,
}); });
//Act test("Provide my license plate on ForwardButtonAction triggers a router.navigateWithSaving", async () => {
await wrapper.vm.forwardButtonAction(); //Arrange
const { wrapper } = setupMocks({});
await wrapper.setData({
selectedVinLookupMethod: vinLookupMethodSelections.LICENSEPLATE,
});
//Assert //Act
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled(); wrapper.vm.forwardButtonAction();
//Assert
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled();
});
// TODO KO
describe("isRepair", () => {
test.todo("is repair and zip codes are valid/serviceable => go to quote page");
test.todo(
"is repair and zip codes are valid but not serviceable => show correct alert, remove loader"
);
test.todo(
"is repair and zip codes are not valid/serviceable => show correct alert, remove loader"
);
});
}); });
test("BackButtonAction triggers a router.navigateWithoutSaving change", async () => { test("BackButtonAction triggers a router.navigateWithoutSaving change", async () => {
@ -85,20 +112,6 @@ describe("estimate.vue", () => {
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled(); expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled();
}); });
test("Provide my license plate on ForwardButtonAction triggers a router.navigateWithSaving", async () => {
//Arrange
const { wrapper } = setupMocks({});
await wrapper.setData({
selectedVinLookupMethod: vinLookupMethodSelections.LICENSEPLATE,
});
//Act
wrapper.vm.forwardButtonAction();
//Assert
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled();
});
describe("arePagePrerequisitesValid", () => { describe("arePagePrerequisitesValid", () => {
beforeEach(() => { beforeEach(() => {
store.commit(storeMutations.UPDATE_IS_REPAIR, false); store.commit(storeMutations.UPDATE_IS_REPAIR, false);

View file

@ -1,7 +1,6 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<loadingModal ref="loadingModal" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<vehicleBanner cmsWidgetName="VehicleBannerWidget" /> <vehicleBanner cmsWidgetName="VehicleBannerWidget" />
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" /> <funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
@ -91,7 +90,6 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he
import buttonQuestion from "@/common-components/button-question/button-question"; import buttonQuestion from "@/common-components/button-question/button-question";
import alert from "@/ux-components/alert/alert"; import alert from "@/ux-components/alert/alert";
import textboxQuestion from "@/common-components/textbox-question/textbox-question"; import textboxQuestion from "@/common-components/textbox-question/textbox-question";
import loadingModal from "@/common-components/loading-modal/loading-modal.vue";
import textBlock from "@/common-components/text-block/text-block"; import textBlock from "@/common-components/text-block/text-block";
//Supporting Files //Supporting Files
@ -197,8 +195,10 @@ export default {
} }
this.displayNonServiceableZipAlert = false; this.displayNonServiceableZipAlert = false;
this.$refs.loadingModal.showModal(); return this.$router.navigateWithSaving(
navigateToHeritageFunnel(); this.navigationScenarios.HAS_NO_QUESTIONS,
this.$route
);
} }
if (this.selectedVinLookupMethod === vinLookupMethodSelections.MANUALVIN) { if (this.selectedVinLookupMethod === vinLookupMethodSelections.MANUALVIN) {
@ -256,7 +256,6 @@ export default {
Form, Form,
alert, alert,
textboxQuestion, textboxQuestion,
loadingModal,
textBlock, textBlock,
}, },
}; };

View file

@ -612,7 +612,6 @@ function setupMocks({
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ""); wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => "");
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn();
wrapper.vm.$refs.loadingModal.showModal = jest.fn();
return { wrapper, apiPromise }; return { wrapper, apiPromise };
} }

View file

@ -1,7 +1,6 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<loadingModal ref="loadingModal" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<vehicleBanner <vehicleBanner
cmsWidgetName="VehicleBannerWidget" cmsWidgetName="VehicleBannerWidget"
@ -98,7 +97,6 @@ import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import alert from "@/ux-components/alert/alert"; import alert from "@/ux-components/alert/alert";
import textboxQuestion from "@/common-components/textbox-question/textbox-question"; import textboxQuestion from "@/common-components/textbox-question/textbox-question";
import loadingModal from "@/common-components/loading-modal/loading-modal.vue";
import textBlock from "@/common-components/text-block/text-block"; import textBlock from "@/common-components/text-block/text-block";
// Supporting files // Supporting files
@ -390,7 +388,6 @@ export default {
funnelSubHeader, funnelSubHeader,
textboxQuestion, textboxQuestion,
alert, alert,
loadingModal,
textBlock, textBlock,
Form, Form,
}, },

View file

@ -146,7 +146,9 @@ export default {
]; ];
} }
this.navigateForward(partsOrQuestions); // this.navigateForward(partsOrQuestions);
// TODO KO delete after quote MVP
this.navigateForward(partsOrQuestions, null, this.shouldGoToHeritageQuote);
}, },
}, },
components: { components: {

View file

@ -129,15 +129,14 @@ export default {
); );
// call API parts method // call API parts method
const partsLookup = await this.dispatchStoreAction(this.storeActions.GET_PARTS).catch( const partsLookup = await this.dispatchStoreAction(this.storeActions.GET_PARTS);
() => {
return this.$refs.funnelFooter.removeLoader();
}
);
const glassPartsForStore = partsLookup.data.glassPieceParts; const glassPartsForStore = partsLookup.data.glassPieceParts;
this.navigateForward(glassPartsForStore); // this.navigateForward(glassPartsForStore);
// TODO KO delete for quote mvp
this.navigateForward(glassPartsForStore, null, this.shouldGoToHeritageQuote);
}, },
}, },
components: { components: {

View file

@ -56,6 +56,7 @@ import servicePackageQuestion from "./service-package-question/service-package-q
import textBlock from "@/common-components/text-block/text-block"; import textBlock from "@/common-components/text-block/text-block";
import modal from "@/common-components/modal/modal"; import modal from "@/common-components/modal/modal";
import baseMixin from "@/mixins/base-mixin.js"; import baseMixin from "@/mixins/base-mixin.js";
import vehicleQuestionsMixin from "../../mixins/vehicle-questions-mixin";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { Form } from "vee-validate"; import { Form } from "vee-validate";
@ -155,6 +156,9 @@ export default {
: null; : null;
} }
}, },
backButtonAction() {
vehicleQuestionsMixin.methods.backButtonAction(this);
},
}, },
components: { components: {
funnelHeader, funnelHeader,

View file

@ -0,0 +1,580 @@
export const mockProcessedCmsContent = {
"05_01_CSR_Quote_Standard_Repair": {
EconomyServicePackage: {
HeaderText: "Economy service",
SubheaderText: "",
BodyText:
"<ul><li>Expert windshield repair</li><li>Exclusive resin sealant</li><li>Nationwide lifetime guarantee</li></ul>",
Image: "",
FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
},
StandardServicePackage: {
HeaderText: "Standard service",
SubheaderText: "MOST POPULAR",
BodyText:
"<ul><li>Expert windshield repair</li><li>Exclusive resin sealant</li><li>Nationwide lifetime guarantee</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "",
FooterText: "",
},
PremiumServicePackage: {
HeaderText: "Premium service",
SubheaderText: "",
BodyText:
"<ul><li>Expert windshield repair</li><li>Exclusive resin sealant</li><li>Nationwide lifetime guarantee</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "",
FooterText: "",
},
},
"05_01_CSR_Quote_Recal": {
EconomyServicePackage: {
HeaderText: "Economy service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "",
FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
},
StandardServicePackage: {
HeaderText: "Standard service",
SubheaderText: "MOST POPULAR",
BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "",
FooterText: "",
},
PremiumServicePackage: {
HeaderText: "Premium service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "",
FooterText: "",
},
},
"05_01_CSR_Quote_RearGlass": {
EconomyServicePackage: {
HeaderText: "Economy service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "",
FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
},
StandardServicePackage: {
HeaderText: "Standard service",
SubheaderText: "MOST POPULAR",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "",
FooterText: "",
},
PremiumServicePackage: {
HeaderText: "Premium service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "",
FooterText: "",
},
},
"05_01_CSR_Quote_RearGlass+NonWindshield": {
EconomyServicePackage: {
HeaderText: "Economy service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "",
FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
},
StandardServicePackage: {
HeaderText: "Standard service",
SubheaderText: "MOST POPULAR",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "",
FooterText: "",
},
PremiumServicePackage: {
HeaderText: "Premium service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "",
FooterText: "",
},
},
"05_01_CSR_Quote_RearGlass+Windshield": {
EconomyServicePackage: {
HeaderText: "Economy service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "",
FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
},
StandardServicePackage: {
HeaderText: "Standard service",
SubheaderText: "MOST POPULAR",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "",
FooterText: "",
},
PremiumServicePackage: {
HeaderText: "Premium service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "",
FooterText: "",
},
},
"05_01_CSR_Quote_RearGlassNoFrontFit": {
EconomyServicePackage: {
HeaderText: "Economy service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "",
FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
},
StandardServicePackage: {
HeaderText: "Standard service",
SubheaderText: "MOST POPULAR",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "",
FooterText: "",
},
PremiumServicePackage: {
HeaderText: "Premium service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "",
FooterText: "",
},
},
"05_01_CSR_Quote_Windshield+SideGlass": {
EconomyServicePackage: {
HeaderText: "Economy service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "",
FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
},
StandardServicePackage: {
HeaderText: "Standard service",
SubheaderText: "MOST POPULAR",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "",
FooterText: "",
},
PremiumServicePackage: {
HeaderText: "Premium service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "",
FooterText: "",
},
},
"05_01_CSR_Quote_SideGlass": {
EconomyServicePackage: {
HeaderText: "Economy service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "",
FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
},
StandardServicePackage: {
HeaderText: "Standard service",
SubheaderText: "MOST POPULAR",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "",
FooterText: "",
},
PremiumServicePackage: {
HeaderText: "Premium service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement glass</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "",
FooterText: "",
},
},
"05_01_CSR_Quote_NoWiperFit": {
EconomyServicePackage: {
HeaderText: "Economy service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "",
FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
},
StandardServicePackage: {
HeaderText: "Standard service",
SubheaderText: "MOST POPULAR",
BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "",
FooterText: "",
},
PremiumServicePackage: {
HeaderText: "Premium service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "",
FooterText: "",
},
},
badCustomValue: {
EconomyServicePackage: {
HeaderText: "Economy service",
SubheaderText: "",
BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li></ul>",
Image: "",
FooterText:
"{if:custom:frontWipersApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{else}{if:custom:rearWiperApplicableForTierTwo}New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.{end}{end}",
},
StandardServicePackage: {
HeaderText: "Standard service",
SubheaderText: "MOST POPULAR",
BodyText:
"<ul><li>New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierTwo}{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierTwo}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li></ul>",
Image: "",
FooterText: "",
},
PremiumServicePackage: {
HeaderText: "Premium service",
SubheaderText: "",
BodyText:
"<ul><li>{if:custom:badCustomValue}DO NOT SHOW{end}New replacement windshield</li><li>Expert installation{if:custom:isRecalibrationOnOrder} and {textLink:EstimateRecalModal,recalibration}{end}</li><li>Nationwide lifetime warranty</li><li>{if:custom:frontWipersApplicableForTierThree}{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateFrontWiperModal,front wiper blades}{else}New {textLink:EstimateFrontWiperModal,wiper blades}{end}{end}</li><li>{if:custom:rearWiperApplicableForTierThree}New {textLink:EstimateRearWiperModal,rear wiper blade}{end}</li><li>{if:custom:rainDefenseApplicableForTierThree}{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment{end}</li></ul>",
Image: "",
FooterText: "",
},
},
};
export const inputQuestionWidgetAnswers = {
CashServicePackageQuestionWidget: {
Answers: [
{
Name: "TierOne",
SubWidgetName: "EconomyServicePackage",
},
{
Name: "TierTwo",
SubWidgetName: "StandardServicePackage",
},
{
Name: "TierThree",
SubWidgetName: "PremiumServicePackage",
},
],
},
InsuranceServicePackageQuestionWidget: {
Answers: [
{
Name: "TierOne",
SubWidgetName: "EconomyServicePackage",
},
{
Name: "TierTwo",
SubWidgetName: "StandardServicePackage",
},
{
Name: "TierThree",
SubWidgetName: "PremiumServicePackage",
},
],
},
};
export const expectedModifiedAnswers = {
"05_01_CSR_Quote_Standard_Repair": {
tierOne: {
value: "TierOne",
buttonLabel: "Economy service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>Expert windshield repair</li><li>Exclusive resin sealant</li><li>Nationwide lifetime guarantee</li></ul>",
buttonAuxillaryCopy: "$500.22",
buttonFooterCopy:
"New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.",
},
tierTwo: {
value: "TierTwo",
buttonLabel: "Standard service",
buttonLabelSubCopy: "MOST POPULAR",
buttonBodyCopy:
"<ul><li>Expert windshield repair</li><li>Exclusive resin sealant</li><li>Nationwide lifetime guarantee</li><li>New {textLink:EstimateFrontWiperModal,wiper blades}</li><li></li></ul>",
buttonAuxillaryCopy: "$585.90",
buttonFooterCopy: "",
},
tierThree: {
value: "TierThree",
buttonLabel: "Premium service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>Expert windshield repair</li><li>Exclusive resin sealant</li><li>Nationwide lifetime guarantee</li><li>New {textLink:EstimateFrontWiperModal,wiper blades}</li><li></li><li>{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment</li></ul>",
buttonAuxillaryCopy: "$621.40",
buttonFooterCopy: "",
},
},
"05_01_CSR_Quote_Recal": {
tierOne: {
value: "TierOne",
buttonLabel: "Economy service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement windshield</li><li>Expert installation and {textLink:EstimateRecalModal,recalibration}</li><li>Nationwide lifetime warranty</li></ul>",
buttonAuxillaryCopy: "$500.22",
buttonFooterCopy:
"New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.",
},
tierTwo: {
value: "TierTwo",
buttonLabel: "Standard service",
buttonLabelSubCopy: "MOST POPULAR",
buttonBodyCopy:
"<ul><li>New replacement windshield</li><li>Expert installation and {textLink:EstimateRecalModal,recalibration}</li><li>Nationwide lifetime warranty</li><li>New {textLink:EstimateFrontWiperModal,wiper blades}</li><li></li></ul>",
buttonAuxillaryCopy: "$585.90",
buttonFooterCopy: "",
},
tierThree: {
value: "TierThree",
buttonLabel: "Premium service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement windshield</li><li>Expert installation and {textLink:EstimateRecalModal,recalibration}</li><li>Nationwide lifetime warranty</li><li>New {textLink:EstimateFrontWiperModal,wiper blades}</li><li></li><li>{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment</li></ul>",
buttonAuxillaryCopy: "$621.40",
buttonFooterCopy: "",
},
},
"05_01_CSR_Quote_RearGlass": {
tierOne: {
value: "TierOne",
buttonLabel: "Economy service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li></ul>",
buttonAuxillaryCopy: "$350.22",
buttonFooterCopy:
"New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.",
},
tierTwo: {
value: "TierTwo",
buttonLabel: "Standard service",
buttonLabelSubCopy: "MOST POPULAR",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li><li></li><li>New {textLink:EstimateRearWiperModal,rear wiper blade}</li></ul>",
buttonAuxillaryCopy: "$374.70",
buttonFooterCopy: "",
},
tierThree: {
value: "TierThree",
buttonLabel: "Premium service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li><li>New {textLink:EstimateFrontWiperModal,front wiper blades}</li><li>New {textLink:EstimateRearWiperModal,rear wiper blade}</li><li></li></ul>",
buttonAuxillaryCopy: "$460.38",
buttonFooterCopy: "",
},
},
"05_01_CSR_Quote_RearGlass+NonWindshield": {
tierOne: {
value: "TierOne",
buttonLabel: "Economy service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li></ul>",
buttonAuxillaryCopy: "$350.22",
buttonFooterCopy:
"New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.",
},
tierTwo: {
value: "TierTwo",
buttonLabel: "Standard service",
buttonLabelSubCopy: "MOST POPULAR",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li><li></li><li>New {textLink:EstimateRearWiperModal,rear wiper blade}</li></ul>",
buttonAuxillaryCopy: "$374.70",
buttonFooterCopy: "",
},
tierThree: {
value: "TierThree",
buttonLabel: "Premium service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li><li>New {textLink:EstimateFrontWiperModal,front wiper blades}</li><li>New {textLink:EstimateRearWiperModal,rear wiper blade}</li><li></li></ul>",
buttonAuxillaryCopy: "$460.38",
buttonFooterCopy: "",
},
},
"05_01_CSR_Quote_RearGlass+Windshield": {
tierOne: {
value: "TierOne",
buttonLabel: "Economy service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li></ul>",
buttonAuxillaryCopy: "$350.22",
buttonFooterCopy:
"New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.",
},
tierTwo: {
value: "TierTwo",
buttonLabel: "Standard service",
buttonLabelSubCopy: "MOST POPULAR",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li><li>New {textLink:EstimateFrontWiperModal,front wiper blades}</li><li>New {textLink:EstimateRearWiperModal,rear wiper blade}</li></ul>",
buttonAuxillaryCopy: "$460.38",
buttonFooterCopy: "",
},
tierThree: {
value: "TierThree",
buttonLabel: "Premium service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li><li>New {textLink:EstimateFrontWiperModal,front wiper blades}</li><li>New {textLink:EstimateRearWiperModal,rear wiper blade}</li><li>{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment</li></ul>",
buttonAuxillaryCopy: "$495.88",
buttonFooterCopy: "",
},
},
"05_01_CSR_Quote_RearGlassNoFrontFit": {
tierOne: {
value: "TierOne",
buttonLabel: "Economy service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li></ul>",
buttonAuxillaryCopy: "$350.22",
buttonFooterCopy:
"New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.",
},
tierTwo: {
value: "TierTwo",
buttonLabel: "Standard service",
buttonLabelSubCopy: "MOST POPULAR",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li><li></li><li>New {textLink:EstimateRearWiperModal,rear wiper blade}</li></ul>",
buttonAuxillaryCopy: "$374.70",
buttonFooterCopy: "",
},
tierThree: {
value: "TierThree",
buttonLabel: "Premium service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li><li></li><li>New {textLink:EstimateRearWiperModal,rear wiper blade}</li><li>{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment</li></ul>",
buttonAuxillaryCopy: "$410.20",
buttonFooterCopy: "",
},
},
"05_01_CSR_Quote_Windshield+SideGlass": {
tierOne: {
value: "TierOne",
buttonLabel: "Economy service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li></ul>",
buttonAuxillaryCopy: "$350.22",
buttonFooterCopy:
"New wiper blades are not included with Economy service. Because chips can disrupt the glass surface, we recommend new wipers after a repair.",
},
tierTwo: {
value: "TierTwo",
buttonLabel: "Standard service",
buttonLabelSubCopy: "MOST POPULAR",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li><li>New {textLink:EstimateFrontWiperModal,wiper blades}</li><li></li></ul>",
buttonAuxillaryCopy: "$435.90",
buttonFooterCopy: "",
},
tierThree: {
value: "TierThree",
buttonLabel: "Premium service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li><li>New {textLink:EstimateFrontWiperModal,wiper blades}</li><li></li><li>{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment</li></ul>",
buttonAuxillaryCopy: "$471.40",
buttonFooterCopy: "",
},
},
"05_01_CSR_Quote_SideGlass": {
tierOne: {
value: "TierOne",
buttonLabel: "Economy service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li></ul>",
buttonAuxillaryCopy: "$350.22",
buttonFooterCopy: "",
},
tierThree: {
value: "TierThree",
buttonLabel: "Premium service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement glass</li><li>Expert installation</li><li>Nationwide lifetime warranty</li><li>New {textLink:EstimateFrontWiperModal,wiper blades}</li><li></li><li>{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment</li></ul>",
buttonAuxillaryCopy: "$471.40",
buttonFooterCopy: "",
},
},
"05_01_CSR_Quote_NoWiperFit": {
tierOne: {
value: "TierOne",
buttonLabel: "Economy service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement windshield</li><li>Expert installation</li><li>Nationwide lifetime warranty</li></ul>",
buttonAuxillaryCopy: "$350.22",
buttonFooterCopy: "",
},
tierThree: {
value: "TierThree",
buttonLabel: "Premium service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement windshield</li><li>Expert installation</li><li>Nationwide lifetime warranty</li><li></li><li></li><li>{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment</li></ul>",
buttonAuxillaryCopy: "$385.72",
buttonFooterCopy: "",
},
},
badCustomValue: {
tierOne: {
value: "TierOne",
buttonLabel: "Economy service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement windshield</li><li>Expert installation</li><li>Nationwide lifetime warranty</li></ul>",
buttonAuxillaryCopy: "$350.22",
buttonFooterCopy: "",
},
tierThree: {
value: "TierThree",
buttonLabel: "Premium service",
buttonLabelSubCopy: "",
buttonBodyCopy:
"<ul><li>New replacement windshield</li><li>Expert installation</li><li>Nationwide lifetime warranty</li><li></li><li></li><li>{textLink:EstimateRainDefenseModal,Rain Defense}&trade; treatment</li></ul>",
buttonAuxillaryCopy: "$385.72",
buttonFooterCopy: "",
},
},
};

View file

@ -0,0 +1,595 @@
import { shallowMount, mount } from "@vue/test-utils";
import servicePackageQuestion from "./service-package-question";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import {
expectedModifiedAnswers,
mockProcessedCmsContent,
inputQuestionWidgetAnswers,
} from "./service-package-question-test-helper";
jest.mock("@/store", () => ({
commit: jest.fn(),
dispatch: jest.fn(),
}));
describe("service-package-question.vue", () => {
beforeEach(async () => {
processedCmsContent = inputQuestionWidgetAnswers;
mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
return processedCmsContent[widgetName][cmsFieldName];
}),
},
};
mockProps = {
modelValue: "initialValue",
isInsuranceSelected: false,
insuranceCmsWidgetName: "InsuranceServicePackageQuestionWidget",
cashCmsWidgetName: "CashServicePackageQuestionWidget",
availableLineItems: [
{
partNumber: "001",
Description: "Windshield with Recal",
partType: "Windshield",
Quantity: "1",
BasePartNumber: "001",
Color: "Green",
CanSafeliteRecalibrate: true,
price: 350.22,
},
{
partNumber: "RAIN DEFENSE",
description: null,
partType: "RAIN DEFENSE",
price: 35.5,
},
],
};
const packageNameKey = "05_01_CSR_Quote_Standard_Repair";
Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]);
});
it("should emit captured value", () => {
// Arrange
const wrapper = setupMocks({});
// Act
wrapper.componentVM.selectedValues = "newValue";
// Assert
expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual("newValue");
});
it("should have the correct insurance pricing text when insurance is selected", () => {
// Arrange
mockProps.isInsuranceSelected = true;
const wrapper = setupMocks({});
// Act
// Assert
expect(
wrapper.vm.servicePackageAnswers[0].buttonAuxillaryCopy.includes("As little as")
).toBe(true);
});
it("should return [] from nullSafeAvailableLineItems and not error out if availableLineItems is null", () => {
// Arrange
mockProps.availableLineItems = null;
const wrapper = setupMocks({});
// Act
// Assert
expect(wrapper.vm.nullSafeAvailableLineItems).toEqual([]);
});
it("should return a null servicePackageAnswers if the cmsContent is falsy", () => {
// Arrange
mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
return null;
}),
},
};
const wrapper = setupMocks({});
// Assert
expect(wrapper.vm.servicePackageAnswers).toBe(null);
});
it("should treat an undefined 'getCustomValueFromString' as a false value and not error out", () => {
// Arrange
const packageNameKey = "badCustomValue";
Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]);
const wrapper = setupMocks({
mountOptionsMockData: {
store: {
getters: {
order: {
damage: {
isRepair: false,
glassToReplace: [{ glassLocation: "Windshield" }],
},
},
hasAnyNonWindshieldGlassParts: false,
},
},
},
});
// Assert
// economy answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[0],
expectedModifiedAnswers[packageNameKey].tierOne
);
// standard answer should not be created
expect(wrapper.vm.servicePackageAnswers.length).toBe(2);
// premium answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[1],
expectedModifiedAnswers[packageNameKey].tierThree
);
});
});
describe("service-package-question.vue, matching business rules for package display", () => {
// mock scenarios in figma:
// https://www.figma.com/file/Spt9hBtGj8r8PFFGNIN3G5/New-Funnel?node-id=100%3A11383
beforeEach(async () => {
processedCmsContent = inputQuestionWidgetAnswers;
mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
return processedCmsContent[widgetName][cmsFieldName];
}),
},
};
mockProps = {
modelValue: "initialValue",
isInsuranceSelected: false,
insuranceCmsWidgetName: "InsuranceServicePackageQuestionWidget",
cashCmsWidgetName: "CashServicePackageQuestionWidget",
availableLineItems: [
{
partNumber: "001",
Description: "Windshield with Recal",
partType: "Windshield",
Quantity: "1",
BasePartNumber: "001",
Color: "Green",
CanSafeliteRecalibrate: true,
price: 350.22,
},
{
partNumber: "RAIN DEFENSE",
description: null,
partType: "RAIN DEFENSE",
price: 35.5,
},
],
};
});
it("should match the 05_01_CSR_Quote_Standard_Repair mock", () => {
// Arrange
const packageNameKey = "05_01_CSR_Quote_Standard_Repair";
Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]);
mockProps.availableLineItems.push(driverFrontWiperLineItem);
mockProps.availableLineItems.push(passengerFrontWiperLineItem);
const wrapper = setupMocks({
mountOptionsMockData: {
store: {
getters: {
order: {
damage: {
isRepair: true,
glassToReplace: [],
},
},
hasAnyNonWindshieldGlassParts: false,
},
},
},
});
// Assert
// economy answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[0],
expectedModifiedAnswers[packageNameKey].tierOne
);
// standard answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[1],
expectedModifiedAnswers[packageNameKey].tierTwo
);
// premium answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[2],
expectedModifiedAnswers[packageNameKey].tierThree
);
});
it("should match the 05_01_CSR_Quote_Recal mock", () => {
// Arrange
const packageNameKey = "05_01_CSR_Quote_Recal";
mockProps.availableLineItems.push(recalLineItem);
mockProps.availableLineItems.push(driverFrontWiperLineItem);
mockProps.availableLineItems.push(passengerFrontWiperLineItem);
Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]);
const wrapper = setupMocks({
mountOptionsMockData: {
store: {
getters: {
order: {
damage: {
isRepair: false,
glassToReplace: [{ glassLocation: "Windshield" }],
},
},
hasAnyNonWindshieldGlassParts: false,
},
},
},
});
// Assert
// economy answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[0],
expectedModifiedAnswers[packageNameKey].tierOne
);
// standard answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[1],
expectedModifiedAnswers[packageNameKey].tierTwo
);
// premium answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[2],
expectedModifiedAnswers[packageNameKey].tierThree
);
});
it("should match the 05_01_CSR_Quote_RearGlass mock", () => {
// Arrange
const packageNameKey = "05_01_CSR_Quote_RearGlass";
Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]);
mockProps.availableLineItems.push(driverFrontWiperLineItem);
mockProps.availableLineItems.push(passengerFrontWiperLineItem);
mockProps.availableLineItems.push(rearWiperLineItem);
const wrapper = setupMocks({
mountOptionsMockData: {
store: {
getters: {
order: {
damage: {
isRepair: false,
glassToReplace: [{ glassLocation: "Rear" }],
},
},
hasAnyNonWindshieldGlassParts: true,
},
},
},
});
// Assert
// economy answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[0],
expectedModifiedAnswers[packageNameKey].tierOne
);
// standard answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[1],
expectedModifiedAnswers[packageNameKey].tierTwo
);
// premium answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[2],
expectedModifiedAnswers[packageNameKey].tierThree
);
});
it("should match the 05_01_CSR_Quote_RearGlass+NonWindshield mock", () => {
// Arrange
const packageNameKey = "05_01_CSR_Quote_RearGlass+NonWindshield";
Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]);
mockProps.availableLineItems.push(driverFrontWiperLineItem);
mockProps.availableLineItems.push(passengerFrontWiperLineItem);
mockProps.availableLineItems.push(rearWiperLineItem);
const wrapper = setupMocks({
mountOptionsMockData: {
store: {
getters: {
order: {
damage: {
isRepair: false,
glassToReplace: [
{ glassLocation: "Rear" },
{ glassLocation: "Driver" },
],
},
},
hasAnyNonWindshieldGlassParts: true,
},
},
},
});
//console.log(wrapper.vm.servicePackageAnswers);
// Assert
// economy answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[0],
expectedModifiedAnswers[packageNameKey].tierOne
);
// standard answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[1],
expectedModifiedAnswers[packageNameKey].tierTwo
);
// premium answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[2],
expectedModifiedAnswers[packageNameKey].tierThree
);
});
it("should match the 05_01_CSR_Quote_RearGlass+Windshield mock", () => {
// Arrange
const packageNameKey = "05_01_CSR_Quote_RearGlass+Windshield";
Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]);
mockProps.availableLineItems.push(driverFrontWiperLineItem);
mockProps.availableLineItems.push(passengerFrontWiperLineItem);
mockProps.availableLineItems.push(rearWiperLineItem);
const wrapper = setupMocks({
mountOptionsMockData: {
store: {
getters: {
order: {
damage: {
isRepair: false,
glassToReplace: [
{ glassLocation: "Rear" },
{ glassLocation: "Windshield" },
],
},
},
hasAnyNonWindshieldGlassParts: true,
},
},
},
});
// Assert
// economy answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[0],
expectedModifiedAnswers[packageNameKey].tierOne
);
// standard answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[1],
expectedModifiedAnswers[packageNameKey].tierTwo
);
// premium answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[2],
expectedModifiedAnswers[packageNameKey].tierThree
);
});
it("should match the 05_01_CSR_Quote_RearGlassNoFrontFit mock", () => {
// Arrange
const packageNameKey = "05_01_CSR_Quote_RearGlassNoFrontFit";
Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]);
mockProps.availableLineItems.push(rearWiperLineItem);
const wrapper = setupMocks({
mountOptionsMockData: {
store: {
getters: {
order: {
damage: {
isRepair: false,
glassToReplace: [{ glassLocation: "Rear" }],
},
},
hasAnyNonWindshieldGlassParts: true,
},
},
},
});
// Assert
// economy answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[0],
expectedModifiedAnswers[packageNameKey].tierOne
);
// standard answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[1],
expectedModifiedAnswers[packageNameKey].tierTwo
);
// premium answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[2],
expectedModifiedAnswers[packageNameKey].tierThree
);
});
it("should match the 05_01_CSR_Quote_Windshield+SideGlass mock", () => {
// Arrange
const packageNameKey = "05_01_CSR_Quote_Windshield+SideGlass";
Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]);
mockProps.availableLineItems.push(driverFrontWiperLineItem);
mockProps.availableLineItems.push(passengerFrontWiperLineItem);
mockProps.availableLineItems.push(rearWiperLineItem);
const wrapper = setupMocks({
mountOptionsMockData: {
store: {
getters: {
order: {
damage: {
isRepair: false,
glassToReplace: [
{ glassLocation: "Driver" },
{ glassLocation: "Windshield" },
],
},
},
hasAnyNonWindshieldGlassParts: true,
},
},
},
});
// Assert
// economy answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[0],
expectedModifiedAnswers[packageNameKey].tierOne
);
// standard answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[1],
expectedModifiedAnswers[packageNameKey].tierTwo
);
// premium answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[2],
expectedModifiedAnswers[packageNameKey].tierThree
);
});
it("should match the 05_01_CSR_Quote_SideGlass mock", () => {
// Arrange
const packageNameKey = "05_01_CSR_Quote_SideGlass";
Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]);
mockProps.availableLineItems.push(driverFrontWiperLineItem);
mockProps.availableLineItems.push(passengerFrontWiperLineItem);
mockProps.availableLineItems.push(rearWiperLineItem);
const wrapper = setupMocks({
mountOptionsMockData: {
store: {
getters: {
order: {
damage: {
isRepair: false,
glassToReplace: [{ glassLocation: "Driver" }],
},
},
hasAnyNonWindshieldGlassParts: true,
},
},
},
});
// Assert
// economy answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[0],
expectedModifiedAnswers[packageNameKey].tierOne
);
// standard answer should not be created
expect(wrapper.vm.servicePackageAnswers.length).toBe(2);
// premium answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[1],
expectedModifiedAnswers[packageNameKey].tierThree
);
});
it("should match the 05_01_CSR_Quote_NoWiperFit mock", () => {
// Arrange
const packageNameKey = "05_01_CSR_Quote_NoWiperFit";
Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]);
const wrapper = setupMocks({
mountOptionsMockData: {
store: {
getters: {
order: {
damage: {
isRepair: false,
glassToReplace: [{ glassLocation: "Windshield" }],
},
},
hasAnyNonWindshieldGlassParts: false,
},
},
},
});
// Assert
// economy answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[0],
expectedModifiedAnswers[packageNameKey].tierOne
);
// standard answer should not be created
expect(wrapper.vm.servicePackageAnswers.length).toBe(2);
// premium answer
runPackageAnswerExpectStatements(
wrapper.vm.servicePackageAnswers[1],
expectedModifiedAnswers[packageNameKey].tierThree
);
});
});
function runPackageAnswerExpectStatements(servicePackageAnswer, expectedOutput) {
expect(servicePackageAnswer.buttonLabel).toBe(expectedOutput.buttonLabel);
expect(servicePackageAnswer.buttonLabelSubCopy).toBe(expectedOutput.buttonLabelSubCopy);
expect(servicePackageAnswer.buttonBodyCopy).toBe(expectedOutput.buttonBodyCopy);
expect(servicePackageAnswer.buttonFooterCopy).toBe(expectedOutput.buttonFooterCopy);
}
function setupMocks({ mountOptionsMockData, props = mockProps }) {
var mountOptionsMockDataDefault = {
store: {
getters: {
order: {
damage: {
isRepair: false,
glassToReplace: [{ glassLocation: "Windshield" }],
},
},
hasAnyNonWindshieldGlassParts: false,
},
},
};
mountOptionsMockData = Object.assign(mountOptionsMockDataDefault, mountOptionsMockData);
const mountOptions = getMountOptions(mountOptionsMockData);
// Modify/augment default mount options
mountOptions.global.mixins = [mockMixin];
mountOptions.propsData = props;
const wrapper = shallowMount(servicePackageQuestion, mountOptions);
return wrapper;
}
///////////////
// Constants //
///////////////
let processedCmsContent;
let mockMixin;
let mockProps;
let recalLineItem = {
partNumber: "RECAL STATIC",
Description: "Recalibration",
partType: "recalibration",
Quantity: "1",
price: 150.0,
};
let driverFrontWiperLineItem = {
partNumber: "SBB16",
description: "SAFELITE BEAM BLADE 16",
partType: "FRONT WIPER",
price: 32.64,
};
let passengerFrontWiperLineItem = {
partNumber: "SBB26",
description: "SAFELITE BEAM BLADE 26",
partType: "FRONT WIPER",
price: 53.04,
};
let rearWiperLineItem = {
partNumber: "SBBR12A",
description: "SAFELITE REAR BLADE 12A",
partType: "REAR WIPER",
price: 24.48,
};

View file

@ -0,0 +1,134 @@
import { mount } from "@vue/test-utils";
import servicePackageRadio from "./service-package-radio";
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
describe("service-package-radio.vue", () => {
it("Should include buttonLabel in html", async () => {
// Arrange
let { wrapper } = setupMocks({
mountOptionsMockData: {
propsData: mockProps,
},
});
// Act
const outputHtml = wrapper.html();
// Assert
expect(outputHtml).toEqual(expect.stringContaining(mockProps["buttonLabel"]));
});
it("Should include buttonLabelAuxillaryCopy in html", async () => {
// Arrange
let { wrapper } = setupMocks({
mountOptionsMockData: {
propsData: mockProps,
},
});
// Act
const outputHtml = wrapper.html();
// Assert
expect(outputHtml).toEqual(expect.stringContaining(mockProps["buttonLabelAuxillaryCopy"]));
});
it("Should include buttonLabelSubCopy in html", async () => {
// Arrange
let { wrapper } = setupMocks({
mountOptionsMockData: {
propsData: mockProps,
},
});
// Act
const outputHtml = wrapper.html();
// Assert
expect(outputHtml).toEqual(expect.stringContaining(mockProps["buttonLabelSubCopy"]));
});
it("Should include buttonFooterCopy in html", async () => {
// Arrange
let { wrapper } = setupMocks({
mountOptionsMockData: {
propsData: mockProps,
},
});
// Act
const outputHtml = wrapper.html();
// Assert
expect(outputHtml).toEqual(expect.stringContaining(mockProps["buttonFooterCopy"]));
});
it("Should get 5 strings from getArrayOfListItemsFromRawCmsCopy when provided with a <ul><li>...</li>(x5)</ul> buttonBodyCopy", async () => {
// Arrange
let { wrapper } = setupMocks({
mountOptionsMockData: {
propsData: mockProps,
},
});
// Act
const results = wrapper.vm.getArrayOfListItemsFromRawCmsCopy(wrapper.vm.buttonBodyCopy);
// Assert
expect(results.length).toBe(5);
});
it("Should get strings from getArrayOfListItemsFromRawCmsCopy without <ul> or <li> tags when provided with a <ul><li>...</li></ul> buttonBodyCopy", async () => {
// Arrange
let { wrapper } = setupMocks({
mountOptionsMockData: {
propsData: mockProps,
},
});
// Act
const results = wrapper.vm.getArrayOfListItemsFromRawCmsCopy(wrapper.vm.buttonBodyCopy);
// Assert
const fileredResults = results.filter((result) => {
return (
result.includes("<ul>") ||
result.includes("</ul>") ||
result.includes("<li>") ||
result.includes("</li>")
);
});
expect(fileredResults.length).toBe(0);
});
it("Should get 5 strings from getArrayOfListItemsFromRawCmsCopy when buttonBodyCopy has 6 total <li>...</li>, but one is empty ", async () => {
// Arrange
const moddedProps = mockProps;
moddedProps["buttonBodyCopy"] =
"<ul><li>buttonBodyCopy test copy</li><li>2</li><li>3</li><li>4</li><li>5</li><li></li></ul>";
let { wrapper } = setupMocks({
mountOptionsMockData: {
propsData: moddedProps,
},
});
// Act
const results = wrapper.vm.getArrayOfListItemsFromRawCmsCopy(wrapper.vm.buttonBodyCopy);
// Assert
expect(results.length).toBe(5);
});
});
const mockProps = {
buttonLabel: "buttonLabel test copy",
buttonLabelAuxillaryCopy: "buttonLabelAuxillaryCopy test copy",
buttonLabelSubCopy: "buttonLabelSubCopy test copy",
buttonBodyCopy:
"<ul><li>buttonBodyCopy test copy</li><li>2</li><li>3</li><li>4</li><li>5</li></ul>",
buttonFooterCopy: "buttonFooterCopy test copy",
};
function setupMocks({ mountOptionsMockData = {} }) {
const wrapper = mount(servicePackageRadio, {
...mountOptionsMockData,
mixins: [inputButtonWrapperMixin],
});
return { wrapper };
}

View file

@ -232,6 +232,9 @@ describe("vehicle-parts.vue", () => {
lineItems: { lineItems: {
glassParts: null, glassParts: null,
}, },
vehicle: {
vin: "MY_VIN",
},
}, },
}, },
}, },
@ -275,6 +278,9 @@ describe("vehicle-parts.vue", () => {
lineItems: { lineItems: {
glassParts: null, glassParts: null,
}, },
vehicle: {
vin: "MY_VIN",
},
}, },
}, },
}, },
@ -292,7 +298,7 @@ describe("vehicle-parts.vue", () => {
//Assert //Assert
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith( expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(
navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, navigationScenarios.CLICKED_BACK_WITH_VIN_AND_NO_MORE_QUESTIONS,
wrapper.vm.$route wrapper.vm.$route
); );
}); });
@ -467,12 +473,10 @@ describe("vehicle-parts.vue", () => {
getters: store.getters, getters: store.getters,
commit: store.commit, commit: store.commit,
}, },
navigateToHeritageFunnel: jest.fn(),
}, },
}); });
wrapper.vm.$store.commit = jest.fn(); wrapper.vm.$store.commit = jest.fn();
wrapper.vm.$refs.loadingModal.showModal = jest.fn();
wrapper.setData({ wrapper.setData({
selectedGlassParts: { "Rear-Stationary": { partNumber: "DB12209GTYN" } }, selectedGlassParts: { "Rear-Stationary": { partNumber: "DB12209GTYN" } },
@ -490,7 +494,12 @@ describe("vehicle-parts.vue", () => {
//Assert //Assert
expect(wrapper.vm.$store.commit).toHaveBeenCalled(); expect(wrapper.vm.$store.commit).toHaveBeenCalled();
expect(navigateToHeritageFunnel).toHaveBeenCalled();
// TODO KO UNCOMMENT FOR QUOTE PAGES RELEASE
// expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(
// navigationScenarios.HAS_NO_MORE_QUESTIONS,
// { query: { fmgPage: "vehicle-parts" } }
// );
}); });
}); });
@ -529,7 +538,6 @@ function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {}
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn();
wrapper.vm.$refs.loadingModal.showModal = jest.fn();
// wrapper.vm.$refs.onSubmit = jest.fn(); // wrapper.vm.$refs.onSubmit = jest.fn();
// wrapper.vm.$refs.onInvalidSubmit = jest.fn(); // wrapper.vm.$refs.onInvalidSubmit = jest.fn();

View file

@ -1,7 +1,6 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
<div class="page-container-grouped-styles vehicle-parts"> <div class="page-container-grouped-styles vehicle-parts">
<loadingModal ref="loadingModal" />
<funnelHeader ref="funnelHeader" cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader ref="funnelHeader" cmsWidgetName="FunnelHeaderWidget" />
<vehicleBanner <vehicleBanner
ref="vehicleBanner" ref="vehicleBanner"
@ -35,6 +34,7 @@
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter" ref="funnelFooter"
:isForwardActionDisabled="isForwardActionDisabled" :isForwardActionDisabled="isForwardActionDisabled"
@tempButtonClicked="() => handleTempButtonClicked(this)"
@back-clicked="backButtonAction" @back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" /> @ForwardClicked="forwardButtonAction" />
</div> </div>
@ -50,7 +50,6 @@ import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import funnelFooter from "@/common-components/funnel-footer/funnel-footer"; import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
import alert from "@/ux-components/alert/alert"; import alert from "@/ux-components/alert/alert";
import loadingModal from "@/common-components/loading-modal/loading-modal.vue";
// Supporting Files // Supporting Files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper"; import { settleAllPromises } from "@/helpers/layout-helper";
@ -188,8 +187,10 @@ export default {
false false
); );
// TODO KO delete after quote MVP
this.navigateForward(matchedParts, null, this.shouldGoToHeritageQuote);
// Navigate to the next page // Navigate to the next page
this.navigateForward(matchedParts); // this.navigateForward(matchedParts);
}, },
LoadInitialPartsData() { LoadInitialPartsData() {
@ -223,7 +224,6 @@ export default {
funnelSubHeader, funnelSubHeader,
funnelFooter, funnelFooter,
alert, alert,
loadingModal,
}, },
}; };
</script> </script>

View file

@ -1,7 +1,6 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<loadingModal ref="loadingModal" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> <funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<vehicleBanner <vehicleBanner
cmsWidgetName="VehicleBannerWidget" cmsWidgetName="VehicleBannerWidget"
@ -104,6 +103,7 @@
cmsWidgetName="FunnelFooterWidget" cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter" ref="funnelFooter"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid"
@tempButtonClicked="() => handleTempButtonClicked(this)"
@back-clicked="backButtonAction" @back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" /> @ForwardClicked="forwardButtonAction" />
</div> </div>
@ -120,7 +120,6 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he
import alert from "@/ux-components/alert/alert"; import alert from "@/ux-components/alert/alert";
import textboxQuestion from "@/common-components/textbox-question/textbox-question"; import textboxQuestion from "@/common-components/textbox-question/textbox-question";
import vinInformation from "@/layouts/vin-lookup/vin-information/vin-information"; import vinInformation from "@/layouts/vin-lookup/vin-information/vin-information";
import loadingModal from "@/common-components/loading-modal/loading-modal.vue";
import textBlock from "@/common-components/text-block/text-block"; import textBlock from "@/common-components/text-block/text-block";
// Supporting files // Supporting files
@ -463,7 +462,6 @@ export default {
textboxQuestion, textboxQuestion,
alert, alert,
vinInformation, vinInformation,
loadingModal,
textBlock, textBlock,
Form, Form,
}, },

View file

@ -7,6 +7,12 @@ import baseMixin from "@/mixins/base-mixin.js";
import store from "@/store"; import store from "@/store";
export default { export default {
data() {
// TODO KO DELETE AFTER QUOTE MVP
return {
shouldGoToHeritageQuote: false,
};
},
methods: { methods: {
hasPartQuestions(partsOrQuestions) { hasPartQuestions(partsOrQuestions) {
return partsOrQuestions?.some((pq) => pq.partQuestions?.length > 0); return partsOrQuestions?.some((pq) => pq.partQuestions?.length > 0);
@ -355,8 +361,9 @@ export default {
} }
} }
}, },
// TODO KO Delete `shouldGoToHeritageQuote`
// Can't use `this` because navigateForward is also called from vin-pages-mixin // Can't use `this` because navigateForward is also called from vin-pages-mixin
async navigateForward(partsOrQuestions, vm) { async navigateForward(partsOrQuestions, vm, shouldGoToHeritageQuote) {
const self = vm ?? this; const self = vm ?? this;
const currentPage = self.$route.query.fmgPage; const currentPage = self.$route.query.fmgPage;
@ -450,28 +457,33 @@ export default {
// save to store lineItems.glassParts // save to store lineItems.glassParts
self.$store.commit(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts); self.$store.commit(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts);
if (self.$refs.questionsPage) { shouldGoToHeritageQuote
self.$refs.questionsPage.showLoadingModal(); ? navigateToHeritageFunnel()
} else if (self.$refs.loadingModal) { : self.$router.navigateWithSaving(
self.$refs.loadingModal.showModal(); self.navigationScenarios.HAS_NO_MORE_QUESTIONS,
} self.$route
);
navigateToHeritageFunnel();
} }
}, },
backButtonAction() { // Can't use `this` because navigateForward is also called from quote
backButtonAction(vm) {
const self = vm ?? this;
const partsOrQuestions = ( const partsOrQuestions = (
this.$store.getters.pageData(fmgPageValues.PART_QUESTIONS) ?? self.$store.getters.pageData(fmgPageValues.PART_QUESTIONS) ??
this.$store.getters.pageData(fmgPageValues.VEHICLE_PARTS) self.$store.getters.pageData(fmgPageValues.VEHICLE_PARTS) ??
self.$store.getters.pageData(fmgPageValues.MOLDING_QUESTIONS) ??
self.$store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS)
)?.partsOrQuestions; )?.partsOrQuestions;
const hasPartQuestions = this.hasPartQuestions(partsOrQuestions); const hasPartQuestions = this.hasPartQuestions(partsOrQuestions);
const hasGlassLocationWithMultipleParts = const hasGlassLocationWithMultipleParts =
this.hasGlassLocationWithMultipleParts(partsOrQuestions); this.hasGlassLocationWithMultipleParts(partsOrQuestions);
const hasChildPartQuestions = this.hasChildPartQuestions(partsOrQuestions); const hasChildPartQuestions = this.hasChildPartQuestions(partsOrQuestions);
const hasCapabilityQuestions = this.hasCapabilityQuestions(partsOrQuestions); const hasCapabilityQuestions = this.hasCapabilityQuestions(partsOrQuestions);
let backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS; let backNavigationScenario = self.$store.getters.vehicle.vin
? navigationScenarios.CLICKED_BACK_WITH_VIN_AND_NO_MORE_QUESTIONS
: navigationScenarios.CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS;
const currentPage = this.$route.query.fmgPage; const currentPage = self.$route.query.fmgPage;
if ( if (
hasCapabilityQuestions && hasCapabilityQuestions &&
this.currentPageComesAfterPage(currentPage, fmgPageValues.CAPABILITY_QUESTIONS) this.currentPageComesAfterPage(currentPage, fmgPageValues.CAPABILITY_QUESTIONS)
@ -495,7 +507,11 @@ export default {
backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS; backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS;
} }
this.$router.navigateWithoutSaving(backNavigationScenario, this.$route); self.$router.navigateWithoutSaving(backNavigationScenario, self.$route);
},
// TODO KO delete this after quote mvp
async handleTempButtonClicked(vm) {
this.shouldGoToHeritageQuote = true;
}, },
}, },
}; };

View file

@ -4,8 +4,6 @@ import { setupMocksForJsFiles, getMountOptions } from "@/helpers/unit-test-helpe
import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { storeMutations } from "@/constants/store-mutations"; import { storeMutations } from "@/constants/store-mutations";
import { storeActions } from "@/constants/store-actions"; import { storeActions } from "@/constants/store-actions";
import loadingModal from "@/common-components/loading-modal/loading-modal.vue";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import { navigationScenarios } from "../router/router-constants/navigation-scenarios"; import { navigationScenarios } from "../router/router-constants/navigation-scenarios";
import { getters } from "@/store"; import { getters } from "@/store";
@ -2088,7 +2086,8 @@ describe("vehicle-questions-mixin", () => {
}); });
}); });
describe("should go to heritage funnel", () => { // TODO KO UNSKIP FOR QUOTE MVP
describe.skip("should go to quote page", () => {
test("single glass location selected, has no part questions and has one part => go to heritage funnel", async () => { test("single glass location selected, has no part questions and has one part => go to heritage funnel", async () => {
// Arrange // Arrange
const partsOrQuestions = [ const partsOrQuestions = [
@ -2123,7 +2122,12 @@ describe("vehicle-questions-mixin", () => {
await wrapper.vm.navigateForward(partsOrQuestions); await wrapper.vm.navigateForward(partsOrQuestions);
// Assert // Assert
expect(navigateToHeritageFunnel).toHaveBeenCalledTimes(1); expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(
navigationScenarios.HAS_NO_MORE_QUESTIONS,
{ query: { fmgPage: "vin-lookup" } }
);
expect(wrapper.vm.$router.navigateWithoutSaving).not.toHaveBeenCalled();
}); });
test("multiple glass locations selected, each has one part and no part questions => go to heritage funnel", async () => { test("multiple glass locations selected, each has one part and no part questions => go to heritage funnel", async () => {
@ -2229,23 +2233,32 @@ describe("vehicle-questions-mixin", () => {
storeMutations.UPDATE_GLASS_PARTS, storeMutations.UPDATE_GLASS_PARTS,
collectedGlassParts collectedGlassParts
); );
expect(wrapper.vm.$refs.loadingModal.showModal).toHaveBeenCalledTimes(1); expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledTimes(1);
expect(navigateToHeritageFunnel).toHaveBeenCalledTimes(1); expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalledWith(
navigationScenarios.HAS_NO_MORE_QUESTIONS,
{ query: { fmgPage: "vin-lookup" } }
);
expect(wrapper.vm.$router.navigateWithoutSaving).not.toHaveBeenCalled();
}); });
}); });
}); });
describe("backButtonAction", () => { describe("backButtonAction", () => {
test("current page is quote and there are no questions => go to vin-lookup", () => { // TODO KO
test.todo(
"current page is quote, there are no questions, and we don't have their vin => go to estimate"
);
test("current page is quote, there are no questions, and we have their vin => go to vin-lookup", () => {
// Arrange // Arrange
const { wrapper } = setupMocks({ fmgPage: fmgPageValues.QUOTE }); const { wrapper } = setupMocks({ fmgPage: fmgPageValues.QUOTE, hasVin: true });
// Act // Act
wrapper.vm.backButtonAction(); wrapper.vm.backButtonAction();
// Assert // Assert
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith( expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(
navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, navigationScenarios.CLICKED_BACK_WITH_VIN_AND_NO_MORE_QUESTIONS,
{ query: { fmgPage: fmgPageValues.QUOTE } } { query: { fmgPage: fmgPageValues.QUOTE } }
); );
}); });
@ -2319,7 +2332,7 @@ describe("vehicle-questions-mixin", () => {
}); });
}); });
function setupMocks({ fmgPage = fmgPageValues.VIN_LOOKUP }) { function setupMocks({ fmgPage = fmgPageValues.VIN_LOOKUP, hasVin, carId }) {
const baseMixin = setupMocksForJsFiles({ const baseMixin = setupMocksForJsFiles({
actionList: [ actionList: [
{ {
@ -2346,7 +2359,13 @@ function setupMocks({ fmgPage = fmgPageValues.VIN_LOOKUP }) {
store: { store: {
commit: jest.fn(), commit: jest.fn(),
dispatch: jest.fn(), dispatch: jest.fn(),
getters: getters, getters: {
...getters,
vehicle: {
vin: hasVin ? "VIN" : undefined,
carId: carId,
},
},
}, },
route: { route: {
query: { query: {
@ -2358,14 +2377,11 @@ function setupMocks({ fmgPage = fmgPageValues.VIN_LOOKUP }) {
// store.dispatch = jest.fn(); // store.dispatch = jest.fn();
const mockVehicleQuestionComponent = { const mockVehicleQuestionComponent = {
components: { loadingModal }, template: "<div />",
template: '<loadingModal ref="loadingModal" />',
mixins: [vehicleQuestionsMixin, baseMixin.baseMixin], mixins: [vehicleQuestionsMixin, baseMixin.baseMixin],
}; };
const wrapper = shallowMount(mockVehicleQuestionComponent, mocks); const wrapper = shallowMount(mockVehicleQuestionComponent, mocks);
wrapper.vm.$refs.loadingModal.showModal = jest.fn();
return { wrapper }; return { wrapper };
} }

View file

@ -4,6 +4,12 @@ import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
import { saveSession } from "@/helpers/heritage-integration/order-helper.js"; import { saveSession } from "@/helpers/heritage-integration/order-helper.js";
export default { export default {
data() {
// TODO KO DELETE AFTER QUOTE MVP
return {
shouldGoToHeritageQuote: false,
};
},
methods: { methods: {
async navigateForwardWithSingleCarMatch() { async navigateForwardWithSingleCarMatch() {
// If we have not already saved a session, we need to save one now before the lengthy call to getPartsOrQuestions // If we have not already saved a session, we need to save one now before the lengthy call to getPartsOrQuestions
@ -14,7 +20,17 @@ export default {
const result = await this.dispatchStoreAction(storeActions.GET_PARTS_OR_QUESTIONS); const result = await this.dispatchStoreAction(storeActions.GET_PARTS_OR_QUESTIONS);
const partsOrQuestions = result.data.partsOrQuestions; const partsOrQuestions = result.data.partsOrQuestions;
vehicleQuestionsMixin.methods.navigateForward(partsOrQuestions, this); // TODO KO delete `this.shouldGoToHeritageQuote`
vehicleQuestionsMixin.methods.navigateForward(
partsOrQuestions,
this,
this.shouldGoToHeritageQuote
);
},
// TODO KO delete this after quote mvp
async handleTempButtonClicked(vm) {
this.shouldGoToHeritageQuote = true;
await vm.forwardButtonAction();
}, },
}, },
}; };

View file

@ -2,7 +2,6 @@ import vinPagesMixin from "@/mixins/vin-pages-mixin";
import { shallowMount } from "@vue/test-utils"; import { shallowMount } from "@vue/test-utils";
import { setupMocksForJsFiles, getMountOptions } from "@/helpers/unit-test-helper.js"; import { setupMocksForJsFiles, getMountOptions } from "@/helpers/unit-test-helper.js";
import { storeActions } from "@/constants/store-actions"; import { storeActions } from "@/constants/store-actions";
import loadingModal from "@/common-components/loading-modal/loading-modal.vue";
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin"; import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({ jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({
@ -62,14 +61,10 @@ function setupMocks({ partsOrQuestions = [] }) {
}); });
const mockVinComponent = { const mockVinComponent = {
components: { loadingModal },
template: '<loadingModal ref="loadingModal" />',
mixins: [vinPagesMixin, baseMixin.baseMixin], mixins: [vinPagesMixin, baseMixin.baseMixin],
}; };
const wrapper = shallowMount(mockVinComponent, mocks); const wrapper = shallowMount(mockVinComponent, mocks);
wrapper.vm.$refs.loadingModal.showModal = jest.fn();
return { wrapper }; return { wrapper };
} }

View file

@ -18,6 +18,7 @@ const navigationScenarios = {
SELECTED_MANUAL_VIN: "SELECTED_MANUAL_VIN", SELECTED_MANUAL_VIN: "SELECTED_MANUAL_VIN",
SELECTED_LICENSE_PLATE: "SELECTED_LICENSE_PLATE", SELECTED_LICENSE_PLATE: "SELECTED_LICENSE_PLATE",
SELECTED_HOME_ADDRESS: "SELECTED_HOME_ADDRESS", SELECTED_HOME_ADDRESS: "SELECTED_HOME_ADDRESS",
HAS_NO_QUESTIONS: "HAS_NO_QUESTIONS",
// Question pages // Question pages
HAS_PART_QUESTIONS: "HAS_PART_QUESTIONS", HAS_PART_QUESTIONS: "HAS_PART_QUESTIONS",
@ -25,10 +26,14 @@ const navigationScenarios = {
HAS_MOLDING_QUESTIONS: "HAS_MOLDING_QUESTIONS", HAS_MOLDING_QUESTIONS: "HAS_MOLDING_QUESTIONS",
HAS_CAPABILITY_QUESTIONS: "HAS_CAPABILITY_QUESTIONS", HAS_CAPABILITY_QUESTIONS: "HAS_CAPABILITY_QUESTIONS",
HAS_NO_MORE_QUESTIONS: "HAS_NO_MORE_QUESTIONS", HAS_NO_MORE_QUESTIONS: "HAS_NO_MORE_QUESTIONS",
CLICKED_BACK_WITH_NO_QUESTIONS: "CLICKED_BACK_WITH_NO_QUESTIONS",
CLICKED_BACK_WITH_PART_QUESTIONS: "CLICKED_BACK_WITH_PART_QUESTIONS", CLICKED_BACK_WITH_PART_QUESTIONS: "CLICKED_BACK_WITH_PART_QUESTIONS",
CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE: "CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE", CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE: "CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE",
CLICKED_BACK_WITH_MOLDING_QUESTIONS: "CLICKED_BACK_WITH_MOLDING_QUESTIONS", CLICKED_BACK_WITH_MOLDING_QUESTIONS: "CLICKED_BACK_WITH_MOLDING_QUESTIONS",
CLICKED_BACK_WITH_CAPABILITY_QUESTIONS: "CLICKED_BACK_WITH_CAPABILITY_QUESTIONS",
// Quote
CLICKED_BACK_WITH_VIN_AND_NO_MORE_QUESTIONS: "CLICKED_BACK_WITH_VIN_AND_NO_MORE_QUESTIONS",
CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS: "CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS",
}; };
export { navigationScenarios }; export { navigationScenarios };

View file

@ -109,6 +109,10 @@ const routingTable = function (store) {
scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS,
destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS,
}, },
{
scenario: navigationScenarios.HAS_NO_MORE_QUESTIONS,
destinationFmgPageValue: fmgPageValues.QUOTE,
},
], ],
}, },
{ {
@ -138,6 +142,10 @@ const routingTable = function (store) {
scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS,
destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS,
}, },
{
scenario: navigationScenarios.HAS_NO_MORE_QUESTIONS,
destinationFmgPageValue: fmgPageValues.QUOTE,
},
], ],
}, },
{ {
@ -171,6 +179,10 @@ const routingTable = function (store) {
scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS,
destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS,
}, },
{
scenario: navigationScenarios.HAS_NO_MORE_QUESTIONS,
destinationFmgPageValue: fmgPageValues.QUOTE,
},
], ],
}, },
{ {
@ -200,6 +212,10 @@ const routingTable = function (store) {
scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS, scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS,
destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS,
}, },
{
scenario: navigationScenarios.HAS_NO_MORE_QUESTIONS,
destinationFmgPageValue: fmgPageValues.QUOTE,
},
], ],
}, },
{ {
@ -221,15 +237,23 @@ const routingTable = function (store) {
scenario: navigationScenarios.SELECTED_HOME_ADDRESS, scenario: navigationScenarios.SELECTED_HOME_ADDRESS,
destinationFmgPageValue: fmgPageValues.ADDRESS_LOOKUP, destinationFmgPageValue: fmgPageValues.ADDRESS_LOOKUP,
}, },
{
scenario: navigationScenarios.HAS_NO_QUESTIONS,
destinationFmgPageValue: fmgPageValues.QUOTE,
},
], ],
}, },
{ {
fmgPageValue: fmgPageValues.PART_QUESTIONS, fmgPageValue: fmgPageValues.PART_QUESTIONS,
maps: [ maps: [
{ {
scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, scenario: navigationScenarios.CLICKED_BACK_WITH_VIN_AND_NO_MORE_QUESTIONS,
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP, destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
}, },
{
scenario: navigationScenarios.CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS,
destinationFmgPageValue: fmgPageValues.ESTIMATE,
},
{ {
scenario: navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE, scenario: navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE,
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS, destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS,
@ -260,9 +284,13 @@ const routingTable = function (store) {
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS, destinationFmgPageValue: fmgPageValues.PART_QUESTIONS,
}, },
{ {
scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, scenario: navigationScenarios.CLICKED_BACK_WITH_VIN_AND_NO_MORE_QUESTIONS,
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP, destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
}, },
{
scenario: navigationScenarios.CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS,
destinationFmgPageValue: fmgPageValues.ESTIMATE,
},
{ {
scenario: navigationScenarios.HAS_MOLDING_QUESTIONS, scenario: navigationScenarios.HAS_MOLDING_QUESTIONS,
destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS, destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS,
@ -281,9 +309,13 @@ const routingTable = function (store) {
fmgPageValue: fmgPageValues.MOLDING_QUESTIONS, fmgPageValue: fmgPageValues.MOLDING_QUESTIONS,
maps: [ maps: [
{ {
scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, scenario: navigationScenarios.CLICKED_BACK_WITH_VIN_AND_NO_MORE_QUESTIONS,
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP, destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
}, },
{
scenario: navigationScenarios.CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS,
destinationFmgPageValue: fmgPageValues.ESTIMATE,
},
{ {
scenario: navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS, scenario: navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS,
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS, destinationFmgPageValue: fmgPageValues.PART_QUESTIONS,
@ -306,9 +338,13 @@ const routingTable = function (store) {
fmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS, fmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS,
maps: [ maps: [
{ {
scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, scenario: navigationScenarios.CLICKED_BACK_WITH_VIN_AND_NO_MORE_QUESTIONS,
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP, destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
}, },
{
scenario: navigationScenarios.CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS,
destinationFmgPageValue: fmgPageValues.ESTIMATE,
},
{ {
scenario: navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS, scenario: navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS,
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS, destinationFmgPageValue: fmgPageValues.PART_QUESTIONS,
@ -327,6 +363,35 @@ const routingTable = function (store) {
}, },
], ],
}, },
{
fmgPageValue: fmgPageValues.QUOTE,
maps: [
{
scenario: navigationScenarios.CLICKED_BACK_WITH_VIN_AND_NO_MORE_QUESTIONS,
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
},
{
scenario: navigationScenarios.CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS,
destinationFmgPageValue: fmgPageValues.ESTIMATE,
},
{
scenario: navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS,
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS,
},
{
scenario: navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE,
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS,
},
{
scenario: navigationScenarios.CLICKED_BACK_WITH_MOLDING_QUESTIONS,
destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS,
},
{
scenario: navigationScenarios.CLICKED_BACK_WITH_CAPABILITY_QUESTIONS,
destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS,
},
],
},
]; ];
}; };

View file

@ -303,13 +303,13 @@ export const mutations = {
state.applicationUser.saveSessionPromise = null; state.applicationUser.saveSessionPromise = null;
}, },
// Misc Mutations // Misc Mutations
updateStateWithOrderInformation(state, orderInformation) { updateStateWithOrderInformation(state, sessionInformation) {
state.order.referralNumber = orderInformation.referralNumber; state.order.referralNumber = sessionInformation.order.referralNumber;
state.order.referralDate = orderInformation.referralDate; state.order.referralDate = sessionInformation.order.referralDate;
state.order.referralCorrelationId = orderInformation.referralCorrelationId; state.order.referralCorrelationId = sessionInformation.order.referralCorrelationId;
state.order.eon = orderInformation.eon; state.order.eon = sessionInformation.order.eon;
if (state.order.vehicle.vin !== orderInformation.vehicle?.vin) { if (state.order.vehicle.vin !== sessionInformation.order.vehicle?.vin) {
state.applicationUser.pageData[fmgPageValues.PART_QUESTIONS] = null; state.applicationUser.pageData[fmgPageValues.PART_QUESTIONS] = null;
state.applicationUser.pageData[fmgPageValues.VEHICLE_PARTS] = null; state.applicationUser.pageData[fmgPageValues.VEHICLE_PARTS] = null;
state.applicationUser.pageData[fmgPageValues.MOLDING_QUESTIONS] = null; state.applicationUser.pageData[fmgPageValues.MOLDING_QUESTIONS] = null;
@ -317,44 +317,51 @@ export const mutations = {
} }
state.order.vehicle = Object.assign(state.order.vehicle, { state.order.vehicle = Object.assign(state.order.vehicle, {
year: orderInformation.vehicle?.year, year: sessionInformation.order.vehicle?.year,
make: orderInformation.vehicle?.make, make: sessionInformation.order.vehicle?.make,
model: orderInformation.vehicle?.model, model: sessionInformation.order.vehicle?.model,
style: orderInformation.vehicle?.style, style: sessionInformation.order.vehicle?.style,
vin: orderInformation.vehicle?.vin, vin: sessionInformation.order.vehicle?.vin,
carId: orderInformation.vehicle?.carId, carId: sessionInformation.order.vehicle?.carId,
category: orderInformation.vehicle?.category, category: sessionInformation.order.vehicle?.category,
imageUrl: orderInformation.vehicle?.imageUrl, imageUrl: sessionInformation.order.vehicle?.imageUrl,
imageVifNumber: orderInformation.vehicle?.imageVifNumber, imageVifNumber: sessionInformation.order.vehicle?.imageVifNumber,
imageColor: orderInformation.vehicle?.imageVifColor, imageColor: sessionInformation.order.vehicle?.imageVifColor,
registration: { registration: {
firstName: orderInformation.vehicle.registration.firstName, firstName: sessionInformation.order.vehicle.registration.firstName,
lastName: orderInformation.vehicle.registration.lastName, lastName: sessionInformation.order.vehicle.registration.lastName,
address: orderInformation.vehicle.registration.streetAddress, address: sessionInformation.order.vehicle.registration.streetAddress,
city: orderInformation.vehicle.registration.city, city: sessionInformation.order.vehicle.registration.city,
state: orderInformation.vehicle.registration.state, state: sessionInformation.order.vehicle.registration.state,
zipCode: orderInformation.vehicle.registration.zipCode, zipCode: sessionInformation.order.vehicle.registration.zipCode,
licensePlate: orderInformation.vehicle.registration.licensePlateNumber, licensePlate: sessionInformation.order.vehicle.registration.licensePlateNumber,
}, },
}); });
state.order.damage.glassToReplace = orderInformation.damage.glassToReplace; state.order.damage.glassToReplace = sessionInformation.order.damage.glassToReplace;
state.order.damage.isRepair = orderInformation.damage.isRepair; state.order.damage.isRepair = sessionInformation.order.damage.isRepair;
state.order.damage.numberOfChips = orderInformation.damage.numberOfChips; state.order.damage.numberOfChips = sessionInformation.order.damage.numberOfChips;
state.order.lineItems.glassParts = orderInformation.parts; state.order.lineItems.glassParts = sessionInformation.order.lineItems.parts;
state.order.accountNumber = orderInformation.accountNumber; state.order.accountNumber = sessionInformation.order.accountNumber;
(state.order.serviceLocation.address = orderInformation.serviceLocation.streetAddress), state.order.providerNumber = sessionInformation.order.providerNumber;
(state.order.serviceLocation.city = orderInformation.serviceLocation.city), (state.order.serviceLocation.address =
(state.order.serviceLocation.state = orderInformation.serviceLocation.state), sessionInformation.order.serviceLocation.streetAddress),
(state.order.serviceLocation.zipCode = orderInformation.serviceLocation.zipCode); (state.order.serviceLocation.city = sessionInformation.order.serviceLocation.city),
(state.order.serviceLocation.state = sessionInformation.order.serviceLocation.state),
(state.order.serviceLocation.zipCode =
sessionInformation.order.serviceLocation.zipCode);
state.order.payment.isInsurance = orderInformation.IsInsuranceOrder; state.order.payment.isInsurance = sessionInformation.order.payment.isInsurance;
state.order.payment.insuranceCoverage.isVerified = state.order.payment.insuranceCoverage.isVerified =
orderInformation?.insuranceInfo.coverageVerified; sessionInformation?.order.payment.insuranceCoverage.isVerified;
state.order.customer.emailAddress = orderInformation.customer.emailAddress; state.order.customer.emailAddress = sessionInformation.order.customer.emailAddress;
state.applicationUser.experiments = orderInformation.experiments; state.order.existingPromoCode = sessionInformation.order.existingPromoCode;
state.applicationUser.experiments = sessionInformation.applicationUser.experiments;
state.applicationUser.crmCustomerId = sessionInformation.applicationUser.crmCustomerId;
state.applicationUser.pageData = sessionInformation.applicationUser.pageData;
state.applicationUser.lastPage = sessionInformation.applicationUser.lastPage;
}, },
updateExperiments(state, experiments) { updateExperiments(state, experiments) {
state.applicationUser.experiments = experiments; state.applicationUser.experiments = experiments;
@ -362,21 +369,6 @@ export const mutations = {
updateTriggeredSiteEntry(state, wasSiteEntryTriggered) { updateTriggeredSiteEntry(state, wasSiteEntryTriggered) {
state.applicationUser.triggeredSiteEntry = wasSiteEntryTriggered; state.applicationUser.triggeredSiteEntry = wasSiteEntryTriggered;
}, },
// START GA click event mutations
// updateCurrentlySelectedValues(state, groupName, value) {
// state.gaClickInformation.currentlySelectedValues[groupName] = value;
// },
// updateFiredGaClickEventValues(state, groupName, value) {
// state.gaClickInformation.firedGaClickEventValues[groupName] = value;
// },
// updateLastFocusedInputGroup(state, groupName) {
// state.gaClickInformation.lastFocusedInputGroup = groupName;
// },
// updateWasLastFocusedInputMultiselect(state, wasLastFocusedInputMultiselect) {
// state.gaClickInformation.wasLastFocusedInputMultiselect =
// wasLastFocusedInputMultiselect;
// },
// END GA click event mutations
}; };
// Export Getters // Export Getters
@ -393,10 +385,10 @@ export const getters = {
eventBus: (state) => state.applicationUser.eventBus, eventBus: (state) => state.applicationUser.eventBus,
damage: (state) => state.order.damage, damage: (state) => state.order.damage,
hasAnyNonWindshieldGlassParts: (state) => { hasAnyNonWindshieldGlassParts: (state) => {
const nonWindshieldItems = state.order.damage.glassToReplace.filter( const nonWindshieldItems = state.order.damage.glassToReplace?.filter(
(glassToReplace) => glassToReplace.glassLocation != "Windshield" (glassToReplace) => glassToReplace.glassLocation != "Windshield"
); );
return !!nonWindshieldItems.length; return !!nonWindshieldItems?.length;
}, },
lineItems: (state) => state.order.lineItems, lineItems: (state) => state.order.lineItems,
pageData: (state) => (page) => { pageData: (state) => (page) => {
@ -465,7 +457,6 @@ export const getters = {
state.applicationUser.experiments state.applicationUser.experiments
.map((x) => x.settings) .map((x) => x.settings)
.reduce((r, c) => Object.assign(r, c), {}) ?? {}, .reduce((r, c) => Object.assign(r, c), {}) ?? {},
// gaClickInformation: (state) => state.gaClickInformation,
}; };
function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) { function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) {
@ -984,17 +975,12 @@ export const actions = {
}, },
}); });
}, },
loadSession(context, { referralNumber, referralDate, referralCorrelationId, accountNumber }) { loadSession(context, { savedSessionId }) {
return globalMethods return globalMethods
.callHttpClient({ .callHttpClient({
method: endpoints.LoadSession.method, method: endpoints.LoadSession.method,
endpoint: endpoints.LoadSession.url, endpoint:
payload: { endpoints.LoadSession.url + "?savedSessionId=" + savedSessionId?.toString(),
referralNumber: referralNumber?.toString(),
referralDate: referralDate,
referralCorrelationId: referralCorrelationId,
accountNumber: accountNumber?.toString(),
},
}) })
.then((response) => { .then((response) => {
// Flatten location and name properties // Flatten location and name properties
@ -1409,6 +1395,12 @@ function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) {
function convertGlassPieceNamingForApi(glassArray) { function convertGlassPieceNamingForApi(glassArray) {
if (!glassArray) return []; if (!glassArray) return [];
// check if array already converted. (likely when a session has been saved previously and then reloaded)
if (glassArray[0].location !== undefined) {
return glassArray;
}
const converted = []; const converted = [];
glassArray.forEach((glass) => { glassArray.forEach((glass) => {
converted.push({ converted.push({

View file

@ -203,34 +203,65 @@ describe("Mutations", () => {
expect(storeState.applicationUser.pageData["vehicle-year"]).toEqual({}); expect(storeState.applicationUser.pageData["vehicle-year"]).toEqual({});
}); });
it("updateStateWithOrderInformation, should set order information in state", () => { it("updateStateWithSessonInformation, should set session information in state", () => {
// Arrange // Arrange
const storeState = state; const storeState = state;
// Act // Act
mutations.updateStateWithOrderInformation(storeState, { mutations.updateStateWithOrderInformation(storeState, {
referralNumber: 123, order: {
referralDate: new Date().toUTCString(), referralNumber: 123,
referralCorrelationId: "xxx-xxx-xxx", referralDate: new Date().toUTCString(),
vehicle: { referralCorrelationId: "xxx-xxx-xxx",
year: "2019", vehicle: {
make: "Acura", year: "2019",
model: "ILX", make: "Acura",
style: "4 DOOR SEDAN", model: "ILX",
carId: "C0000001", style: "4 DOOR SEDAN",
category: "CAR", carId: "C0000001",
registration: {}, category: "CAR",
registration: {},
},
damage: {
glassToReplace: ["Windshield"],
isRepair: false,
numberOfChips: 0,
},
lineItems: {
glassParts: null,
},
payment: {
insuranceCoverage: {
isVerified: false,
},
isInsurance: false,
},
parts: [],
accountNumber: "123456789",
insuranceInfo: {},
serviceLocation: {},
customer: {},
}, },
damage: { applicationUser: {
glassToReplace: ["Windshield"], experiments: [
isRepair: false, {
numberOfChips: 0, universeName: "Concept Funnel Test With Rules",
universeId: 463,
testName: "Concept Dev Test",
testId: 392,
variationName: "Concept Test Variation",
variationId: 1133,
isActive: false,
isExposed: true,
userPartitionNumber: 84,
assignmentId: 12211739,
settings: {
someKey: "false",
sampleSetting: "Hi, my name is Vidya",
},
},
],
}, },
parts: [],
accountNumber: "123456789",
insuranceInfo: {},
serviceLocation: {},
customer: {},
}); });
// Assert // Assert
@ -640,9 +671,7 @@ describe("Actions", () => {
// Act // Act
const response = await actions.loadSession(context, { const response = await actions.loadSession(context, {
referralNumber: "123", savedSessionId: "",
referralDate: new Date().toUTCString(),
referralCorrelationId: "xxx-xxx-xxx",
}); });
// Assert // Assert
@ -667,9 +696,7 @@ describe("Actions", () => {
// Act // Act
const response = await actions.loadSession(context, { const response = await actions.loadSession(context, {
referralNumber: "123", savedSessionId: "",
referralDate: new Date().toUTCString(),
referralCorrelationId: "xxx-xxx-xxx",
}); });
// Assert // Assert
@ -694,9 +721,7 @@ describe("Actions", () => {
// Act // Act
const response = await actions.loadSession(context, { const response = await actions.loadSession(context, {
referralNumber: "123", savedSessionId: "",
referralDate: new Date().toUTCString(),
referralCorrelationId: "xxx-xxx-xxx",
}); });
// Assert // Assert

View file

@ -1,13 +1,10 @@
html { html {
.has-error { .has-error {
// START HOVER
&.list-button-horizontal, &.list-button-horizontal,
&.list-button, &.list-button,
&.list-button.list-group, &.list-button.list-group,
&.list-card { &.list-card {
border: 1px solid $red;
position: relative; position: relative;
z-index: 4;
.button-content { .button-content {
border: none; border: none;
@ -17,9 +14,30 @@ html {
@include box-shadow-hover($red-200); @include box-shadow-hover($red-200);
z-index: 5; z-index: 5;
} }
input[type="radio"]:focus + .list-button-content { input[type="radio"]:focus + .list-button-content {
box-shadow: 0 0 0 2.5px $red; box-shadow: 0 0 0 2.5px $red;
} }
.list-button-content,
.list-button-horizontal-content {
border: 1px solid $red;
}
input[type="radio"]:focus + .list-button-horizontal-content {
z-index: 5;
box-shadow: 0 0 0 2.5px $red;
}
input[type="radio"]:hover + .list-button-content,
input[type="radio"]:hover + .list-button-horizontal-content {
z-index: 5;
box-shadow: none;
}
}
&.list-card {
border: 1px solid $red;
} }
&.ui-radio { &.ui-radio {
@ -32,10 +50,9 @@ html {
} }
.form-check-input { .form-check-input {
&:focus { &:focus {
@include box-shadow-hover($red-200); box-shadow: 0 0 0 2.5px $red;
} }
} }
// END HOVER
&.list-button, &.list-button,
&.list-card { &.list-card {
@ -174,7 +191,7 @@ html {
} }
&.btn.btn-primary:focus, &.btn.btn-primary:focus,
&.btn.btn-primary:focus-visible { &.btn.btn-primary:focus-visible {
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $blue-700; box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $gray-700;
} }
} }
} }

View file

@ -1,7 +1,7 @@
<template> <template>
<button <button
:aria-disabled="isDisabled" :aria-disabled="isDisabled"
class="btn d-flex align-items-center py-3 px-4 delay" class="btn d-flex align-items-center justify-content-center py-3 px-4 delay"
:class="[ :class="[
isPrimary ? 'btn-primary' : 'btn-secondary', isPrimary ? 'btn-primary' : 'btn-secondary',
isFloat ? 'float-end' : '', isFloat ? 'float-end' : '',
@ -11,7 +11,7 @@
<span class="m-0">{{ this.buttonText }}</span> <span class="m-0">{{ this.buttonText }}</span>
<loader <loader
class="ms-2" class="ms-2"
v-if="isLoaderDisplayed" v-if="isLoaderDisplayed && !suppressLoader"
v-bind:class="[this.loaderColor, this.loaderPosition]" /> v-bind:class="[this.loaderColor, this.loaderPosition]" />
</button> </button>
</template> </template>
@ -28,6 +28,7 @@ export default {
loaderColor: String, loaderColor: String,
loaderPosition: String, loaderPosition: String,
isFloat: Boolean, isFloat: Boolean,
suppressLoader: Boolean,
}, },
data() { data() {
return { return {
@ -50,6 +51,12 @@ export default {
this.$emit("click-event"); this.$emit("click-event");
} }
}, },
resetButtonStyle() {
this.isLoaderDisplayed = false;
},
},
mounted() {
this.isLoaderDisplayed = false;
}, },
components: { components: {
loader, loader,