diff --git a/src/constants/store-actions.js b/src/constants/store-actions.js index 702f7c737..84241a2f8 100644 --- a/src/constants/store-actions.js +++ b/src/constants/store-actions.js @@ -131,6 +131,8 @@ const storeActions = { UPDATE_HAS_TRIGGERED_ERROR: "updateHasTriggeredError", GET_VALID_IDEMPOTENCY_KEY: "getValidIdempotencyKey", CORRECT_IDEMPOTENCY_KEY_EXPIRY: "correctIdempotencyKeyExpiry", + + SAVE_BAILOUT_CODE: "saveBailoutCode", }; export { storeActions }; diff --git a/src/constants/store-mutations.js b/src/constants/store-mutations.js index c43e737d3..238898bb9 100644 --- a/src/constants/store-mutations.js +++ b/src/constants/store-mutations.js @@ -114,6 +114,9 @@ const storeMutations = { UPDATE_EXPERIMENTS: "updateExperiments", UPDATE_TRIGGERED_SITE_ENTRY: "updateTriggeredSiteEntry", + // BAILOUT MUTATIONS + UPDATE_BAILOUT_CODE: "updateBailoutCode", + // EXTERNAL_PARAMETER MUTATIONS UPDATE_IS_EXTERNAL_PARAMETER: "updateIsExternalParameter", UPDATE_EXTERNAL_PARAMETER_YEAR: "updateExternalParameterYear", diff --git a/src/layouts/bailout/bailout.vue b/src/layouts/bailout/bailout.vue index 3bb5c33b1..ae5600e08 100644 --- a/src/layouts/bailout/bailout.vue +++ b/src/layouts/bailout/bailout.vue @@ -157,7 +157,7 @@ export default { methods: { getBailoutCodeFromStore() { - return bailoutCodes.PARTS_NOT_FOUND; + return store.getters.applicationUser.bailoutCode; }, getFirstNameFromStore() { return store.getters.order.customer.firstName; @@ -248,10 +248,11 @@ export default { computed: { getSubHeaderWidget() { - if (this.bailoutCode === bailoutCodes.PARTS_NOT_FOUND) { - return "PartsNotFoundSubHeaderWidget"; - } else { - return "FunnelSubHeaderWidget"; + switch (this.bailoutCode) { + case bailoutCodes.PARTS_NOT_FOUND: + return "PartsNotFoundSubHeaderWidget"; + default: + return "FunnelSubHeaderWidget"; } }, }, diff --git a/src/mixins/bailout-mixin.js b/src/mixins/bailout-mixin.js new file mode 100644 index 000000000..f8c724da2 --- /dev/null +++ b/src/mixins/bailout-mixin.js @@ -0,0 +1,13 @@ +import { navigationScenarios } from "@/router/constants/navigation-scenarios"; + +export default { + methods: { + navigateToBailoutPage(vm, bailoutCode) { + const self = vm ?? this; + + self.dispatchStoreAction(self.storeActions.SAVE_BAILOUT_CODE, bailoutCode).then(() => { + self.$router.navigateWithoutSaving(navigationScenarios.BAILOUT, self.pageName); + }); + }, + }, +}; diff --git a/src/mixins/bailout-mixin.spec.js b/src/mixins/bailout-mixin.spec.js new file mode 100644 index 000000000..c213de767 --- /dev/null +++ b/src/mixins/bailout-mixin.spec.js @@ -0,0 +1,103 @@ +import bailoutMixin from "@/mixins/bailout-mixin"; +import { storeActions } from "@/constants/store-actions.js"; +import { navigationScenarios } from "@/router/constants/navigation-scenarios"; +import { bailoutCodes } from "@/constants/bailout-codes.js"; + +describe("bailout-mixin.js", () => { + test("navigateToBailoutPage: dispatches SAVE_BAILOUT_CODE action with bailout code", async () => { + // Arrange + const mockVm = createMockVm(); + const bailoutCode = bailoutCodes.PARTS_NOT_FOUND; + + // Act + await bailoutMixin.methods.navigateToBailoutPage(mockVm, bailoutCode); + + // Assert + expect(mockVm.dispatchStoreAction).toHaveBeenCalledWith( + storeActions.SAVE_BAILOUT_CODE, + bailoutCode + ); + }); + + test("navigateToBailoutPage: navigates to bailout page after saving bailout code", async () => { + // Arrange + const mockVm = createMockVm(); + const bailoutCode = bailoutCodes.PARTS_NOT_FOUND; + + // Act + await bailoutMixin.methods.navigateToBailoutPage(mockVm, bailoutCode); + + // Assert + expect(mockVm.$router.navigateWithoutSaving).toHaveBeenCalledWith( + navigationScenarios.BAILOUT, + mockVm.pageName + ); + }); + + test("navigateToBailoutPage: uses current context (this) when vm is not provided", async () => { + // Arrange + const mockRouter = { + navigateWithoutSaving: jest.fn().mockResolvedValue(undefined), + }; + const mockThis = { + dispatchStoreAction: jest.fn().mockResolvedValue(undefined), + $router: mockRouter, + storeActions: storeActions, + pageName: "test-page", + }; + + const bailoutCode = bailoutCodes.PARTS_NOT_FOUND; + + // Act + await bailoutMixin.methods.navigateToBailoutPage.call(mockThis, undefined, bailoutCode); + + // Assert + expect(mockThis.dispatchStoreAction).toHaveBeenCalledWith( + storeActions.SAVE_BAILOUT_CODE, + bailoutCode + ); + }); + + test("navigateToBailoutPage: passes correct bailout code to store", async () => { + // Arrange + const mockVm = createMockVm(); + const customBailoutCode = 999; + + // Act + await bailoutMixin.methods.navigateToBailoutPage(mockVm, customBailoutCode); + + // Assert + expect(mockVm.dispatchStoreAction).toHaveBeenCalledWith( + storeActions.SAVE_BAILOUT_CODE, + customBailoutCode + ); + }); + + test("navigateToBailoutPage: calls navigateWithoutSaving with correct parameters", async () => { + // Arrange + const mockVm = createMockVm(); + const mockPageName = "vehicle-damage"; + mockVm.pageName = mockPageName; + const bailoutCode = bailoutCodes.PARTS_NOT_FOUND; + + // Act + await bailoutMixin.methods.navigateToBailoutPage(mockVm, bailoutCode); + + // Assert + expect(mockVm.$router.navigateWithoutSaving).toHaveBeenCalledWith( + navigationScenarios.BAILOUT, + mockPageName + ); + }); +}); + +function createMockVm() { + return { + dispatchStoreAction: jest.fn().mockResolvedValue(undefined), + $router: { + navigateWithoutSaving: jest.fn().mockResolvedValue(undefined), + }, + storeActions, + pageName: "test-page", + }; +} diff --git a/src/mixins/vin-pages-mixin.js b/src/mixins/vin-pages-mixin.js index fc195d910..2309cbde5 100644 --- a/src/mixins/vin-pages-mixin.js +++ b/src/mixins/vin-pages-mixin.js @@ -1,9 +1,11 @@ import { storeActions } from "@/constants/store-actions.js"; import store from "@/store"; import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin"; +import bailoutMixin from "@/mixins/bailout-mixin"; import { saveSession } from "@/helpers/heritage-integration/order-helper.js"; import { experimentSettings } from "@/constants/experiments"; import { vinLookupMethodSelections } from "@/constants/vin-lookup-method-selections.js"; +import { bailoutCodes } from "@/constants/bailout-codes"; export default { computed: { @@ -34,6 +36,11 @@ export default { const result = await this.dispatchStoreAction(storeActions.GET_PARTS_OR_QUESTIONS, { pageNameToLog: pageName, }); + + if (result.PartsNotFound) { + bailoutMixin.methods.navigateToBailoutPage(this, bailoutCodes.PARTS_NOT_FOUND); + } + const partsOrQuestions = result.data.partsOrQuestions; vehicleQuestionsMixin.methods.navigateForward(partsOrQuestions, this); diff --git a/src/router/constants/routing-table.js b/src/router/constants/routing-table.js index 81c010d5b..877802995 100644 --- a/src/router/constants/routing-table.js +++ b/src/router/constants/routing-table.js @@ -274,6 +274,10 @@ const routingTable = function () { scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS, destinationPageData: routeData.QUOTE, }, + { + scenario: navigationScenarios.BAILOUT, + destinationPageData: routeData.BAILOUT, + }, ], }, { diff --git a/src/store/index.js b/src/store/index.js index 0d64fc51f..e8d6cc7fe 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -216,6 +216,7 @@ const getDefaultState = () => { affiliateCookies: [], loggingOption: false, hasAlreadyTriggeredError: false, + bailoutCode: null, }, idempotencyKeyFields: { referralCorrelationId: null, @@ -950,6 +951,9 @@ export const mutations = { state.idempotencyKeyFields.totalInCents = totalInCents; state.idempotencyKeyFields.expiryTime = expiryTime; }, + updateBailoutCode(state, bailoutCode) { + state.applicationUser.bailoutCode = bailoutCode; + }, }; // Export Getters @@ -1903,21 +1907,31 @@ export const actions = { // create a new array to avoid mutating state const glassArrayForPayload = convertGlassPieceNamingForApi(glassArray); - const response = await globalMethods.callHttpClient({ - method: endpoints.GetPartsOrQuestions.method, - endpoint: endpoints.GetPartsOrQuestions.url, - payload: { - carId: carId, - glassPieces: glassArrayForPayload, - zip: zipCode, - vin: vin, - serviceType: serviceType, - referralSeqNumber: referralSeqNumber, - parentAccountNumber: parentAccountNumber, - }, - logApiCall: true, - pageNameToLog: pageNameToLog, - }); + const response = await globalMethods + .callHttpClient({ + method: endpoints.GetPartsOrQuestions.method, + endpoint: endpoints.GetPartsOrQuestions.url, + payload: { + carId: carId, + glassPieces: glassArrayForPayload, + zip: zipCode, + vin: vin, + serviceType: serviceType, + referralSeqNumber: referralSeqNumber, + parentAccountNumber: parentAccountNumber, + }, + logApiCall: true, + pageNameToLog: pageNameToLog, + }) + .catch((error) => { + if (error.status == 500) { + return { PartsNotFound: true }; + } + }); + + if (response.PartsNotFound) { + return response; + } // Flatten location and name properties response.data.partsOrQuestions = convertGlassPieceNamingFromApi( @@ -3820,6 +3834,10 @@ export const actions = { context.commit(storeMutations.UPDATE_IDEMPOTENCY_KEY, newKey); } }, + + saveBailoutCode(context, bailoutCode) { + context.commit(storeMutations.UPDATE_BAILOUT_CODE, bailoutCode); + }, }; export default createStore({