@@ -159,6 +159,7 @@ export default {
defaultSiteHeader: 'SiteSubHeaderWidget',
noTpa: 'ContentGroupNoTPAWidget',
notSeeingPreferredShop: 'ContentGroupNotSeeingPreferredShop',
+ ymmNotFound: 'YMMNotFoundWidget',
disclaimerText: 'DisclaimerWidget'
},
rules: {
@@ -172,20 +173,21 @@ export default {
computed: {
subHeaderCmsWidgetName() {
switch (this.bailout.bailoutCode) {
- case BailoutCode.TPANotEnabled:
- return this.widget.noTpa;
-
case BailoutCode.DoNotSeeMyShop:
return this.widget.notSeeingPreferredShop;
-
+ case BailoutCode.TPANotEnabled:
+ return this.widget.noTpa;
+ case BailoutCode.YMMNotFound:
+ return this.widget.ymmNotFound;
default:
return this.widget.defaultSiteHeader;
}
},
subHeaderContentProperty() {
switch (this.bailout.bailoutCode) {
- case BailoutCode.TPANotEnabled:
case BailoutCode.DoNotSeeMyShop:
+ case BailoutCode.TPANotEnabled:
+ case BailoutCode.YMMNotFound:
return widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT;
default:
@@ -194,8 +196,9 @@ export default {
},
subContentProperty() {
switch (this.bailout.bailoutCode) {
- case BailoutCode.TPANotEnabled:
case BailoutCode.DoNotSeeMyShop:
+ case BailoutCode.TPANotEnabled:
+ case BailoutCode.YMMNotFound:
return widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT;
default:
@@ -259,6 +262,10 @@ export default {
min-height: 1px;
padding-left: .9375rem;
padding-right: .9375rem;
+
+ .sub-header-content {
+ margin-top: 1.25rem;
+ }
}
}
diff --git a/src/layouts/contact-confirmation/contact-confirmation.vue b/src/layouts/contact-confirmation/contact-confirmation.vue
index 2819efd6..a21f7a1a 100644
--- a/src/layouts/contact-confirmation/contact-confirmation.vue
+++ b/src/layouts/contact-confirmation/contact-confirmation.vue
@@ -7,7 +7,9 @@
@@ -20,6 +22,8 @@
import siteHeader from '@/iss-components/site-header/site-header.vue';
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue';
// Supporting files
+import BailoutCode from '@/constants/bailoutCode';
+import widgetFields from '@/constants/cms-widget-fields.js';
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import issPageValues from '@/router/router-constants/issPage-values.js';
import settleAllPromises from '@/helpers/layout-helper';
@@ -55,12 +59,44 @@ export default {
return { mainStore };
},
data() {
- const contactConfirmationData = useMainStore().pageData(issPageValues.CONTACT_CONFIRMATION);
+ const bailout = useMainStore().pageData(issPageValues.BAILOUT_PAGE);
return {
- contactConfirmationModel: contactConfirmationData
+ bailout: bailout ?? { bailoutCode: BailoutCode.Unknown },
+ contactConfirmationModel: useMainStore().pageData(issPageValues.CONTACT_CONFIRMATION),
+ widget: {
+ defaultSiteHeader: 'SiteSubHeaderWidget',
+ ymmNotFound: 'YMMNotFoundWidget'
+ },
};
},
- computed: {},
+ computed: {
+ subHeaderCmsWidgetName() {
+ switch (this.bailout.bailoutCode) {
+ case BailoutCode.YMMNotFound:
+ return this.widget.ymmNotFound;
+ default:
+ return this.widget.defaultSiteHeader;
+ }
+ },
+ subHeaderContentProperty() {
+ switch (this.bailout.bailoutCode) {
+ case BailoutCode.YMMNotFound:
+ return widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT;
+
+ default:
+ return widgetFields.SUB_HEADER_WIDGET.SUB_HEADER_TEXT;
+ }
+ },
+ subContentProperty() {
+ switch (this.bailout.bailoutCode) {
+ case BailoutCode.YMMNotFound:
+ return widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT;
+
+ default:
+ return widgetFields.SUB_HEADER_WIDGET.SECONDARY_TEXT;
+ }
+ },
+ },
methods: {}
};
diff --git a/src/layouts/entry-page/entry-page.vue b/src/layouts/entry-page/entry-page.vue
index ce114928..89609dd7 100644
--- a/src/layouts/entry-page/entry-page.vue
+++ b/src/layouts/entry-page/entry-page.vue
@@ -13,9 +13,9 @@ import { validateISSClientTag, validateISSClientSignature } from '@/helpers/clie
import { getISSCookie, updateOrCreateISSCookie } from '@/helpers/cookie-helper.js';
import { useMainStore } from '@/store';
import showIssLoadingModal from '@/helpers/loading-modal-helper';
-import applicationConfig from '@/constants/application-config';
import { toPossessive } from '@/helpers/text-helper';
import analyticsMixin from '@/mixins/analytics-mixin';
+import routerParams from '@/router/router-constants/router-params';
export default {
name: 'entry-page',
@@ -80,23 +80,19 @@ export default {
}
this.mainStore.applicationUser.coverageAttempts = 0;
- if (
- applicationConfig.CURRENT_ENVIRONMENT === 'Localhost'
- || applicationConfig.CURRENT_ENVIRONMENT === 'Dev'
- || applicationConfig.CURRENT_ENVIRONMENT === 'SysTest'
- ) {
- this.navigateForward();
- } else {
- // Forced full location redirect here. We do not want the entry page as part of the router/flow/path history.
- window.location = `/?issPage=${issPageValues.WELCOME_PAGE}`;
- }
+
+ // Forced full location redirect here. We do not want the entry page as part of the router/flow/path history.
+ window.location = `/?issPage=${issPageValues.WELCOME_PAGE}`;
},
methods:
{
navigateForward() {
+ // If we use navigateForward we need to skip saving session (creating referral).
this.$router.navigate(
this.navigationScenarios.MOVE_FORWARD_ENTRY_PAGE,
- this.$route
+ this.$route,
+ {},
+ { [routerParams.SKIP_SAVE_SESSION]: true }
);
},
parseQueryParms() {
diff --git a/src/layouts/license-plate-lookup/license-plate-lookup.vue b/src/layouts/license-plate-lookup/license-plate-lookup.vue
index aeb58dcb..d70c7b39 100644
--- a/src/layouts/license-plate-lookup/license-plate-lookup.vue
+++ b/src/layouts/license-plate-lookup/license-plate-lookup.vue
@@ -76,6 +76,7 @@
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import settleAllPromises from '@/helpers/layout-helper';
import { useMainStore } from '@/store';
+import bailoutMessage from '@/constants/bailoutMessage';
import errorMessages from '@/constants/error-messages';
import { required } from '@/helpers/validation-rules';
import { defineRule, Form } from 'vee-validate';
@@ -303,6 +304,14 @@ export default {
}
});
+ if (this.mainStore.order.vehicle.vinRequired) {
+ this.mainStore.setBailout(bailoutMessage.YMMNotFound());
+ return this.$router.navigate(
+ this.navigationScenarios.BAILOUT,
+ this.$route
+ );
+ }
+
return this.navigateForward();
},
async navigateForward() {
diff --git a/src/layouts/vehicle-lookup/vin-lookup-methods/vin-lookup-methods.vue b/src/layouts/vehicle-lookup/vin-lookup-methods/vin-lookup-methods.vue
index fa5868aa..2ebf89a4 100644
--- a/src/layouts/vehicle-lookup/vin-lookup-methods/vin-lookup-methods.vue
+++ b/src/layouts/vehicle-lookup/vin-lookup-methods/vin-lookup-methods.vue
@@ -65,6 +65,15 @@ export default {
const homeAddressIndex = answers.findIndex(answer => answer.Name === vinLookupMethodSelections.HOMEADDRESS);
answers.splice(homeAddressIndex, 1);
}
+
+ const isVinRequired = useMainStore().order.vehicle.vinRequired;
+ if (isVinRequired) {
+ const vinIndex = answers.findIndex(answer => answer.Name === vinLookupMethodSelections.NOVIN);
+ if (vinIndex >= 0) {
+ answers.splice(vinIndex, 1);
+ }
+ }
+
this.answersFromCms = answers;
}
},
diff --git a/src/layouts/vin-lookup/vin-lookup.spec.js b/src/layouts/vin-lookup/vin-lookup.spec.js
index 5e82c2f8..820d2820 100644
--- a/src/layouts/vin-lookup/vin-lookup.spec.js
+++ b/src/layouts/vin-lookup/vin-lookup.spec.js
@@ -50,6 +50,10 @@ const getPartsOrQuestions = {
methodName: 'getPartsOrQuestions',
mockResponse: null
};
+const getPartsOrQuestionsVinRequired = {
+ methodName: 'getPartsOrQuestionsVinRequired',
+ mockResponse: null
+};
const vehicleWithPartQuestionsMockResponse = {
data: {
@@ -372,6 +376,8 @@ describe('vin-lookup.vue', () => {
.mockResolvedValue(lookupVehicleByVin.mockResponse);
jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
.mockResolvedValue(getPartsOrQuestions.mockResponse);
+ jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestionsVinRequired.methodName)
+ .mockResolvedValue(getPartsOrQuestionsVinRequired.mockResponse);
const { container } = render(VinLookupComponent, mountOptions);
@@ -403,6 +409,8 @@ describe('vin-lookup.vue', () => {
.mockResolvedValue(lookupVehicleByVin.mockResponse);
jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
.mockResolvedValue(getPartsOrQuestions.mockResponse);
+ jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestionsVinRequired.methodName)
+ .mockResolvedValue(getPartsOrQuestionsVinRequired.mockResponse);
const { container } = render(VinLookupComponent, mountOptions);
@@ -436,6 +444,8 @@ describe('vin-lookup.vue', () => {
.mockResolvedValue(lookupVehicleByVin.mockResponse);
jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
.mockResolvedValue(getPartsOrQuestions.mockResponse);
+ jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestionsVinRequired.methodName)
+ .mockResolvedValue(getPartsOrQuestionsVinRequired.mockResponse);
store.getCapabilityQuestions.mockResolvedValueOnce({ data: [] });
const { container } = render(VinLookupComponent, mountOptions);
@@ -469,6 +479,8 @@ describe('vin-lookup.vue', () => {
.mockResolvedValue(lookupVehicleByVin.mockResponse);
jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
.mockResolvedValue(getPartsOrQuestions.mockResponse);
+ jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestionsVinRequired.methodName)
+ .mockResolvedValue(getPartsOrQuestionsVinRequired.mockResponse);
const { container } = render(VinLookupComponent, mountOptions);
@@ -505,6 +517,8 @@ describe('vin-lookup.vue', () => {
.mockResolvedValue(lookupVehicleByVin.mockResponse);
jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
.mockResolvedValue(getPartsOrQuestions.mockResponse);
+ jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestionsVinRequired.methodName)
+ .mockResolvedValue(getPartsOrQuestionsVinRequired.mockResponse);
const { container } = render(VinLookupComponent, mountOptions);
diff --git a/src/layouts/vin-lookup/vin-lookup.vue b/src/layouts/vin-lookup/vin-lookup.vue
index 9057e864..1729c3c7 100644
--- a/src/layouts/vin-lookup/vin-lookup.vue
+++ b/src/layouts/vin-lookup/vin-lookup.vue
@@ -176,6 +176,14 @@ export default {
this.$refs.siteFooter.enableForwardAction();
showIssLoadingModal(true);
+ if (this.mainStore.order.vehicle.vinRequired) {
+ this.mainStore.setBailout(bailoutMessage.YMMNotFound());
+ return this.$router.navigate(
+ this.navigationScenarios.BAILOUT,
+ this.$route
+ );
+ }
+
if (this.needToLookupVehicle) {
try {
const vehicleLookupResponse = await this.lookupVehicleByVin(this.vin);
@@ -262,6 +270,30 @@ export default {
return;
}
+ /*
+ * If the vehicle is vinRequired, we need to call getPartsOrQuestionsVinRequired() to get the parts or questions.
+ * If the vehicle is not vinRequired, we can call getPartsOrQuestions() to get the parts or questions.
+ */
+ /*
+ if (this.mainStore.order.vehicle.vinRequired) {
+ try {
+ const partsOrQuestionsResponse = await this.getPartsOrQuestionsVinRequired();
+ await this.navigateForward(
+ partsOrQuestionsResponse.data.partsOrQuestions,
+ this
+ );
+ } catch (e) {
+ this.mainStore.setBailout(bailoutMessage.HeavyTruckVehicle(this.vehicleFromLookup.carId));
+ this.$router.navigate(
+ this.navigationScenarios.BAILOUT,
+ this.$route
+ );
+ throw e;
+ }
+
+ return;
+ }
+ */
const partsOrQuestionsResponse = await this.getPartsOrQuestions();
// Comes from vehicleQuestionsMixin.navigateForward()
diff --git a/src/layouts/welcome-page/welcome-page.vue b/src/layouts/welcome-page/welcome-page.vue
index 92ddeab2..38b35558 100644
--- a/src/layouts/welcome-page/welcome-page.vue
+++ b/src/layouts/welcome-page/welcome-page.vue
@@ -371,7 +371,6 @@ export default {
}
}
- await saveSession({ shouldAwaitSaveSessionQueue: true, bailoutOnError: true })
this.navigateForward();
},
async configureZip() {
@@ -388,6 +387,7 @@ export default {
navigateForward() {
if ((this.mainStore.applicationUser.duplicateOrders?.length > 0 ?? false)
&& !this.answeredContinueModal) {
+ // Duplicate orders found; navigating to duplicate check page and skipping save session.
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_DUPLICATES,
this.$route,
diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js
index 956ef0af..2330dde9 100644
--- a/src/mixins/analytics-mixin.js
+++ b/src/mixins/analytics-mixin.js
@@ -68,7 +68,7 @@ export default {
const store = useMainStore();
const issConfig = store.issConfig;
const currentPageName = this.getPageNameByQueryString();
- //await this.validateSession();
+ // await this.validateSession();
const submittedOrder = store.getSubmittedOrder();
const hasSubmittedOrder = store.hasSubmittedOrder();
@@ -98,7 +98,7 @@ export default {
const store = useMainStore();
const issConfig = store.issConfig;
const currentPageName = this.getPageNameByQueryString();
- //await this.validateSession();
+ // await this.validateSession();
const submittedOrder = store.getSubmittedOrder();
const hasSubmittedOrder = store.hasSubmittedOrder();
@@ -246,6 +246,18 @@ export default {
payload.vehicleStyle = "";
}
+ if (isDefined(order.vehicle.carId)) {
+ payload.vehicleCarId = order.vehicle.carId;
+ } else {
+ payload.vehicleCarId = "";
+ }
+
+ if (isDefined(order.vehicle.vinRequired)) {
+ payload.vehicleIsVinRequired = order.vehicle.vinRequired;
+ } else {
+ payload.vehicleIsVinRequired = "";
+ }
+
// Glass pieces
const glass = order.damage.glassToReplace ?? [];
if (glass.length === 0) {
@@ -257,7 +269,7 @@ export default {
payload.glassToReplace = glassString;
}
- //EON
+ // EON
if (order.eon) {
payload.eon = order.eon;
} else {
@@ -555,7 +567,6 @@ export default {
},
async initSession() {
-
regenerateDeviceId();
regenerateUserId();
diff --git a/src/mixins/vehicle-questions-mixin.js b/src/mixins/vehicle-questions-mixin.js
index b4bdad07..54331ae6 100644
--- a/src/mixins/vehicle-questions-mixin.js
+++ b/src/mixins/vehicle-questions-mixin.js
@@ -355,6 +355,9 @@ export default {
async getPartsOrQuestions() {
return useMainStore().getPartsOrQuestions();
},
+ async getPartsOrQuestionsVinRequired() {
+ return useMainStore().getPartsOrQuestionsVinRequired();
+ },
// Can't use `this` because navigateForward is also called from vin-pages-mixin
async navigateForward(partsOrQuestions, vm) {
diff --git a/src/router/index.js b/src/router/index.js
index 1fbe148f..25400a08 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -46,6 +46,7 @@ const routes = [
if (getISSCookie() !== null && !isSavedSessionStillActive()) {
// await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE);
await GoToStartOn404(next);
+ return null;
}
// Process ISS cookie.
@@ -66,9 +67,10 @@ const routes = [
if (!arePagePrerequisitesValid(component)) {
await GoToStartOn404(next);
+ return null;
}
- return next({name: issPageToUse, query: to.query, params: to.params});
+ return next({name: issPageToUse, query: to.query, params: to.params, state: to.state});
}
var routeData = [];
@@ -123,13 +125,15 @@ const routes = [
const tempMsgCopy = 'Pre Requisites failed, need to handle. Default is Welcome Page.';
const tempMsgHeadline = `${issPageToUse}: pre-req failed...`;
await GoToStartOn404(next, tempMsgCopy, tempMsgHeadline);
+ return null;
}
// Assign current query string parameters, as well as our issPage one.
next({
name: routeData[0].name,
query: Object.assign(to.query, {issPage: routeData[0].name}),
- params: to.params
+ params: to.params,
+ state: to.state
});
return null;
}
@@ -146,11 +150,29 @@ const router = createRouter({
});
router.beforeEach(async (to, from) => {
+ const store = useMainStore();
const fromQueryPage = from.query?.issPage;
+
if (fromQueryPage === undefined) {
showIssLoadingModal(true);
}
+ const callSaveSession = !store.order?.referralNumber && (to.name === issPageValues.VEHICLE_SELECTION || to.name === issPageValues.POLICY_VEHICLES);
+ if (callSaveSession) {
+ // Forcing a synchronous savesession call here to create the referral for the first time.
+ // AfterEach does not support synchronous calls that block navigation. Which is why this is in the beforeEach method.
+ // This only needs to be called once in a certain location, all other saveSession calls are async in afterEach (except the last one on order submission)
+ // NOTE: This call should be creating the referral number. After this point in the site flow, referral number is critical for several pieces of logic and logging.
+ // Therefore, we want to force a bailout here if it errors out.
+ showIssLoadingModal(true);
+ try {
+ await saveSession({ shouldAwaitSaveSessionQueue: true, bailoutOnError: true });
+ }
+ finally {
+ showIssLoadingModal(false);
+ }
+ }
+
const toQueryPage = to.query?.issPage;
const notToPayInAdvanceReturn = toQueryPage !== issPageValues.PAYMENT_RETURN;
const isInIframe = window !== window.top || fromQueryPage === issPageValues.PAYMENT_PAGE;
@@ -168,7 +190,6 @@ router.beforeEach(async (to, from) => {
return false;
}
- const store = useMainStore();
// Prevent navigating backwards if we enter a bailout that we are not allowed to go back on
if (store.isBailout && from.name === issPageValues.BAILOUT_PAGE && to.name !== 'root' && to.name !== issPageValues.CONTACT_CONFIRMATION
&& !canBailoutNavigateBack()) {
@@ -196,11 +217,15 @@ router.afterEach(async (to, from) => {
await analyticsMixin.methods.validateSession();
- const skipSaveSession = !!router.options.history.state[routerParams.SKIP_SAVE_SESSION];
- if (from.name !== undefined && !skipSaveSession && !store.hasSubmittedOrder()) {
- await saveSession({ bailoutOnError: from.name === issPageValues.ENTRY_PAGE});
- }
+ const skipSaveSession = !!(
+ to.state?.[routerParams.SKIP_SAVE_SESSION]
+ ?? router.options.history.state?.[routerParams.SKIP_SAVE_SESSION]
+ );
+ if (from.name !== undefined && !skipSaveSession && !store.hasSubmittedOrder()) {
+ await saveSession({ bailoutOnError: false });
+ }
+
document.title = routerTitles[to.query.issPage] || 'Safelite Solutions®';
if (shouldRunExperiments) {
diff --git a/src/router/router.afterEach.spec.js b/src/router/router.afterEach.spec.js
new file mode 100644
index 00000000..6f2aee8f
--- /dev/null
+++ b/src/router/router.afterEach.spec.js
@@ -0,0 +1,115 @@
+import issPageValues from '@/router/router-constants/issPage-values';
+import routerParams from '@/router/router-constants/router-params';
+
+let router;
+let saveSession;
+let mockAfterEachHandler;
+let mockStore;
+
+jest.mock('vue-router', () => ({
+ createWebHistory: jest.fn(() => ({ state: {} })),
+ createRouter: jest.fn((options) => ({
+ options,
+ beforeEach: jest.fn(),
+ afterEach: jest.fn((handler) => {
+ mockAfterEachHandler = handler;
+ }),
+ addRoute: jest.fn(),
+ getRoutes: jest.fn(() => []),
+ hasRoute: jest.fn(() => true),
+ push: jest.fn(),
+ go: jest.fn(),
+ currentRoute: { value: { query: { issPage: 'welcomePage' } } }
+ }))
+}));
+
+jest.mock('@/helpers/order-helper.js', () => ({
+ saveSession: jest.fn(() => Promise.resolve())
+}));
+
+jest.mock('@/mixins/analytics-mixin', () => ({
+ __esModule: true,
+ default: {
+ methods: {
+ validateSession: jest.fn(() => Promise.resolve()),
+ logDigitalConsumer: jest.fn(),
+ pushIssSessionData: jest.fn(),
+ pushPageViewToGA: jest.fn(),
+ pushExperimentsToDataLayer: jest.fn(),
+ pushOrderToDataLayer: jest.fn()
+ }
+ }
+}));
+
+jest.mock('@/store', () => ({
+ useMainStore: jest.fn(() => mockStore)
+}));
+
+describe('Router afterEach skipSaveSession', () => {
+ beforeAll(() => {
+ router = require('@/router/').default;
+ ({ saveSession } = require('@/helpers/order-helper.js'));
+ });
+
+ beforeEach(() => {
+ mockStore = {
+ applicationUser: { triggeredSiteEntry: true },
+ runExperimentsForTrigger: jest.fn(() => Promise.resolve()),
+ updateLastPageVisited: jest.fn(),
+ clearSaveSessionPromise: jest.fn(),
+ hasSubmittedOrder: jest.fn(() => false)
+ };
+
+ router.options.history.state = {};
+ jest.clearAllMocks();
+ });
+
+ it('should skip saveSession when skipSaveSession is true on to.state', async () => {
+ await mockAfterEachHandler(
+ {
+ name: issPageValues.WELCOME_PAGE,
+ query: { issPage: issPageValues.WELCOME_PAGE },
+ state: { [routerParams.SKIP_SAVE_SESSION]: true }
+ },
+ {
+ name: issPageValues.ENTRY_PAGE,
+ redirectedFrom: undefined
+ }
+ );
+
+ expect(saveSession).not.toHaveBeenCalled();
+ });
+
+ it('should skip saveSession when skipSaveSession is true on history state', async () => {
+ router.options.history.state = { [routerParams.SKIP_SAVE_SESSION]: true };
+
+ await mockAfterEachHandler(
+ {
+ name: issPageValues.WELCOME_PAGE,
+ query: { issPage: issPageValues.WELCOME_PAGE }
+ },
+ {
+ name: issPageValues.ENTRY_PAGE,
+ redirectedFrom: undefined
+ }
+ );
+
+ expect(saveSession).not.toHaveBeenCalled();
+ });
+
+ it('should call saveSession when skipSaveSession is not present', async () => {
+ await mockAfterEachHandler(
+ {
+ name: issPageValues.WELCOME_PAGE,
+ query: { issPage: issPageValues.WELCOME_PAGE }
+ },
+ {
+ name: issPageValues.ENTRY_PAGE,
+ redirectedFrom: undefined
+ }
+ );
+
+ expect(saveSession).toHaveBeenCalledTimes(1);
+ expect(saveSession).toHaveBeenCalledWith({ bailoutOnError: false });
+ });
+});
\ No newline at end of file
diff --git a/src/router/router.beforeEach.spec.js b/src/router/router.beforeEach.spec.js
new file mode 100644
index 00000000..85c9160e
--- /dev/null
+++ b/src/router/router.beforeEach.spec.js
@@ -0,0 +1,72 @@
+import issPageValues from '@/router/router-constants/issPage-values';
+
+let router;
+let saveSession;
+let showIssLoadingModal;
+let mockBeforeEachHandler;
+let mockStore;
+
+jest.mock('vue-router', () => ({
+ createWebHistory: jest.fn(() => ({ state: {} })),
+ createRouter: jest.fn((options) => ({
+ options,
+ beforeEach: jest.fn((handler) => {
+ mockBeforeEachHandler = handler;
+ }),
+ afterEach: jest.fn(),
+ addRoute: jest.fn(),
+ getRoutes: jest.fn(() => []),
+ hasRoute: jest.fn(() => true),
+ push: jest.fn(),
+ go: jest.fn(),
+ currentRoute: { value: { query: { issPage: 'welcome-page' } } }
+ }))
+}));
+
+jest.mock('@/helpers/order-helper.js', () => ({
+ saveSession: jest.fn(() => Promise.resolve())
+}));
+
+jest.mock('@/helpers/loading-modal-helper', () => jest.fn());
+
+jest.mock('@/store', () => ({
+ useMainStore: jest.fn(() => mockStore)
+}));
+
+describe('Router beforeEach callSaveSession', () => {
+ beforeAll(() => {
+ router = require('@/router/').default;
+ ({ saveSession } = require('@/helpers/order-helper.js'));
+ const loadingModalModule = require('@/helpers/loading-modal-helper');
+ showIssLoadingModal = loadingModalModule.default || loadingModalModule;
+ });
+
+ beforeEach(() => {
+ mockStore = {
+ order: {},
+ isBailout: false
+ };
+
+ jest.clearAllMocks();
+ });
+
+ it('calls saveSession synchronously when entering vehicle-selection without a referral number', async () => {
+ const result = await mockBeforeEachHandler(
+ {
+ name: issPageValues.VEHICLE_SELECTION,
+ query: { issPage: issPageValues.VEHICLE_SELECTION },
+ href: '/?issPage=vehicle-selection'
+ },
+ {
+ name: issPageValues.WELCOME_PAGE,
+ query: { issPage: issPageValues.WELCOME_PAGE }
+ }
+ );
+
+ expect(saveSession).toHaveBeenCalledTimes(1);
+ expect(saveSession).toHaveBeenCalledWith({ shouldAwaitSaveSessionQueue: true, bailoutOnError: true });
+ expect(showIssLoadingModal).toHaveBeenNthCalledWith(1, true);
+ expect(showIssLoadingModal).toHaveBeenNthCalledWith(2, false);
+ expect(result).toBe(true);
+ });
+});
\ No newline at end of file
diff --git a/src/store/index.js b/src/store/index.js
index 386832f3..091ffec1 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -4,13 +4,13 @@ import bailoutCode from '@/constants/bailoutCode';
import coverageStatuses from '@/constants/coverage-statuses';
import coverageType from '@/constants/coverage-type';
import damageLocationsSelected from '@/constants/damage-locations-selected';
-import endorsementOptions from '@/constants/endorsement-options';
import endpoints from '@/constants/endpoints';
import { experimentSettings, experimentTriggers, experimentUniverses } from '@/constants/experiments';
import partNumberStrings from '@/constants/part-number-strings';
import partTypeStrings from '@/constants/part-type-strings';
import { paymentMethods } from '@/constants/payment-method-constants';
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
+import submitType from '@/constants/submit-type';
import webStorageConstants from '@/constants/web-storage-constants';
import globalMethods from '@/global-methods';
import { getSessionKeyValue, getUserIdValue, deleteISSCookie, getDeviceIdValue } from '@/helpers/cookie-helper';
@@ -27,7 +27,6 @@ import { getRecalPartNumbers, getTopLevelGlassPartsWithRecal, getHasRecalibratio
import { getDateForSavedSessionTimeout } from '@/helpers/session-helper';
import { isMobileDevice } from '@/helpers/useragent-helper';
import issPageValues from '@/router/router-constants/issPage-values';
-import CoverageStatuses from '@/constants/coverage-statuses';
import { getNonFalseValuesOfPropertyInArrayOfObjects, sortArrayOfObjectsByPropertyValue } from '@/helpers/object-helper';
const storeId = 'main';
@@ -85,7 +84,10 @@ export const getDefaultState = () => ({
lastName: null
},
isBigTruck: null,
- canSafeliteService: null
+ canSafeliteService: null,
+ skipVINLookup: null,
+ skipPartQuestions: null,
+ vinRequired: null
},
damage: {
isRepair: null,
@@ -768,7 +770,7 @@ export const useMainStore = defineStore({
this.updateDeductible(r.data);
return r;
} catch (e) {
- this.updateCoverageStatus(CoverageStatuses.PENDING);
+ this.updateCoverageStatus(coverageStatuses.PENDING);
}
},
@@ -814,9 +816,7 @@ export const useMainStore = defineStore({
},
// PartsOrQuestions API Actions
- async getPartsOrQuestions() {
- this.resetPartsAndDependencies();
-
+ getPartsOrQuestionsPayload() {
const { vehicle } = this;
const { damage } = this;
const { order } = this;
@@ -828,17 +828,22 @@ export const useMainStore = defineStore({
// create a new array to avoid mutating state
const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray);
+ return {
+ carId,
+ glassPieces: glassArrayForPayload,
+ zip: zipCode,
+ vin,
+ oemEndorsementFlag: this.hasOemEndorsement
+ };
+ },
+ async getPartsOrQuestions() {
+ this.resetPartsAndDependencies();
+ const payload = this.getPartsOrQuestionsPayload();
const response = await globalMethods.callHttpClient({
method: endpoints.GetPartsOrQuestions.method,
endpoint: endpoints.GetPartsOrQuestions.url,
- payload: {
- carId,
- glassPieces: glassArrayForPayload,
- zip: zipCode,
- vin,
- oemEndorsementFlag: this.hasOemEndorsement
- }
+ payload: payload
});
// Flatten location and name properties
@@ -846,7 +851,21 @@ export const useMainStore = defineStore({
return response;
},
+ async getPartsOrQuestionsVinRequired() {
+ this.resetPartsAndDependencies();
+ const payload = this.getPartsOrQuestionsPayload();
+ const response = await globalMethods.callHttpClient({
+ method: endpoints.GetPartsOrQuestionsVinRequired.method,
+ endpoint: endpoints.GetPartsOrQuestionsVinRequired.url,
+ payload: payload
+ });
+
+ // Flatten location and name properties
+ response.data.partsOrQuestions = convertGlassPieceNamingFromApi(response.data.partsOrQuestions);
+
+ return response;
+ },
async getParts() {
const { vehicle } = this.order;
const { damage } = this.order;
@@ -2087,6 +2106,9 @@ export const useMainStore = defineStore({
this.order.vehicle.imageColor = vehicle.imageVifColor;
this.order.vehicle.isBigTruck = vehicle.isBigTruck;
this.order.vehicle.canSafeliteService = vehicle.canSafeliteService;
+ this.order.vehicle.skipVINLookup = vehicle.skipVINLookup;
+ this.order.vehicle.skipPartQuestions = vehicle.skipPartQuestions;
+ this.order.vehicle.vinRequired = vehicle.vinRequired;
this.updateSupportingItems(null);
this.updateVaps(null);
@@ -2221,6 +2243,9 @@ export const useMainStore = defineStore({
this.order.vehicle.registration.lastName = null;
this.order.vehicle.isBigTruck = null;
this.order.vehicle.canSafeliteService = null;
+ this.order.vehicle.skipVINLookup = null;
+ this.order.vehicle.skipPartQuestions = null;
+ this.order.vehicle.vinRequired = null;
if (this.isBailout && this.bailoutCode === bailoutCode.HeavyTruckVehicle) {
this.resetBailout();
@@ -3164,7 +3189,7 @@ export const useMainStore = defineStore({
return JSON.parse(window.sessionStorage.getItem(webStorageConstants.SUBMITTED_ORDER));
},
- async createSubmittedOrder(submitType) {
+ async createSubmittedOrder(pSubmitType) {
if (this.hasSubmittedOrder()) {
return;
}
@@ -3175,16 +3200,27 @@ export const useMainStore = defineStore({
submittedOrder.isUnverified = this.isUnverified;
submittedOrder.isVerified = this.isVerified;
- submittedOrder.submitType = submitType;
+ submittedOrder.submitType = pSubmitType;
submittedOrder.payment.isPayInAdvance = this.isPayInAdvance;
submittedOrder.hasRecalibrationPart = hasRecalibrationPart;
// set to sessionStorage
window.sessionStorage.setItem(webStorageConstants.SUBMITTED_ORDER, JSON.stringify(submittedOrder));
+ let persistedBailout = null;
+ if (pSubmitType === submitType.BAILOUT) {
+ persistedBailout = this.pageData(issPageValues.BAILOUT_PAGE);
+ }
// clear vuex
this.resetState();
+ if (persistedBailout) {
+ this.updatePageData({
+ page: issPageValues.BAILOUT_PAGE,
+ data: persistedBailout
+ });
+ }
+
// delete cookie
deleteISSCookie();