diff --git a/src/global-methods.js b/src/global-methods.js index 280f318d..7f7ad72b 100644 --- a/src/global-methods.js +++ b/src/global-methods.js @@ -94,10 +94,11 @@ export default { } if (error.response.status !== 404) { - global.$logger.logError(`${method}: ${endpoint}: ${error.message}`, error.response); + const errorData = { method, url, payload, error }; + global.$logger.logError(`${method}: ${endpoint}`, errorData); if (bailoutOnError && global.bailoutOnAxiosError !== undefined) { - global.bailoutOnAxiosError({ url, error }); + global.bailoutOnAxiosError(errorData); } } return reject(error.response); diff --git a/src/global-methods.spec.js b/src/global-methods.spec.js index fbb084ff..e5e82e45 100644 --- a/src/global-methods.spec.js +++ b/src/global-methods.spec.js @@ -21,7 +21,8 @@ global.$logger = { function setupMocksForHttpClient({ endpoint = null, isError = false, - additionalData = null + additionalData = null, + bailoutOnError = false }) { // Clear node module axios.mockClear(); @@ -57,7 +58,8 @@ function setupMocksForHttpClient({ return { endpoint, - logApiCall: true + logApiCall: true, + bailoutOnError }; } @@ -92,3 +94,21 @@ it('Global Methods - Call Http Client - Should Reject Promise', () => { expect(err.status).toEqual(500); }); }); + +it('Global Methods - Call Http Client - Rejected Promised - bailoutOnError: true - Calls global.bailoutOnAxiosError', () => { + // Arrange + const endpoint = 'https://mock.safelite.com'; + const httpArgs = setupMocksForHttpClient({ + endpoint, + isError: true, + bailoutOnError: true + }); + analyticsMixIn.methods.pushEventToGA = jest.fn(); + global.bailoutOnAxiosError = jest.fn(); + + // Act + globalMethods.callHttpClient(httpArgs).catch((err) => { + // Assert + expect(global.bailoutOnAxiosError).toHaveBeenCalledTimes(1); + }); +}); \ No newline at end of file diff --git a/src/helpers/order-helper.js b/src/helpers/order-helper.js index f1ab7c72..cbc5c890 100644 --- a/src/helpers/order-helper.js +++ b/src/helpers/order-helper.js @@ -4,8 +4,8 @@ import submitType from '@/constants/submit-type'; /* Encapsulates asynchronous Save Session logic inside a promise to allow for Save Session queuing */ -async function saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA }) { - const savedSessionInfo = await store.saveSession({ submitAfterSave, createWorkOrderNumberForPIA }); +async function saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError }) { + const savedSessionInfo = await store.saveSession({ submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError }); if (savedSessionInfo) { store.setSaveSessionInfo(savedSessionInfo.data); } @@ -16,11 +16,11 @@ async function saveSessionHelper(store, { submitAfterSave, createWorkOrderNumber This will also set Referral information in the store after saving, and then update the cookie. To force synchronous behavior pass in 'true' for shouldAwaitSaveSessionQueue */ -export async function saveSession({ shouldAwaitSaveSessionQueue = false, submitAfterSave = false, createWorkOrderNumberForPIA = false }) { +export async function saveSession({ shouldAwaitSaveSessionQueue = false, submitAfterSave = false, createWorkOrderNumberForPIA = false, bailoutOnError = false }) { const store = useMainStore(); const saveSessionPromise = store.applicationUser.saveSessionPromise - ? store.applicationUser.saveSessionPromise.then(() => saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA })) - : saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA }); + ? store.applicationUser.saveSessionPromise.then(() => saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError })) + : saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError }); store.setSaveSessionPromise(saveSessionPromise); @@ -38,7 +38,8 @@ export async function submitWorkOrder({ submitType }) { store.resetSubmittedOrder(); await saveSession({ shouldAwaitSaveSessionQueue: true, - submitAfterSave: true + submitAfterSave: true, + bailoutOnError: true }); await store.createSubmittedOrder(submitType); } diff --git a/src/layouts/capability-questions/capability-questions.vue b/src/layouts/capability-questions/capability-questions.vue index c322a54b..e6dfd7bf 100644 --- a/src/layouts/capability-questions/capability-questions.vue +++ b/src/layouts/capability-questions/capability-questions.vue @@ -157,8 +157,7 @@ export default { this.navigateForward(this.partsOrQuestionsData, null); }, requestCallbackBailout() { - this.mainStore.setBailout(bailoutMessage.RequestCallback()); - this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route); + this.$router.navigateBailout(bailoutMessage.RequestCallback()); } } }; diff --git a/src/layouts/coverage-statement/coverage-statement.spec.js b/src/layouts/coverage-statement/coverage-statement.spec.js index 854a5368..a8b77aa5 100644 --- a/src/layouts/coverage-statement/coverage-statement.spec.js +++ b/src/layouts/coverage-statement/coverage-statement.spec.js @@ -63,7 +63,8 @@ const loadingModalStub = { function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}) { const mountOptions = getMountOptions({ router: { - navigate: jest.fn() + navigate: jest.fn(), + navigateBailout: jest.fn() } }); @@ -690,7 +691,7 @@ describe('coverageStatement.vue', () => { undefined ); }); - test('If Verified ITAC, selected Cancel, navigate forward w/ CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP', () => { + test('If Verified ITAC, selected Cancel, navigateBailout', () => { // Arrange const deductible = servicePrice + 1; const mainInitialState = { @@ -716,11 +717,7 @@ describe('coverageStatement.vue', () => { wrapper.vm.cancelClaim(); // Assert - expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, - undefined - ); + expect(wrapper.vm.$router.navigateBailout).toHaveBeenCalled(); }); test('If No Comp and selected Safelite, navigate forward with CLICKED_FORWARD_WITH_SAFELITE scenario', () => { // Arrange @@ -753,7 +750,7 @@ describe('coverageStatement.vue', () => { undefined ); }); - test('If No comp and selected other shop, navigate forward with CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP ', () => { + test('If No comp and selected other shop, navigateBailout ', () => { // Arrange const mainInitialState = { order: { @@ -778,11 +775,7 @@ describe('coverageStatement.vue', () => { wrapper.vm.cancelClaim(); // Assert - expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, - undefined - ); + expect(wrapper.vm.$router.navigateBailout).toHaveBeenCalled(); }); }); describe('openCancelClaimModal', () => { diff --git a/src/layouts/coverage-statement/coverage-statement.vue b/src/layouts/coverage-statement/coverage-statement.vue index 13ecac57..832d9219 100644 --- a/src/layouts/coverage-statement/coverage-statement.vue +++ b/src/layouts/coverage-statement/coverage-statement.vue @@ -397,18 +397,7 @@ export default { // We only call the ITAC pricing endpoint if we are not repair or we are NoComp if (!this.isRepair || this.mainStore.isNoComp) { - const pricingResults = await useMainStore().getITACPriceOrderItems(availableLineItems) - .catch((err) => { - useMainStore().setBailout(bailoutMessage.pricingResponseError( - availableLineItems.map((li) => li.partNumber), - { - code: err.code, - message: err.message, - data: err.data - } - )); - this.navigateWithScenario(navigationScenarios.PRICING_LOOKUP_ERROR); - }); + const pricingResults = await useMainStore().getITACPriceOrderItems(availableLineItems); this.setBaseServiceLineItems(pricingResults); } }, @@ -419,8 +408,7 @@ export default { this.mainStore.updateIsSafeliteProvider(true); this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE); } else { - this.mainStore.setBailout(bailoutMessage.coverageStatementInvalidState()); - this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE); + this.$router.navigateBailout(bailoutMessage.coverageStatementInvalidState()); } }, navigateWithScenario(scenario) { @@ -474,8 +462,7 @@ export default { }, cancelClaim() { this.mainStore.updateIsSafeliteProvider(false); - this.mainStore.setBailout(bailoutMessage.RequestCallback()); - this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP); + this.$router.navigateBailout(bailoutMessage.RequestCallback()); } } }; diff --git a/src/layouts/duplicate-check/duplicate-check.spec.js b/src/layouts/duplicate-check/duplicate-check.spec.js index 10be35e2..95346c26 100644 --- a/src/layouts/duplicate-check/duplicate-check.spec.js +++ b/src/layouts/duplicate-check/duplicate-check.spec.js @@ -357,11 +357,15 @@ describe('duplicateCheck.vue', () => { useMainStore().loadSession = jest.fn().mockImplementation(() => Promise.reject(error)); // Act - await wrapper.vm.forwardButtonAction(); // Assert + expect.assertions(2); + try { + await wrapper.vm.forwardButtonAction(); + } catch (e) { + expect(e).toMatch(error); + } expect(wrapper.vm.mainStore.loadSession).toHaveBeenCalledTimes(1); - expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1); }); test('coverageType deductible and policy vehicles returned => CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES', async () => { // Arrange diff --git a/src/layouts/duplicate-check/duplicate-check.vue b/src/layouts/duplicate-check/duplicate-check.vue index 1cac5a1e..3c4ead5f 100644 --- a/src/layouts/duplicate-check/duplicate-check.vue +++ b/src/layouts/duplicate-check/duplicate-check.vue @@ -140,18 +140,14 @@ export default { return; } - try { - const selectedReferral = - this.mainStore.applicationUser.duplicateOrders.find((o) => o.correlationId === this.selectedAnswer); + const selectedReferral = + this.mainStore.applicationUser.duplicateOrders.find((o) => o.correlationId === this.selectedAnswer); - if (selectedReferral) { - await this.mainStore.loadSession(selectedReferral); - } - } catch (err) { - console.error(`Error on loading session from duplicate check ${err}`); - } finally { - this.navigateForward(); + if (selectedReferral) { + await this.mainStore.loadSession(selectedReferral); } + + this.navigateForward(); }, navigateForward() { this.mainStore.updateDuplicateCheckVisited(true); diff --git a/src/layouts/entry-page/entry-page.vue b/src/layouts/entry-page/entry-page.vue index e9880117..47031709 100644 --- a/src/layouts/entry-page/entry-page.vue +++ b/src/layouts/entry-page/entry-page.vue @@ -34,44 +34,51 @@ export default { computed: { }, async mounted() { - const queryStringParams = this.parseQueryParms(); - - const { isAuthorized, clientData, decryptedParams } = await this.validateClientTagOnEntry(queryStringParams); - - this.unauthorized = !isAuthorized; - if (!isAuthorized) { - // Remove the loading animation if client tag validation fails so users can see the Unauthorized Access message. - showIssLoadingModal(false); - return; - } - - this.populateISSConfigValues(clientData); - try { - // Check cookie - const issCookie = getISSCookie(); - if (issCookie !== null && issCookie.VehicleMake && issCookie.VehicleModel) { - const clientParentAccountNumber = clientData.parentAccountNumber; - const cookieParentAccountNumber = issCookie.ReferralParentAccountNumber; + const queryStringParams = this.parseQueryParms(); - if (clientParentAccountNumber === cookieParentAccountNumber) { - const savedSessionTimeStamp = new Date(issCookie.SavedSessionTimeoutDate); - const isSavedSessionTimedOut = new Date(new Date().toUTCString()) > savedSessionTimeStamp; + const { isAuthorized, clientData, decryptedParams } = await this.validateClientTagOnEntry(queryStringParams); - if (!isSavedSessionTimedOut) { - this.mainStore.issConfig.enableContinueFromCookie = true; + this.unauthorized = !isAuthorized; + if (!isAuthorized) { + // Remove the loading animation if client tag validation fails so users can see the Unauthorized Access message. + showIssLoadingModal(false); + return; + } + + this.populateISSConfigValues(clientData); + + try { + // Check cookie + const issCookie = getISSCookie(); + if (issCookie !== null && issCookie.VehicleMake && issCookie.VehicleModel) { + const clientParentAccountNumber = clientData.parentAccountNumber; + const cookieParentAccountNumber = issCookie.ReferralParentAccountNumber; + + if (clientParentAccountNumber === cookieParentAccountNumber) { + const savedSessionTimeStamp = new Date(issCookie.SavedSessionTimeoutDate); + const isSavedSessionTimedOut = new Date(new Date().toUTCString()) > savedSessionTimeStamp; + + if (!isSavedSessionTimedOut) { + this.mainStore.issConfig.enableContinueFromCookie = true; + } } + } else { + updateOrCreateISSCookie(true); } - } else { + } catch { updateOrCreateISSCookie(true); } - } catch { - updateOrCreateISSCookie(true); - } - if (clientData.parameters?.length > 0) { - const finalParams = this.combineClientParameters(clientData.parameters, { ...queryStringParams, ...decryptedParams }); - this.populateStoreItemsFromParams(finalParams); + if (clientData.parameters?.length > 0) { + const finalParams = this.combineClientParameters(clientData.parameters, { ...queryStringParams, ...decryptedParams }); + this.populateStoreItemsFromParams(finalParams); + } + } catch (e) { + global.$logger.logError('[Entry Page] Client Setup Error:', e); + this.unauthorized = true; + showIssLoadingModal(false); + return; } this.mainStore.applicationUser.coverageAttempts = 0; @@ -151,37 +158,33 @@ export default { this.mainStore.issConfig.isCoverageEnabled = data.coverageEnabled; this.mainStore.issConfig.siteType = data.siteType; - try { - if (data.clientFlags) { - const clientFlags = JSON.parse(data.clientFlags); + if (data.clientFlags) { + const clientFlags = JSON.parse(data.clientFlags); - if (clientFlags.TPAEnabled) { - this.mainStore.issConfig.enableTPAFlow = true; - } - - if (clientFlags.ClientFullName != null) { - this.mainStore.issConfig.clientFullName = clientFlags.ClientFullName; - } - - if (clientFlags.ClientDisplayName != null) { - this.mainStore.issConfig.clientDisplayName = clientFlags.ClientDisplayName; - this.mainStore.issConfig.clientPossessiveName = toPossessive(clientFlags.ClientDisplayName); - } - - if (clientFlags.ClientPossessiveName != null) { - this.mainStore.issConfig.clientPossessiveName = clientFlags.ClientPossessiveName; - } - - if (clientFlags.ClaimRegistrationRequired) { - this.mainStore.issConfig.isClaimRegistrationRequired = true; - } - - if (clientFlags.EnableNoCompQuote) { - this.mainStore.issConfig.enableNoCompQuote = true; - } + if (clientFlags.TPAEnabled) { + this.mainStore.issConfig.enableTPAFlow = true; + } + + if (clientFlags.ClientFullName != null) { + this.mainStore.issConfig.clientFullName = clientFlags.ClientFullName; + } + + if (clientFlags.ClientDisplayName != null) { + this.mainStore.issConfig.clientDisplayName = clientFlags.ClientDisplayName; + this.mainStore.issConfig.clientPossessiveName = toPossessive(clientFlags.ClientDisplayName); + } + + if (clientFlags.ClientPossessiveName != null) { + this.mainStore.issConfig.clientPossessiveName = clientFlags.ClientPossessiveName; + } + + if (clientFlags.ClaimRegistrationRequired) { + this.mainStore.issConfig.isClaimRegistrationRequired = true; + } + + if (clientFlags.EnableNoCompQuote) { + this.mainStore.issConfig.enableNoCompQuote = true; } - } catch (e) { - console.error(`Error parsing client flags: ${e}`); } }, combineClientParameters(configParams, queryStringParams) { diff --git a/src/layouts/molding-questions/molding-questions.vue b/src/layouts/molding-questions/molding-questions.vue index cb0f4f04..44d6af81 100644 --- a/src/layouts/molding-questions/molding-questions.vue +++ b/src/layouts/molding-questions/molding-questions.vue @@ -148,8 +148,7 @@ export default { this.navigateForward(partsOrQuestions, null); }, requestCallbackBailout() { - this.mainStore.setBailout(bailoutMessage.RequestCallback()); - this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route); + this.$router.navigateBailout(bailoutMessage.RequestCallback()); } } }; diff --git a/src/layouts/part-questions/part-questions.vue b/src/layouts/part-questions/part-questions.vue index 1230016a..9bed6345 100644 --- a/src/layouts/part-questions/part-questions.vue +++ b/src/layouts/part-questions/part-questions.vue @@ -148,8 +148,7 @@ export default { this.navigateForward(glassPartsForStore, null); }, requestCallbackBailout() { - this.mainStore.setBailout(bailoutMessage.RequestCallback()); - this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route); + this.$router.navigateBailout(bailoutMessage.RequestCallback()); } }, }; diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index ba1f761b..676af3ef 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -260,25 +260,16 @@ export default { async forwardButtonAction() { useMainStore().savePaymentMethodChoice(this.paymentMethod); if (this.paymentMethod === paymentMethods.PAY_AT_TIME_OF_SERVICE) { - try { - await submitWorkOrder({ submitType: submitType.SAFELITE }); - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD, - this.$route - ); - } catch (error) { - useMainStore().setBailout(bailoutMessage.saveSessionError(error.data)); - this.$router.navigate( - this.navigationScenarios.SAVE_SESSION_FAILED, - this.$route, - { issPage: issPageValues.PAYMENT_METHOD } - ); - console.error(`error: response from submit work order:${error.message}`); - } + await submitWorkOrder({ submitType: submitType.SAFELITE }); + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD, + this.$route + ); } else { await saveSession({ createWorkOrderNumberForPIA: true, - shouldAwaitSaveSessionQueue: true + shouldAwaitSaveSessionQueue: true, + bailoutOnError: true }); this.$router.navigate( diff --git a/src/layouts/payment-return/payment-return.vue b/src/layouts/payment-return/payment-return.vue index 09860d17..d4ba19a4 100644 --- a/src/layouts/payment-return/payment-return.vue +++ b/src/layouts/payment-return/payment-return.vue @@ -124,13 +124,7 @@ export default { }, async saveAndSubmitWorkOrder() { // Final work order submit after returning from pay in advance. - try { - await submitWorkOrder({ submitType: submitType.SAFELITE }); - } catch (error) { - console.error(`error: response from submit work order:${error.message}`); - this.navigateOnPayInAdvanceError(); - return; - } + await submitWorkOrder({ submitType: submitType.SAFELITE }); this.$router.navigate( this.navigationScenarios.PAY_IN_ADVANCE_SUCCESS, diff --git a/src/layouts/policy-vehicles/policy-vehicles.spec.js b/src/layouts/policy-vehicles/policy-vehicles.spec.js index a4670b55..62c6b526 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.spec.js +++ b/src/layouts/policy-vehicles/policy-vehicles.spec.js @@ -252,12 +252,12 @@ describe('policy-vehicles.vue', () => { ); test( - 'Error in lookupVehicleByVin call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario.', + 'Error in lookupVehicleByVin call => error thrown in forwardButtonAction.', async () => { // Arrange const { wrapper } = setupMocks({}); const lookupReturnValue = { error: true, status: 500, data: 'error' }; - wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue(lookupReturnValue); + wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue(Promise.reject(lookupReturnValue)); const vin = getRandomString(17, 17); await wrapper.setData({ @@ -270,21 +270,14 @@ describe('policy-vehicles.vue', () => { }); // Act - wrapper.vm.mainStore.applicationUser.pageData[issPageValues.BAILOUT_PAGE] = { - 'bailout-page': { - bailoutCode: bailoutCode.VehicleVinLookupError - } - }; - await wrapper.vm.forwardButtonAction(); // Assert - expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalledWith(bailoutMessage.vehicleVinLookupError(vin, lookupReturnValue.data)); - expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - undefined, - {}, - {} - ); + expect.assertions(1); + try { + await wrapper.vm.forwardButtonAction(); + } catch (e) { + expect(e).toBe(lookupReturnValue); + } } ); @@ -292,7 +285,7 @@ describe('policy-vehicles.vue', () => { async () => { // Arrange const { wrapper } = setupMocks({}); - wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue({ error: true, status: 404 }); + wrapper.vm.lookupVehicleByVin = jest.fn().mockRejectedValue({ isAxiosError: true, status: 404 }); const vin = getRandomString(17, 17); await wrapper.setData({ diff --git a/src/layouts/policy-vehicles/policy-vehicles.vue b/src/layouts/policy-vehicles/policy-vehicles.vue index c459cefd..9cedd58d 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.vue +++ b/src/layouts/policy-vehicles/policy-vehicles.vue @@ -59,7 +59,6 @@ import vehicleSelectionOptions from '@/constants/vehicle-selection-options.js'; import endorsementOptions from '@/constants/endorsement-options.js'; import globalRules from '@/constants/global-rules.js'; import { useMainStore } from '@/store/index.js'; -import bailoutMessage from '@/constants/bailoutMessage'; import { deductibleForSelectedVehicle, endorsementsForSelectedVehicle, @@ -144,10 +143,6 @@ export default { }, repairWaivedForSelectedVehicle() { return repairWaivedForSelectedVehicle(this.selectedPolicyVehicle); - }, - selectedVehicle() { - const vehicle = this.mainStore.lookupVehicleByVin(this.selectedVehicleVin); - return vehicle; } }, watch: { @@ -156,15 +151,12 @@ export default { if (value === vehicleSelectionOptions.VEHICLE_NOT_LISTED) { // clear previously selected vehicle and image this.mainStore.resetVehicleState(); - } else { - // get vehicle details from selected VIN - const vehicle = await this.lookupVehicleByVin(value); + return; + } - // handle error in case vehicle info doesn't come back for selected VIN - if (vehicle?.error === true) { - this.mainStore.resetVehicleState(); - return; - } + // get vehicle details from selected VIN + try { + const vehicle = await this.lookupVehicleByVin(value); if (!vehicle?.data.canSafeliteService) { this.displayNoServiceAlert = true; return; @@ -177,6 +169,8 @@ export default { vin: value }); } + } catch (e) { + this.mainStore.resetVehicleState(); } } }, @@ -196,33 +190,16 @@ export default { ); }, async forwardButtonAction() { - if (this.selectedVehicleVin !== vehicleSelectionOptions.VEHICLE_NOT_LISTED) { + if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) { + useMainStore().updateCoverageType(coverageType.NONE); + useMainStore().updateCoverageStatus(coverageStatuses.NO_COVERAGE); + this.navigateForward(); + return; + } + + const vehicle = this.policyVehicles.find((pv) => pv.vin === this.selectedVehicleVin); + try { const vehicleLookupResponse = await this.lookupVehicleByVin(this.selectedVehicleVin); - const vehicle = this.policyVehicles.find((pv) => pv.vin === this.selectedVehicleVin); - - if (vehicleLookupResponse.error) { - if (vehicleLookupResponse.status === 404) { - this.mainStore.resetVehicleState(); - useMainStore().updateVehicle({ - policyVehicleId: vehicle.id, - carId: '0', - category: '', - year: vehicle.vehicleYear || '', - make: vehicle.vehicleMake || '', - model: vehicle.vehicleModel || '', - style: vehicle.vehicleStyle || '', - vin: vehicle.vin - }); - this.policyVinFound = false; - return this.navigateForward(); - } - - this.mainStore.setBailout(bailoutMessage.vehicleVinLookupError( - vehicle.vin, - vehicleLookupResponse.data - )); - return this.navigateForward(); - } useMainStore().updateVehicle({ ...vehicleLookupResponse.data, @@ -235,21 +212,30 @@ export default { repairWaived: this.repairWaivedForSelectedVehicle, endorsements: this.endorsementsForSelectedVehicle }); - } else { - useMainStore().updateCoverageType(coverageType.NONE); - useMainStore().updateCoverageStatus(coverageStatuses.NO_COVERAGE); + this.navigateForward(); + } catch (e) { + if (e.isAxiosError && e.status === 404) { + this.mainStore.resetVehicleState(); + useMainStore().updateVehicle({ + policyVehicleId: vehicle.id, + carId: '0', + category: '', + year: vehicle.vehicleYear || '', + make: vehicle.vehicleMake || '', + model: vehicle.vehicleModel || '', + style: vehicle.vehicleStyle || '', + vin: vehicle.vin + }); + this.policyVinFound = false; + this.navigateForward(); + return; + } + + throw e; } - return this.navigateForward(); }, navigateForward() { - if (this.mainStore.isBailout) { - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route, - {}, - {} - ); - } else if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) { + if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) { this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE, this.$route, @@ -281,15 +267,7 @@ export default { } }, async lookupVehicleByVin(vin) { - try { - return await useMainStore().lookupVehicleByVin(vin); - } catch (responseError) { - return { - error: true, - status: responseError.status, - data: responseError.data - }; - } + return useMainStore().lookupVehicleByVin(vin); }, async addAnotherVehicle() { this.selectedVehicleVin = vehicleSelectionOptions.VEHICLE_NOT_LISTED; diff --git a/src/layouts/provider-preference/provider-preference.spec.js b/src/layouts/provider-preference/provider-preference.spec.js index 3fdcb495..0d93ed3a 100644 --- a/src/layouts/provider-preference/provider-preference.spec.js +++ b/src/layouts/provider-preference/provider-preference.spec.js @@ -20,7 +20,8 @@ jest.mock('@/helpers/cms-content-helper', () => ({ function setupMocks(mockApiResponses) { const mountOptions = getMountOptions({ router: { - navigate: jest.fn() + navigate: jest.fn(), + navigateBailout: jest.fn() }, route: 'provider-preference' }); @@ -43,7 +44,7 @@ function setupMocks(mockApiResponses) { } describe('provider-preference.vue', () => { - test('Should navigate to tpa disabled route page when TPAOption selected and TPA Flow disabled', () => { + test('Should navigateBailout when TPAOption selected and TPA Flow disabled', () => { // Arrange const { wrapper } = setupMocks(); @@ -52,7 +53,7 @@ describe('provider-preference.vue', () => { wrapper.vm.findAnotherShopClicked(); // Test - expect(wrapper.vm.$router.navigate).toBeCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED, 'provider-preference'); + expect(wrapper.vm.$router.navigateBailout).toBeCalled(); }); test('Should navigate to safelite flow when navigateWithTPARecalAnswer is called with SafeliteOption', () => { diff --git a/src/layouts/provider-preference/provider-preference.vue b/src/layouts/provider-preference/provider-preference.vue index 4400174e..27e38ff8 100644 --- a/src/layouts/provider-preference/provider-preference.vue +++ b/src/layouts/provider-preference/provider-preference.vue @@ -186,9 +186,7 @@ export default { this.scheduleWithTPA(); } } else { - this.mainStore.setBailout(bailoutMessage.TPANotEnabled()); - const scenario = this.navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED; - this.navigateForward(scenario); + this.$router.navigateBailout(bailoutMessage.TPANotEnabled()); } }, openStateSteeringModal() { diff --git a/src/layouts/tpa-search/tpa-search.spec.js b/src/layouts/tpa-search/tpa-search.spec.js index a888e60f..20a22153 100644 --- a/src/layouts/tpa-search/tpa-search.spec.js +++ b/src/layouts/tpa-search/tpa-search.spec.js @@ -53,7 +53,8 @@ const loaderStub = { function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}) { const mountOptions = getMountOptions({ router: { - navigate: jest.fn() + navigate: jest.fn(), + navigateBailout: jest.fn() }, route: { query: { issPage: 'tpa-search' } @@ -787,7 +788,7 @@ describe('tpa-search.vue', () => { }); describe('needHelpLinkClick', () => { - test('sets bailout and navigates', () => { + test('navigatesBailout', () => { // Arrange const { wrapper } = getMountedComponent(); @@ -795,11 +796,7 @@ describe('tpa-search.vue', () => { wrapper.vm.needHelpLinkClick(); // Assert - expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalled(); - expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( - navigationScenarios.CLICKED_NEED_HELP, - expect.anything() - ); + expect(wrapper.vm.$router.navigateBailout).toBeCalled(); }); }); diff --git a/src/layouts/tpa-search/tpa-search.vue b/src/layouts/tpa-search/tpa-search.vue index 30be1bd5..bac8ccbf 100644 --- a/src/layouts/tpa-search/tpa-search.vue +++ b/src/layouts/tpa-search/tpa-search.vue @@ -360,11 +360,7 @@ export default { return getTpaProvidersResult?.data ?? []; }, needHelpLinkClick() { - this.mainStore.setBailout(bailoutMessage.RequestCallback()); - this.$router.navigate( - this.navigationScenarios.CLICKED_NEED_HELP, - this.$route - ); + this.$router.navigateBailout(bailoutMessage.RequestCallback()); }, async searchClick() { if (isNaN(this.tpaSearchValue)) { diff --git a/src/layouts/tpa-submit/tpa-submit.vue b/src/layouts/tpa-submit/tpa-submit.vue index d1a1e1ed..bd113db7 100644 --- a/src/layouts/tpa-submit/tpa-submit.vue +++ b/src/layouts/tpa-submit/tpa-submit.vue @@ -315,20 +315,8 @@ export default { }; }, async forwardButtonAction() { - try { - await submitWorkOrder({ submitType: submitType.TPA }).then(() => { - this.navigate(this.navigationScenarios.CLICKED_FORWARD); - }).catch((submitError) => { - this.mainStore.setBailout(bailoutMessage.saveSessionError(submitError.data)); - this.navigate( - this.navigationScenarios.SAVE_SESSION_FAILED, - this.$route, - { issPage: this.issPageValues.TPA_SUBMIT } - ); - }); - } catch (error) { - console.error(`error: response from submit work order:${error.message}`); - } + await submitWorkOrder({ submitType: submitType.TPA }) + this.navigate(this.navigationScenarios.CLICKED_FORWARD); }, navigate(scenario) { this.$router.navigate(scenario, this.$route); diff --git a/src/layouts/vehicle-damage/vehicle-damage.spec.js b/src/layouts/vehicle-damage/vehicle-damage.spec.js index db8ae50d..8bc26edb 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.spec.js +++ b/src/layouts/vehicle-damage/vehicle-damage.spec.js @@ -139,7 +139,7 @@ describe('vehicle-damage.vue', () => { expect(vehicleQuestionsMixin.methods.getPartsOrQuestions).toHaveBeenCalledTimes(1); expect(vehicleQuestionsMixin.methods.navigateForward).toHaveBeenCalledTimes(1); }); - test('Error in getPartsOrQuestions call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario', async () => { + test('Error in getPartsOrQuestions call => error thrown in forwardButtonAction', async () => { mountOptions.global.plugins = [createTestingPinia({ initialState: { main: { @@ -154,23 +154,21 @@ describe('vehicle-damage.vue', () => { } } })]; - mountOptions.data = () => ({ - hasBailedOut: true - }); + mountOptions.data = () => ({}); const wrapper = mount(VehicleDamageComponent, mountOptions); - const siteFooterWrapper = wrapper.getComponent({ ref: 'siteFooter' }); const partsQuestionsErrorResponse = { error: 'Error getting parts' }; vehicleQuestionsMixin.methods.getPartsOrQuestions.mockImplementation(() => ( - partsQuestionsErrorResponse + Promise.reject(partsQuestionsErrorResponse) )); - siteFooterWrapper.vm.$emit('forwardClicked'); - await flushPromises(); - expect(mockRouter.navigate).toHaveBeenCalledTimes(1); - expect(mockRouter.navigate) - .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, mockRoute); + expect.assertions(1); + try { + await wrapper.vm.forwardButtonAction(); + } catch (e) { + expect(e).toBe(partsQuestionsErrorResponse); + } }); }); diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue index b486f90e..a27e3655 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.vue +++ b/src/layouts/vehicle-damage/vehicle-damage.vue @@ -189,8 +189,7 @@ export default { this.getPassengerSideReplaceOptionsFromStore() }, selectedWindshieldOptions: this.getWindshieldOptionsFromStore(), - selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore(), - hasBailedOut: false + selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore() }; }, computed: { @@ -429,23 +428,12 @@ export default { // If vin already exists or not replacing windshield, get parts/questions and navigate forward const partsOrQuestionsResponse = await this.getPartsOrQuestions(); - if (partsOrQuestionsResponse.error) { - this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data)); - window.console.error('Error on retrieving PartsOrQuestions'); - this.hasBailedOut = true; - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - } // Comes from vehicleQuestionsMixin.navigateForward() - if (!this.hasBailedOut) { - await this.navigateForward( - partsOrQuestionsResponse.data.partsOrQuestions, - this - ); - } + await this.navigateForward( + partsOrQuestionsResponse.data.partsOrQuestions, + this + ); } else { this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN, diff --git a/src/layouts/vehicle-lookup/vehicle-lookup.vue b/src/layouts/vehicle-lookup/vehicle-lookup.vue index b322b7c8..acbf58a0 100644 --- a/src/layouts/vehicle-lookup/vehicle-lookup.vue +++ b/src/layouts/vehicle-lookup/vehicle-lookup.vue @@ -113,21 +113,10 @@ export default { break; case vinLookupMethodSelections.NOVIN: const partsOrQuestionsResponse = await this.getPartsOrQuestions(); - if (partsOrQuestionsResponse.error) { - this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data)); - window.console.error('Error on retrieving PartsOrQuestions'); - this.hasBailedOut = true; - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - } - else { - await this.navigateForward( - partsOrQuestionsResponse.data.partsOrQuestions, - this - ); - } + await this.navigateForward( + partsOrQuestionsResponse.data.partsOrQuestions, + this + ); break; default: break; diff --git a/src/layouts/vehicle-parts/vehicle-parts.vue b/src/layouts/vehicle-parts/vehicle-parts.vue index aa2bba0c..a49e8be4 100644 --- a/src/layouts/vehicle-parts/vehicle-parts.vue +++ b/src/layouts/vehicle-parts/vehicle-parts.vue @@ -65,7 +65,6 @@ import BaseFormMixin from '@/mixins/base-form-mixin.js'; import { useMainStore } from '@/store'; import widgetFields from '@/constants/cms-widget-fields'; import bailoutMessage from '@/constants/bailoutMessage'; -import navigationScenarios from '@/router/router-constants/navigation-scenarios'; export default { name: 'vehicle-parts', @@ -215,8 +214,7 @@ export default { }); }, requestCallbackBailout() { - this.mainStore.setBailout(bailoutMessage.RequestCallback()); - this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route); + this.$router.navigateBailout(bailoutMessage.RequestCallback()); } } }; diff --git a/src/layouts/vehicle-selection/vehicle-selection.vue b/src/layouts/vehicle-selection/vehicle-selection.vue index 1f56fec3..e489fe0d 100644 --- a/src/layouts/vehicle-selection/vehicle-selection.vue +++ b/src/layouts/vehicle-selection/vehicle-selection.vue @@ -163,16 +163,18 @@ export default { } }, selectedStyle(value) { - this.resetAlert(); - this.mainStore.updateVehicleStyle(value); - this.mainStore.setVehicle( - this.selectedYear, - this.selectedMake, - this.selectedModel, - this.selectedStyle - ).then((result) => { - this.displayNoServiceAlert = !result.data.canSafeliteService; - }); + if (value) { + this.resetAlert(); + this.mainStore.updateVehicleStyle(value); + this.mainStore.setVehicle( + this.selectedYear, + this.selectedMake, + this.selectedModel, + this.selectedStyle + ).then((result) => { + this.displayNoServiceAlert = !result.data.canSafeliteService; + }); + } } }, mounted() { @@ -206,77 +208,25 @@ export default { }, navigateForward() { - this.mainStore.setVehicle().then( - () => { - if (this.mainStore.isBailout) { - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - return; - } - + this.mainStore.setVehicle().then(() => { this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD, this.$route ); - }, - (error) => { - this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, this.mainStore.vehicle.model, this.mainStore.vehicle.style, error)); - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); } ); }, async updateYearValues() { - return this.mainStore.getVehicleYears().then( - (response) => response, - (error) => { - this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(null, null, null, null, error)); - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - } - ); + return this.mainStore.getVehicleYears(); }, async updateMakeValues() { - return this.mainStore.getVehicleMakes().then( - (response) => response, - (error) => { - this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, null, null, null, error)); - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - } - ); + return this.mainStore.getVehicleMakes(); }, async updateModelValues() { - return this.mainStore.getVehicleModels().then( - (response) => response, - (error) => { - this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, null, null, error)); - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - } - ); + return this.mainStore.getVehicleModels(); }, async updateStyleValues() { - return this.mainStore.getVehicleStyles().then( - (response) => response, - (error) => { - this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, this.mainStore.vehicle.model, null, error)); - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - } - ); + return this.mainStore.getVehicleStyles(); }, resetAlert() { this.displayNoServiceAlert = false; diff --git a/src/layouts/vin-lookup/vin-lookup.spec.js b/src/layouts/vin-lookup/vin-lookup.spec.js index 0376eaa3..b5808d77 100644 --- a/src/layouts/vin-lookup/vin-lookup.spec.js +++ b/src/layouts/vin-lookup/vin-lookup.spec.js @@ -148,7 +148,8 @@ const mockRoute = { }; const mockRouter = { navigate: jest.fn(), - navigateWithSpinner: jest.fn() + navigateWithSpinner: jest.fn(), + navigateBailout: jest.fn() }; const maska = jest.fn(); jest.mock('@/helpers/damage-helper', () => ({ @@ -553,45 +554,6 @@ describe('vin-lookup.vue', () => { ); }); }); - test( - 'Error in getPartsOrQuestions call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario', - async () => { - const user = userEvent.setup(); - mountOptions.global.stubs.vinQuestion = false; - - mountOptions.data = () => ({ - vinWithNonMatchingCarId: false, - isCarIdDifferentFromTheStore: false, - vin: mockValidVin, - hasBailedOut: true - }); - - getPartsOrQuestions.mockResponse = partsOrQuestionsErrorMockResponse; - - jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName) - .mockResolvedValue(lookupVehicleByVin.mockResponse); - jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName) - .mockResolvedValue(getPartsOrQuestions.mockResponse); - - const { container } = render(VinLookupComponent, mountOptions); - - const vinInput = container.querySelector(vinInputSelector); - await user.type(vinInput, mockValidVin); - - const continueButton = container.querySelector(continueButtonQuerySelector); - await user.click(continueButton); - - await flushPromises(); - await waitFor(() => { - expect(mockRouter.navigate).toHaveBeenCalledTimes(1); - expect(mockRouter.navigate) - .toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - mockRoute - ); - }); - } - ); }); }); }); diff --git a/src/layouts/vin-lookup/vin-lookup.vue b/src/layouts/vin-lookup/vin-lookup.vue index 3bb0d637..05582873 100644 --- a/src/layouts/vin-lookup/vin-lookup.vue +++ b/src/layouts/vin-lookup/vin-lookup.vue @@ -112,8 +112,7 @@ export default { vinWithNonMatchingCarId: vin?.length > 0 && !this.hasValidCarId(), vin, forwardButtonCarStyle: '', - vinPopulatedOnPageLoad: vin?.length > 0 && this.hasValidCarId(), - hasBailedOut: false + vinPopulatedOnPageLoad: vin?.length > 0 && this.hasValidCarId() }; }, computed: { @@ -176,35 +175,37 @@ export default { showIssLoadingModal(true); if (this.needToLookupVehicle) { - const vehicleLookupResponse = await this.lookupVehicleByVin(this.vin); + try { + const vehicleLookupResponse = await this.lookupVehicleByVin(this.vin); - if (vehicleLookupResponse.error) { - this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_FOUND; - this.mainStore.setBailout(bailoutMessage.vehicleNotFound(this.vin)); - this.resetVehicleFromLookup(); - // Temp solution to turn on 'disabled' style on the Continue button - // because the form itself actually passes its client-side validation. - // SSR-189 Scenario #4. - this.$refs.siteFooter.enableForwardAction(); - showIssLoadingModal(false); - return; - } - - if (!vehicleLookupResponse.data.canSafeliteService) { - this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NO_SERVICE; - this.resetVehicleFromLookup(); - this.$refs.siteFooter.disableForwardButton(); - showIssLoadingModal(false); - return; - } - - // Add vin bcs the response from the service doesn't contain vin - this.vehicleFromLookup = Object.assign( - vehicleLookupResponse.data, - { - vin: this.vin + if (!vehicleLookupResponse.data.canSafeliteService) { + this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NO_SERVICE; + this.resetVehicleFromLookup(); + this.$refs.siteFooter.disableForwardButton(); + showIssLoadingModal(false); + return; } - ); + + // Add vin bcs the response from the service doesn't contain vin + this.vehicleFromLookup = Object.assign( + vehicleLookupResponse.data, + { + vin: this.vin + } + ); + } catch (e) { + if (e.status === 404) { + this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_FOUND; + this.resetVehicleFromLookup(); + // Temp solution to turn on 'disabled' style on the Continue button + // because the form itself actually passes its client-side validation. + // SSR-189 Scenario #4. + this.$refs.siteFooter.enableForwardAction(); + showIssLoadingModal(false); + return; + } + throw e; + } } if (this.needToLookupVehicle && this.isCarIdDifferentFromTheStore) { @@ -259,34 +260,15 @@ export default { } const partsOrQuestionsResponse = await this.getPartsOrQuestions(); - if (partsOrQuestionsResponse.error) { - this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data)); - window.console.error('Error on retrieving PartsOrQuestions'); - this.hasBailedOut = true; - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); - } // Comes from vehicleQuestionsMixin.navigateForward() - if (!this.hasBailedOut) { - await this.navigateForward( - partsOrQuestionsResponse.data.partsOrQuestions, - this - ); - } + await this.navigateForward( + partsOrQuestionsResponse.data.partsOrQuestions, + this + ); }, async lookupVehicleByVin(vin) { - try { - return await this.mainStore.lookupVehicleByVin(vin); - } catch (responseError) { - return { - error: { - status: responseError.status - } - }; - } + return this.mainStore.lookupVehicleByVin(vin); }, resetActiveAlert() { this.activeVehicleLookupAlertType = null; diff --git a/src/layouts/welcome-page/welcome-page.spec.js b/src/layouts/welcome-page/welcome-page.spec.js index 4748a9b0..39fd8284 100644 --- a/src/layouts/welcome-page/welcome-page.spec.js +++ b/src/layouts/welcome-page/welcome-page.spec.js @@ -72,7 +72,8 @@ function setupMocks({ const mockDataMountOptions = { ...mountOptionsMockData, router: { - navigate: jest.fn() + navigate: jest.fn(), + navigateBailout: jest.fn() } }; @@ -166,7 +167,7 @@ describe('navigation', () => { const { wrapper } = setupMocks({}); wrapper.vm.mainStore.getDuplicateReferrals = jest.fn().mockImplementation(() => Promise.resolve({})); wrapper.vm.mainStore.applicationUser.duplicateOrders = [{ test: 'a' }]; - wrapper.vm.mainStore.validateZip = jest.fn().mockImplementation(() => Promise.reject()); + wrapper.vm.mainStore.validateZip = jest.fn().mockImplementation(() => Promise.reject({ isAxiosError: false })); // Act await wrapper.vm.forwardButtonAction(); @@ -316,10 +317,14 @@ describe('navigation', () => { })); // Act - await wrapper.vm.forwardButtonAction(); // Assert - expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); + expect.assertions(1); + try { + await wrapper.vm.forwardButtonAction(); + } catch (e) { + expect(e).toMatch(error); + } }); }); }); diff --git a/src/layouts/welcome-page/welcome-page.vue b/src/layouts/welcome-page/welcome-page.vue index 6520a930..15e96929 100644 --- a/src/layouts/welcome-page/welcome-page.vue +++ b/src/layouts/welcome-page/welcome-page.vue @@ -177,6 +177,7 @@ import { getPropertyCaseInsensitive } from '@/helpers/object-helper'; import { saveSession } from '@/helpers/order-helper'; import { getISSCookie } from '@/helpers/cookie-helper.js'; import bailoutMessage from '@/constants/bailoutMessage'; +import showIssLoadingModal from '@/helpers/loading-modal-helper'; // define validation rules defineRule( @@ -303,54 +304,46 @@ export default { }, methods: { async forwardButtonAction() { - try { - this.mainStore.updatePolicyData(this.welcomePageModel); - const promises = []; - promises.push(this.configureZip().then(async () => await this.mainStore.getBillToInfo())); + this.mainStore.updatePolicyData(this.welcomePageModel); - // Clear duplicate orders if navigating away from the welcome page after visiting duplicate check page. - if (this.mainStore.order.visitedDuplicateCheckPage) { - this.mainStore.clearDuplicateOrders(); - } - - // Skip duplicate check if loaded from cookie or already visited duplicate check page. - if (!this.mainStore.order.loadedFromCookie && !this.mainStore.order.visitedDuplicateCheckPage) { - promises.push(this.mainStore.getDuplicateReferrals()); - } - promises.push(this.mainStore.getCoveragePolicyInfo()); - await Promise.allSettled(promises); - } catch (e) { - console.error(e); - // TODO: Bailout? - } finally { - if (!this.displayInvalidZipAlert) { - await saveSession({ shouldAwaitSaveSessionQueue: true }) - .catch((error) => { - this.mainStore.setBailout(bailoutMessage.saveSessionError(error.data)); - }) - .finally(() => this.navigateForward()); - } + // We want to await this separately. It's quick and if this has an invalid zip we don't want to be waiting on the slower API calls + await this.configureZip(); + if (this.displayInvalidZipAlert) { + showIssLoadingModal(false); + return; } + + // Clear duplicate orders if navigating away from the welcome page after visiting duplicate check page. + if (this.mainStore.order.visitedDuplicateCheckPage) { + this.mainStore.clearDuplicateOrders(); + } + + const promises = [ + // Suppress error from API call we can try again later in the flow + this.mainStore.getBillToInfo() + ]; + // Skip duplicate check if loaded from cookie or already visited duplicate check page. + if (!this.mainStore.order.loadedFromCookie && !this.mainStore.order.visitedDuplicateCheckPage) { + promises.push(this.mainStore.getDuplicateReferrals()); + } + promises.push(this.mainStore.getCoveragePolicyInfo()); + await Promise.all(promises); + await saveSession({ shouldAwaitSaveSessionQueue: true, bailoutOnError: true }) + this.navigateForward(); }, async configureZip() { try { this.displayInvalidZipAlert = false; await this.mainStore.validateZip({ zip: this.welcomePageModel.policyZipCode }); - return Promise.resolve(); } catch (e) { + if (e.isAxiosError) { + throw e; + } this.displayInvalidZipAlert = true; - return Promise.reject(e); } }, navigateForward() { - if (this.mainStore.isBailout) { - this.$router.navigate( - this.navigationScenarios.SAVE_SESSION_FAILED, - this.$route, - {}, - { [routerParams.SKIP_SAVE_SESSION]: true } - ); - } else if ((this.mainStore.applicationUser.duplicateOrders?.length > 0 ?? false) + if ((this.mainStore.applicationUser.duplicateOrders?.length > 0 ?? false) && !this.answeredContinueModal) { this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD_WITH_DUPLICATES, @@ -445,12 +438,7 @@ export default { .then((response) => { this.answeredContinueModal = true; if (response && !response.provider?.isSafeliteProvider) { - this.mainStore.setBailout(bailoutMessage.SafeliteNotTheProvider()); - this.hasBailedOut = true; - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route - ); + this.$router.navigateBailout(bailoutMessage.SafeliteNotTheProvider()); } else { this.updateWelcomePageModel(response); } diff --git a/src/main.js b/src/main.js index 243335b9..440bcaca 100644 --- a/src/main.js +++ b/src/main.js @@ -48,7 +48,7 @@ function getPageName(vm) { // Vue Error Handling vueApp.config.errorHandler = (err, vm, info) => { const pageName = getPageName(vm); - global.$logger.logError(`Page Name - ${pageName} - ${info}: ${err.message}\n${err.stack}`); + global.$logger.logError(`[${pageName}] ${info}`, err); if (applicationConfig.BAILOUT_ON_APPLICATION_ERROR) { router.navigateBailout(bailoutMessage.applicationError(`[${pageName}] ${info}: ${err.message}\n${err.stack}`)); } @@ -56,7 +56,7 @@ vueApp.config.errorHandler = (err, vm, info) => { // Vue Router Error Handling router.onError((err) => { - global.$logger.logError(err.message, err.cause); + global.$logger.logError('Router Error:', err); if (applicationConfig.BAILOUT_ON_ROUTER_ERROR) { router.navigateBailout(bailoutMessage.routerError(`${err.message}\n${err.stack}`)); } diff --git a/src/mixins/vehicle-questions-mixin.js b/src/mixins/vehicle-questions-mixin.js index d5c492d8..6efed9ee 100644 --- a/src/mixins/vehicle-questions-mixin.js +++ b/src/mixins/vehicle-questions-mixin.js @@ -353,16 +353,7 @@ export default { }, async getPartsOrQuestions() { - try { - return await useMainStore().getPartsOrQuestions(); - } catch (responseError) { - return { - error: { - status: responseError.status, - data: responseError.data - } - }; - } + return useMainStore().getPartsOrQuestions(); }, // Can't use `this` because navigateForward is also called from vin-pages-mixin diff --git a/src/router/index.js b/src/router/index.js index cd77544f..c6a18493 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -25,100 +25,95 @@ const routes = [ path: '/', name: 'root', async beforeEnter(to, from, next) { - try { - const issPageToUse = !to.query.issPage ? issPageValues.WELCOME_PAGE : to.query.issPage; - const fromQueryPage = from.query?.issPage; + const issPageToUse = !to.query.issPage ? issPageValues.WELCOME_PAGE : to.query.issPage; + const fromQueryPage = from.query?.issPage; - if ((issPageToUse === issPageValues.ACCESS_DENIED + if ((issPageToUse === issPageValues.ACCESS_DENIED || (issPageToUse !== issPageValues.ENTRY_PAGE && !useMainStore().issConfig.parentAccountNumber)) - && process.env.VUE_APP_CURRENT_ENVIRONMENT !== 'Localhost' - ) { - return await GoToAccessIsDenied(next); + && process.env.VUE_APP_CURRENT_ENVIRONMENT !== 'Localhost' + ) { + return await GoToAccessIsDenied(next); + } + + // Do not run these for the main entry page - as it is not part of the user flow. + if (issPageToUse !== issPageValues.ENTRY_PAGE) { + if (analyticsMixin.methods.noSession()) { + await analyticsMixin.methods.initSession(); + } else { + updateSessionIdCookie(); } - // Do not run these for the main entry page - as it is not part of the user flow. - if (issPageToUse !== issPageValues.ENTRY_PAGE) { - if (analyticsMixin.methods.noSession()) { - await analyticsMixin.methods.initSession(); - } else { - updateSessionIdCookie(); - } + await runExperiments(issPageToUse); // fmg has this further down + } - await runExperiments(issPageToUse); // fmg has this further down + // Intercept all navigation if a submitted order exists in storage + if (useMainStore().hasSubmittedOrder()) { + if (to.query.issPage !== issPageValues.ENTRY_PAGE) { + return await GoToConfirmationPage(next, useMainStore().getSubmittedOrder()); + } + } + + // If the saved session has timed out, clear the session, execute 404 logic. + if (getISSCookie() !== null && !isSavedSessionStillActive()) { + // await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE); + await GoToStartOn404(next); + } + + // Process ISS cookie. + // Skip if Entry Page or Refreshing Welcome page + if (issPageToUse !== issPageValues.ENTRY_PAGE + && !(issPageToUse === issPageValues.WELCOME_PAGE && fromQueryPage === undefined)) { + updateOrCreateISSCookie(); + } + + if (router.hasRoute(issPageToUse)) { + // Since our route is already in scope, we can grab the component and call the arePagePrerequisitesValid function. + let component = router.getRoutes().filter((x) => x.name === issPageToUse)[0].components; + + // If the component hasn't been loaded fully, load it before we check prerequisites. + if (component.default.methods === undefined) { + component = await component.default(); } - // Intercept all navigation if a submitted order exists in storage - if (useMainStore().hasSubmittedOrder()) { - if (to.query.issPage !== issPageValues.ENTRY_PAGE) { - return await GoToConfirmationPage(next, useMainStore().getSubmittedOrder()); - } - } - - // If the saved session has timed out, clear the session, execute 404 logic. - if (getISSCookie() !== null && !isSavedSessionStillActive()) { - // await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE); + if (!arePagePrerequisitesValid(component)) { await GoToStartOn404(next); } - // Process ISS cookie. - // Skip if Entry Page or Refreshing Welcome page - if (issPageToUse !== issPageValues.ENTRY_PAGE - && !(issPageToUse === issPageValues.WELCOME_PAGE && fromQueryPage === undefined)) { - updateOrCreateISSCookie(); - } - - if (router.hasRoute(issPageToUse)) { - // Since our route is already in scope, we can grab the component and call the arePagePrerequisitesValid function. - let component = router.getRoutes().filter((x) => x.name === issPageToUse)[0].components; - - // If the component hasn't been loaded fully, load it before we check prerequisites. - if (component.default.methods === undefined) { - component = await component.default(); - } - - if (!arePagePrerequisitesValid(component)) { - await GoToStartOn404(next); - } - - return next({ name: issPageToUse, query: to.query, params: to.params }); - } - - const routeData = await GetRouteInfoFromPageName(issPageToUse); - - if (routeData[0].name.toLowerCase() === 'error') { - throw new Error('Page not found!'); - } - - // Add our dynamic route. - router.addRoute({ - path: routeData[0].path, // Always the same path, because we control it with query strings. - name: routeData[0].name, - component: routeData[0].component - }); - - // Call the next components arePagePrerequisitesValid method before load. - // If it returns false, use the 404 logic. - const nextComponent = await router - .getRoutes() - .filter((x) => x.name === routeData[0].name)[0] - .components.default(); - - if (!arePagePrerequisitesValid(nextComponent)) { - const tempMsgCopy = 'Pre Requisites failed, need to handle. Default is Welcome Page.'; - const tempMsgHeadline = `${issPageToUse}: pre-req failed...`; - await GoToStartOn404(next, tempMsgCopy, tempMsgHeadline); - } - - // 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 - }); - } catch (error) { - window.console.warn(error); - await GoToStartOn404(next); + return next({name: issPageToUse, query: to.query, params: to.params}); } + + const routeData = await GetRouteInfoFromPageName(issPageToUse); + + if (routeData[0].name.toLowerCase() === 'error') { + throw new Error('Page not found!'); + } + + // Add our dynamic route. + router.addRoute({ + path: routeData[0].path, // Always the same path, because we control it with query strings. + name: routeData[0].name, + component: routeData[0].component + }); + + // Call the next components arePagePrerequisitesValid method before load. + // If it returns false, use the 404 logic. + const nextComponent = await router + .getRoutes() + .filter((x) => x.name === routeData[0].name)[0] + .components.default(); + + if (!arePagePrerequisitesValid(nextComponent)) { + const tempMsgCopy = 'Pre Requisites failed, need to handle. Default is Welcome Page.'; + const tempMsgHeadline = `${issPageToUse}: pre-req failed...`; + await GoToStartOn404(next, tempMsgCopy, tempMsgHeadline); + } + + // 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 + }); return null; } } @@ -177,7 +172,7 @@ router.afterEach(async (to, from) => { const skipSaveSession = !!router.options.history.state[routerParams.SKIP_SAVE_SESSION]; if (from.name !== undefined && !skipSaveSession && !store.hasSubmittedOrder()) { - await saveSession({}); + await saveSession({ bailoutOnError: from.name === issPageValues.ENTRY_PAGE}); } if (to.query.issPage !== issPageValues.ENTRY_PAGE) { @@ -350,17 +345,12 @@ router.navigateBailout = (bailoutData = null) => { // Get navigation map depending on the scenario and the current 'page' you're on. function getNavigationMap(scenario, currentRoute) { const issPageValue = currentRoute.query.issPage; - try { - const matchedQueryValue = routingTable(useMainStore()) - .filter((item) => item.issPageValue === issPageValue && item.maps.filter((map) => map.scenario === scenario).length > 0); + const matchedQueryValue = routingTable(useMainStore()) + .filter((item) => item.issPageValue === issPageValue && item.maps.filter((map) => map.scenario === scenario).length > 0); - const maps = matchedQueryValue ? matchedQueryValue.map((m) => m.maps.filter((map) => map.scenario === scenario))[0] : undefined; + const maps = matchedQueryValue ? matchedQueryValue.map((m) => m.maps.filter((map) => map.scenario === scenario))[0] : undefined; - return maps ? maps.filter((x) => x.filter === true || x.filter === undefined)[0] : undefined; - } catch (e) { - window.console.error(e); - return undefined; - } + return maps ? maps.filter((x) => x.filter === true || x.filter === undefined)[0] : undefined; } async function GoToAccessIsDenied(next) { diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js index f0603b96..07a012ed 100644 --- a/src/router/router-constants/navigation-scenarios.js +++ b/src/router/router-constants/navigation-scenarios.js @@ -12,7 +12,6 @@ const navigationScenarios = Object.freeze({ CLICKED_FORWARD_POLICY_UNVERIFIED: 'CLICKED_FORWARD_POLICY_UNVERIFIED', CLICKED_FORWARD_POLICY_VERIFIED_NO_VEHICLES: 'CLICKED_FORWARD_POLICY_VERIFIED_NO_VEHICLES', CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES: 'CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES', - SAVE_SESSION_FAILED: 'SAVE_SESSION_FAILED', // Duplicate Check CLICKED_FORWARD_LOADED_DUPLICATE_WITH_POLICY_VEHICLE: 'CLICKED_FORWARD_LOADED_DUPLICATE_WITH_POLICY_VEHICLE', @@ -70,11 +69,8 @@ const navigationScenarios = Object.freeze({ // Coverage Statement CLICKED_BACK_WITH_REPAIR: 'CLICKED_BACK_WITH_REPAIR', - CLICKED_FORWARD_WITH_INVALID_STATE: 'CLICKED_FORWARD_WITH_INVALID_STATE', - PRICING_LOOKUP_ERROR: 'PRICING_LOOKUP_ERROR', // TPA Search - CLICKED_NEED_HELP: 'CLICKED_NEED_HELP', CLICKED_FORWARD_WITH_SAFELITE_SHOP: 'CLICKED_FORWARD_WITH_SAFELITE_SHOP', CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP: 'CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP', @@ -88,7 +84,6 @@ const navigationScenarios = Object.freeze({ // Provider Preference CLICKED_FORWARD_WITH_SAFELITE: 'CLICKED_FORWARD_WITH_SAFELITE', CLICKED_FORWARD_WITH_TPA_ENABLED: 'CLICKED_FORWARD_WITH_TPA_ENABLED', - CLICKED_FORWARD_WITH_TPA_DISABLED: 'CLICKED_FORWARD_WITH_TPA_DISABLED', CLICKED_FORWARD_WITH_POLICY_AND_VEHICLES: 'CLICKED_FORWARD_WITH_POLICY_AND_VEHICLES', // Service Package @@ -112,8 +107,6 @@ const navigationScenarios = Object.freeze({ PAY_IN_ADVANCE_SUCCESS: 'PAY_IN_ADVANCE_SUCCESS', // Bailout - CLICKED_FORWARD_WITH_BAILOUT: 'CLICKED_FORWARD_WITH_BAILOUT', - CLICKED_NEED_HELP_WITH_BAILOUT: 'CLICKED_NEED_HELP_WITH_BAILOUT', BAILOUT: 'BAILOUT' }); diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js index 3751060c..7911b5c6 100644 --- a/src/router/router-constants/routing-table.js +++ b/src/router/router-constants/routing-table.js @@ -14,10 +14,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD, destinationIssPageValue: issPageValues.POLICY_HOLDER_DETAILS - }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -55,10 +51,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN, destinationIssPageValue: issPageValues.VEHICLE_LOOKUP - }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -141,14 +133,6 @@ const routingTable = () => [ { scenario: issPageValues.VEHICLE_LOOKUP, destinationIssPageValue: issPageValues.VEHICLE_LOOKUP - }, - { - scenario: issPageValues.BAILOUT_PAGE, // TODO is this a bug - destinationIssPageValue: issPageValues.BAILOUT_PAGE - }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -215,10 +199,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS, destinationIssPageValue: issPageValues.COVERAGE_STATEMENT - }, - { - scenario: navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -256,10 +236,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS, destinationIssPageValue: issPageValues.COVERAGE_STATEMENT - }, - { - scenario: navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -293,10 +269,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS, destinationIssPageValue: issPageValues.COVERAGE_STATEMENT - }, - { - scenario: navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -455,14 +427,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES, destinationIssPageValue: issPageValues.POLICY_VEHICLES - }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE - }, - { - scenario: navigationScenarios.SAVE_SESSION_FAILED, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -539,10 +503,6 @@ const routingTable = () => [ scenario: navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND, destinationIssPageValue: issPageValues.VIN_LOOKUP }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - destinationIssPageValue: issPageValues.BAILOUT_PAGE - }, { scenario: navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS, destinationIssPageValue: issPageValues.POLICY_ENDORSEMENTS @@ -591,18 +551,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE, destinationIssPageValue: issPageValues.SCHEDULE_PAGE - }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, - destinationIssPageValue: issPageValues.BAILOUT_PAGE - }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE, - destinationIssPageValue: issPageValues.BAILOUT_PAGE - }, - { - scenario: navigationScenarios.PRICING_LOOKUP_ERROR, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -620,10 +568,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITH_TPA_ENABLED, destinationIssPageValue: issPageValues.TPA_SEARCH - }, - { - scenario: navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -692,10 +636,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_PAY_NOW, destinationIssPageValue: issPageValues.PAYMENT_PAGE - }, - { - scenario: navigationScenarios.SAVE_SESSION_FAILED, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -768,10 +708,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD, destinationIssPageValue: issPageValues.TPA_CONFIRMATION - }, - { - scenario: navigationScenarios.SAVE_SESSION_FAILED, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, @@ -798,10 +734,6 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, destinationIssPageValue: issPageValues.TPA_SUBMIT - }, - { - scenario: navigationScenarios.CLICKED_NEED_HELP, - destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, diff --git a/src/router/router.spec.js b/src/router/router.spec.js index ee35151a..e9a4bd6c 100644 --- a/src/router/router.spec.js +++ b/src/router/router.spec.js @@ -4,6 +4,8 @@ import issPageValues from '@/router/router-constants/issPage-values'; import { createApp } from 'vue'; import { createPinia } from 'pinia'; import App from '@/App.vue'; +import bailoutMessage from '@/constants/bailoutMessage'; +import { useMainStore } from '@/store'; describe('Router', () => { beforeAll(() => { @@ -37,18 +39,15 @@ describe('Router', () => { expect(router.push.mock.calls[0][0].state).toBe(parameters); }); - it('Should route from CAPABILITY_QUESTIONS to BAILOUT_PAGE on CLICKED_NEED_HELP_WITH_BAILOUT', () => { - const scenario = navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT; - const currentRoute = { query: { issPage: issPageValues.CAPABILITY_QUESTIONS } }; - + it('Should set bailout and navigate to bailout page when calling navigateBailout', () => { + // Arrange router.push = jest.fn(); // Act - router.navigate(scenario, currentRoute); + router.navigateBailout(bailoutMessage.unknown({})); // Assert - expect(router.push).toHaveBeenCalled(); - expect(router.push.mock.calls[0][0].query.issPage).toBe(issPageValues.BAILOUT_PAGE); + expect(useMainStore().isBailout).toBeTruthy(); }); }); diff --git a/src/store/index.js b/src/store/index.js index bff40a1b..b23ca103 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,7 +1,6 @@ import { defineStore } from 'pinia'; import applicationConfig from '@/constants/application-config'; import bailoutCode from '@/constants/bailoutCode'; -import bailoutMessage from '@/constants/bailoutMessage'; import coverageStatuses from '@/constants/coverage-statuses'; import coverageType from '@/constants/coverage-type'; import damageLocationsSelected from '@/constants/damage-locations-selected'; @@ -22,14 +21,11 @@ import { noCoverageForSelectedVehicle, repairWaivedForSelectedVehicle } from '@/helpers/policy-vehicle-helper'; -import { - buildURLSearchParams, - getPartNumbersListForQueryString, - getTaxLineItemQueryString -} from '@/helpers/querystring-helper'; +import { buildURLSearchParams, getPartNumbersListForQueryString } from '@/helpers/querystring-helper'; import { getTopLevelGlassPartsWithRecal } from '@/helpers/recal-helper'; import { getDateForSavedSessionTimeout } from '@/helpers/session-helper'; import issPageValues from '@/router/router-constants/issPage-values'; +import CoverageStatuses from '@/constants/coverage-statuses'; const storeId = 'main'; @@ -533,20 +529,15 @@ export const useMainStore = defineStore({ payload: {} }); }, - getIsVinbyAddressPermissible() { + async getIsVinbyAddressPermissible() { try { - const response = globalMethods.callHttpClient({ + return globalMethods.callHttpClient({ method: endpoints.IsVinbyAddressPermissible.method, endpoint: `${endpoints.IsVinbyAddressPermissible.url}?state=${this.order.customer.address.state}`, payload: {} - }); - return response; - } catch (responseError) { - return { - error: { - status: responseError.status - } - }; + }); + } catch (e) { + return false; } }, async getCoveragePolicyInfo() { @@ -555,7 +546,7 @@ export const useMainStore = defineStore({ if (!issConfig.isCoverageEnabled || applicationUser.coverageLookupAttempts > 10) { this.updateCoverageType(coverageType.NONE); - return Promise.resolve(); + return; } this.applicationUser.coverageAttempts += 1; @@ -571,7 +562,8 @@ export const useMainStore = defineStore({ dateOfLoss: policy.dateOfLoss, zipCode: policy.policyZipCode, referralCorrelationId: order.referralCorrelationId - } + }, + bailoutOnError: false }); const responsePolicy = response?.data?.policies?.[0]; @@ -600,10 +592,8 @@ export const useMainStore = defineStore({ } else { this.updateCoverageType(coverageType.NONE); } - return Promise.resolve(); } catch (e) { this.updateCoverageType(coverageType.NONE); - return Promise.reject(e); } }, clearDuplicateOrders() { @@ -621,82 +611,81 @@ export const useMainStore = defineStore({ updateIsItacOptimized(isItacOptimized) { this.order.insuranceCoverage.isItacOptimized = isItacOptimized || false; }, - registerClaim() { + async registerClaim() { const nonNumberCharRegex = /[^0-9]/g; - const { order, isITAC } = this; - return new Promise((resolve, reject) => { - globalMethods.callHttpClient({ + const { isITAC } = this; + + try { + const response = await globalMethods.callHttpClient({ method: endpoints.RegisterClaim.method, endpoint: endpoints.RegisterClaim.url, payload: - { - referralCorrelationId: this.order.referralCorrelationId, - accountNumber: this.order.parentAccountNumber?.toString() ?? '', - policyData: this.order.policy.policyData, - isItac: isITAC, - insured: { - firstName: this.order.customer.firstName, - lastName: this.order.customer.lastName, - address: { - addressLine1: this.order.customer.address.streetAddress, - addressLine2: this.order.customer.address.streetAddress2, - city: this.order.customer.address.city, - state: this.order.customer.address.state, - zipCode: this.order.customer.address.zipCode, - country: 'US' // TODO set from store + { + referralCorrelationId: this.order.referralCorrelationId, + accountNumber: this.order.parentAccountNumber?.toString() ?? '', + policyData: this.order.policy.policyData, + isItac: isITAC, + insured: { + firstName: this.order.customer.firstName, + lastName: this.order.customer.lastName, + address: { + addressLine1: this.order.customer.address.streetAddress, + addressLine2: this.order.customer.address.streetAddress2, + city: this.order.customer.address.city, + state: this.order.customer.address.state, + zipCode: this.order.customer.address.zipCode, + country: 'US' // TODO set from store + }, + homePhone: { + number: this.contactInfo.homePhone?.replaceAll(nonNumberCharRegex, '') ?? '' + } }, - homePhone: { - number: this.contactInfo.homePhone?.replaceAll(nonNumberCharRegex, '') ?? '' + driver: { + firstName: this.order.customer.firstName, + lastName: this.order.customer.lastName + }, + caller: { + homePhone: {} + }, + policyInfo: { + policyNumber: this.order.policy.policyNumber, + safelitePolicy: { + policies: [] + }, + actualDeductible: this.currentDeductible.toString() ?? '' + }, + lossInfo: { + dateOfLoss: this.order.policy.dateOfLoss, + location: { + city: this.order.policy.damageCity, + state: this.order.policy.damageState, + country: 'US' // TODO set from store + }, + vehicle: { + id: this.order.vehicle.policyVehicleId?.toString() ?? '', + year: this.order.vehicle.year?.toString() ?? '', + make: this.order.vehicle.make, + model: this.order.vehicle.model, + vin: this.order.vehicle.vin + }, + cause: this.order.policy.damageCause, + damageDescription: this.order.policy.damageCause } }, - driver: { - firstName: this.order.customer.firstName, - lastName: this.order.customer.lastName - }, - caller: { - homePhone: {} - }, - policyInfo: { - policyNumber: this.order.policy.policyNumber, - safelitePolicy: { - policies: [] - }, - actualDeductible: this.currentDeductible.toString() ?? '' - }, - lossInfo: { - dateOfLoss: this.order.policy.dateOfLoss, - location: { - city: this.order.policy.damageCity, - state: this.order.policy.damageState, - country: 'US' // TODO set from store - }, - vehicle: { - id: this.order.vehicle.policyVehicleId?.toString() ?? '', - year: this.order.vehicle.year?.toString() ?? '', - make: this.order.vehicle.make, - model: this.order.vehicle.model, - vin: this.order.vehicle.vin - }, - cause: this.order.policy.damageCause, - damageDescription: this.order.policy.damageCause - } - } - }).then((response) => { - this.order.insuranceCoverage.claimNumber = response.data.claimNumber; - if (response.data.isSuccess) { - this.updateCoverageStatus(coverageStatuses.VERIFIED); - } else { - this.updateCoverageStatus(coverageStatuses.NO_COVERAGE); - } - return resolve(response); - }, (error) => { - this.updateCoverageStatus(coverageStatuses.NO_COVERAGE); - this.order.insuranceCoverage.claimNumber = null; - return reject(error); + bailoutOnError: false }); - }); + this.order.insuranceCoverage.claimNumber = response.data.claimNumber; + if (response.data.isSuccess) { + this.updateCoverageStatus(coverageStatuses.VERIFIED); + } else { + this.updateCoverageStatus(coverageStatuses.NO_COVERAGE); + } + } catch (e) { + this.updateCoverageStatus(coverageStatuses.NO_COVERAGE); + this.order.insuranceCoverage.claimNumber = null; + } }, - getFinalDeductible() { + async getFinalDeductible() { const endorsementAnswersForPayload = []; const endorsementAnswers = this.order.policy.endorsementQuestionAnswers; if (endorsementAnswers) { @@ -712,9 +701,9 @@ export const useMainStore = defineStore({ glassInformation?.forEach((glassPiece) => { manualGlassNamesArray.push(glassPiece?.glassLocation?.toUpperCase()); }); - - return new Promise((resolve, reject) => { - globalMethods.callHttpClient({ + + try { + const r = await globalMethods.callHttpClient({ method: endpoints.FinalDeductible.method, endpoint: endpoints.FinalDeductible.url, payload: { @@ -736,16 +725,18 @@ export const useMainStore = defineStore({ vehicleVin: this.order.vehicle.vin, policyData: this.order.policy.policyData, isItac: this.isITAC - } - }).then((r) => { - this.order.policy.policyData = r.data.policyData; - this.updateDeductible(r.data); - return resolve(r); - }).catch((error) => reject(error)); - }); + }, + bailoutOnError: false + }); + this.order.policy.policyData = r.data.policyData; + this.updateDeductible(r.data); + return r; + } catch (e) { + this.updateCoverageStatus(CoverageStatuses.PENDING); + } }, - getDuplicateReferrals() { + async getDuplicateReferrals() { const params = new URLSearchParams({ parentAccountNumber: this.order.parentAccountNumber, customerPhoneNumber: this.order.contactInfo.servicePhone, @@ -754,18 +745,16 @@ export const useMainStore = defineStore({ dateOfLoss: this.order.policy.dateOfLoss }); - return new Promise((resolve, reject) => { - globalMethods.callHttpClient({ + try { + const duplicates = await globalMethods.callHttpClient({ method: endpoints.DuplicateSearch.method, - endpoint: `${endpoints.DuplicateSearch.url}?${params.toString()}` - }).then((r) => { - this.applicationUser.duplicateOrders = r.data ?? []; - return resolve(r.data); - }).catch((error) => { - this.applicationUser.duplicateOrders = []; - return reject(error); + endpoint: `${endpoints.DuplicateSearch.url}?${params.toString()}`, + bailoutOnError: false }); - }); + this.applicationUser.duplicateOrders = duplicates.data ?? []; + } catch (e) { + this.applicationUser.duplicateOrders = []; + } }, async lookupVinByPlate(licensePlate, licenseState) { try { @@ -1219,9 +1208,6 @@ export const useMainStore = defineStore({ IsOEMRequest: this.hasOemEndorsement } } - }).catch((error) => { - console.error(error); - throw error; }); const { lineItems, serverData, isItac, primaryBillToNumber, partsWerePriced, isItacOptimized } = response.data; @@ -1361,7 +1347,8 @@ export const useMainStore = defineStore({ endpoint: endpoints.LookupVehicleByVin.url, payload: { vin - } + }, + bailoutOnError: false }); }, @@ -1393,7 +1380,7 @@ export const useMainStore = defineStore({ this.applicationUser.crmCustomerId = response.crmCustomerId.toString(); }, - saveSession({ submitAfterSave, createWorkOrderNumberForPIA }) { + saveSession({ submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError }) { const { vehicle, damage, policy, customer, contactInfo, payment, lineItems, serviceLocation, schedule, insuranceCoverage } = this.order; @@ -1545,7 +1532,7 @@ export const useMainStore = defineStore({ method: endpoints.SaveSession.method, endpoint: endpoints.SaveSession.url, payload, - bailoutOnError: false + bailoutOnError }).then((response) => { if (loadedFromDupeCheck) { this.order.loadedSessionClearedPreviousData = true; @@ -2462,6 +2449,7 @@ export const useMainStore = defineStore({ populateInitialState(forceReset) { if (!sessionStorage.getItem(storeId) || forceReset) { this.$state = getDefaultState(); + this.resetSubmittedOrder(); } }, @@ -2700,34 +2688,26 @@ export const useMainStore = defineStore({ async getBillToInfo(componentProviderNumber = null) { const { order, issConfig } = this; - try { - const params = new URLSearchParams({ - parentAccountNumber: order.parentAccountNumber.toString(), - providerNumber: componentProviderNumber || this.providerNumber, - typeOfClaim: 'GLASS ONLY', - lineOfBusiness: 'PERSONAL', - isItac: this.isITAC - }); + const params = new URLSearchParams({ + parentAccountNumber: order.parentAccountNumber.toString(), + providerNumber: componentProviderNumber || this.providerNumber, + typeOfClaim: 'GLASS ONLY', + lineOfBusiness: 'PERSONAL', + isItac: this.isITAC + }); - const response = await globalMethods.callHttpClient({ - method: endpoints.GetBillToInfo.method, - endpoint: `${endpoints.GetBillToInfo.url}?${params.toString()}` - }); + const response = await globalMethods.callHttpClient({ + method: endpoints.GetBillToInfo.method, + endpoint: `${endpoints.GetBillToInfo.url}?${params.toString()}`, + bailoutOnError: false + }); - const billToInfo = response.data; - if (billToInfo != null) { - issConfig.billToAccountNumber = billToInfo.toString(); - return Promise.resolve(); - } - - return Promise.reject(new Error('Invalid billToInfo')); - } catch (e) { - return Promise.reject(e); - } + const billToInfo = response.data; + issConfig.billToAccountNumber = billToInfo.toString(); }, async validateClientTag(clientTag) { - return await globalMethods.callHttpClient({ + return globalMethods.callHttpClient({ method: endpoints.ValidateClientTag.method, endpoint: `${endpoints.ValidateClientTag.url}/${clientTag}` }); diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 24c6139f..0e0900d2 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -459,9 +459,9 @@ describe('Store', () => { expect(store.order.insuranceCoverage.claimNumber).not.toBeNull(); }); - it('Call to client returns exception, resulting in object with error property being returned', async () => { + it('Call to client returns exception, and no error is returned from registerClaim method', async () => { // Arrange - expect.assertions(4); + expect.assertions(3); const error = 'register claim error'; globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error)); store.order.insuranceCoverage.coverageStatus = coverageStatuses.PENDING; @@ -1494,8 +1494,8 @@ describe('Store', () => { // Assert expect(globalMethods.callHttpClient).toHaveBeenCalled(); }); - it('api call throws exception => insuranceCoverage.coverageType is NONE', async () => { - expect.assertions(3); + it('api call throws exception => no error is thrown from getCoveragePolicyInfo and insuranceCoverage.coverageType is NONE', async () => { + expect.assertions(2); const error = 'get coverage policy info error'; store.issConfig.isCoverageEnabled = true; store.applicationUser.coverageLookupAttempts = 0; @@ -1567,9 +1567,9 @@ describe('Store', () => { expect(globalMethods.callHttpClient).toHaveBeenCalled(); expect(store.applicationUser.duplicateOrders).toEqual(expected); }); - it('Call to client returns exception => object with error property returned and duplicateReferrals set to []', async () => { + it('Call to client returns exception => object with error property returned and duplicateReferrals set to [] and no error is returned from getDuplicateReferrals', async () => { // Arrange - expect.assertions(3); + expect.assertions(2); const error = 'get duplicate referrals error'; globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error)); @@ -1807,7 +1807,8 @@ describe('Store', () => { vehicleVin, policyData, isItac: store.isITAC - }) + }), + bailoutOnError: false })); }); it('only "Yes" endorsement answers are added to payload', () => { @@ -1912,14 +1913,15 @@ describe('Store', () => { vehicleVin, policyData, isItac: store.isITAC - }) + }), + bailoutOnError: false })); }); }); describe('unsuccessful api call', () => { - it('api call throws exception', async () => { + it('api call throws exception. no error from getFinalDeductible', async () => { // Arrange - expect.assertions(2); + expect.assertions(1); const error = 'final deductible error'; globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error)); @@ -2078,8 +2080,8 @@ describe('Store', () => { expect(result.data.shopProviders[1]).toBe(provider2); }); }); - it('api call throws exception => coverageType none', async () => { - expect.assertions(3); + it('api call throws exception => coverageType none and no error from getCoveragePolicyInfo', async () => { + expect.assertions(2); const error = 'get coverage policy info error'; store.issConfig.isCoverageEnabled = true;