diff --git a/src/helpers/cms-content-helper.js b/src/helpers/cms-content-helper.js index b3130ac7..2ce5dc26 100644 --- a/src/helpers/cms-content-helper.js +++ b/src/helpers/cms-content-helper.js @@ -243,7 +243,7 @@ function mapStringToState(str) { // Reset store state for each match. const valueFromStore = getStoreValueFromString(match[2]); if (!valueFromStore) { - console.warning('Unable to resolve global state data.'); + console.warn('Unable to resolve global state data.'); return ''; // if we can't map our string to state data, return an empty string. } const stringWithReplacement = str.replace(match[0], valueFromStore); diff --git a/src/layouts/policy-endorsements/policy-endorsements.spec.js b/src/layouts/policy-endorsements/policy-endorsements.spec.js index c01818f5..7af82ef8 100644 --- a/src/layouts/policy-endorsements/policy-endorsements.spec.js +++ b/src/layouts/policy-endorsements/policy-endorsements.spec.js @@ -140,7 +140,7 @@ describe('policyEndorsements.vue', () => { expect(wrapper.vm.$router.navigate) .toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK, undefined); }); - test('Forward button clicked triggers navigation', () => { + test('Forward button clicked with valid car id triggers navigation', () => { // Arrange const wrapper = shallowMount(policyEndorsements, getMountOptions({ router: { @@ -148,6 +148,10 @@ describe('policyEndorsements.vue', () => { } })); + wrapper.setData({ + hasValidCarId: true + }); + // Act wrapper.vm.forwardButtonAction(); wrapper.vm.navigateForward(); @@ -157,6 +161,27 @@ describe('policyEndorsements.vue', () => { expect(wrapper.vm.$router.navigate) .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD, undefined); }); + test('Forward button clicked with invalid car id triggers navigation scenario CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND', () => { + // Arrange + const wrapper = shallowMount(policyEndorsements, getMountOptions({ + router: { + navigate: jest.fn() + } + })); + + wrapper.setData({ + hasValidCarId: false, + }); + + // Act + wrapper.vm.forwardButtonAction(); + wrapper.vm.navigateForward(); + + // Assert + expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); + expect(wrapper.vm.$router.navigate) + .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND, undefined); + }); test('Forward button clicked saves endorsement question answers', () => { // Arrange const mountOptions = getMountOptions({ diff --git a/src/layouts/policy-endorsements/policy-endorsements.vue b/src/layouts/policy-endorsements/policy-endorsements.vue index b3f88ffa..f50b4c30 100644 --- a/src/layouts/policy-endorsements/policy-endorsements.vue +++ b/src/layouts/policy-endorsements/policy-endorsements.vue @@ -96,11 +96,17 @@ export default { }); }, emits: ['update:modelValue'], + setup() { + const mainStore = useMainStore(); + + return { mainStore }; + }, data() { return { questionAnswersArray: [], schoolPropertyAnswer: '', parkingLotAnswer: '', + hasValidCarId: this.mainStore.vehicle.carId && this.mainStore.vehicle.carId !== '0', rules: { selectionRequired: globalRules.OPTION_REQUIRED } @@ -156,10 +162,17 @@ export default { return this.navigateForward(); }, navigateForward() { - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD, - this.$route - ); + if (!this.hasValidCarId) { + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND, + this.$route + ); + } else { + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD, + this.$route + ); + } } } }; diff --git a/src/layouts/policy-vehicles/policy-vehicles.spec.js b/src/layouts/policy-vehicles/policy-vehicles.spec.js index 6bb10a35..378e9b5f 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.spec.js +++ b/src/layouts/policy-vehicles/policy-vehicles.spec.js @@ -100,7 +100,8 @@ describe('policy-vehicles.vue', () => { policyVehicles: [ { vin } ], - bailout: false + bailout: false, + policyVinFound: true }); const year = getRandomInt(1998, 2023); @@ -126,6 +127,7 @@ describe('policy-vehicles.vue', () => { // Assert expect(wrapper.vm.bailout).toBeFalsy(); + expect(wrapper.vm.policyVinFound).toBeTruthy(); expect(store.updateVehicle).toHaveBeenCalledWith(expectedInput); expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( navigationScenarios.CLICKED_FORWARD_LISTED_VEHICLE, @@ -190,6 +192,7 @@ describe('policy-vehicles.vue', () => { async () => { // Arrange const { wrapper } = setupMocks({}); + wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue({ error: true, status: 500 }); const vin = getRandomString(17, 17); await wrapper.setData({ @@ -197,9 +200,6 @@ describe('policy-vehicles.vue', () => { bailout: false }); - const store = useMainStore(); - store.lookupVehicleByVin.mockReturnValue(Promise.reject()); - // Act await wrapper.vm.forwardButtonAction(); @@ -214,6 +214,43 @@ describe('policy-vehicles.vue', () => { } ); + test( + // eslint-disable-next-line max-len + 'Vehicle not found in lookupVehicleByVin call => policyVinFound false and navigate forward with CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND scenario.', + async () => { + // Arrange + const { wrapper } = setupMocks({}); + wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue({ error: true, status: 404 }); + + const vin = getRandomString(17, 17); + await wrapper.setData({ + selectedVehicleVin: vin, + bailout: false, + policyVinFound: true, + policyVehicles: [{ + vin, + vehicleYear: getRandomInt(2000, 2100), + vehicleMake: getRandomString(5, 10), + vehicleMode: getRandomString(5, 10), + vehicleStyle: getRandomString(5, 10) + }] + }); + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.bailout).toBeFalsy(); + expect(wrapper.vm.policyVinFound).toBeFalsy(); + expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND, + undefined, + {}, + {} + ); + } + ); + test('vehicle not listed => navigate forward with CLICKED_FORWARD_NON_LISTED_VEHICLE scenario.', async () => { // Arrange const { wrapper } = setupMocks({}); diff --git a/src/layouts/policy-vehicles/policy-vehicles.vue b/src/layouts/policy-vehicles/policy-vehicles.vue index 8ea67941..1765aac5 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.vue +++ b/src/layouts/policy-vehicles/policy-vehicles.vue @@ -77,6 +77,7 @@ export default { policyVehicles, selectedVehicleVin: '', displayGeneric: true, + policyVinFound: true, bailout: false, rules: { optionRequired: globalRules.OPTION_REQUIRED @@ -174,6 +175,26 @@ export default { if (this.selectedVehicleVin !== vehicleSelectionOptions.VEHICLE_NOT_LISTED) { const vehicleLookupResponse = await this.lookupVehicleByVin(this.selectedVehicleVin); if (vehicleLookupResponse.error) { + if (vehicleLookupResponse.status === 404) { + const vehicle = this.policyVehicles.find((pv) => pv.vin === this.selectedVehicleVin); + this.mainStore.resetVehicleState(); + useMainStore().updateVehicle({ + carId: '0', + category: '', + year: vehicle.vehicleYear || '', + make: vehicle.vehicleMake || '', + model: vehicle.vehicleModel || '', + style: vehicle.vehicleStyle || '', + vin: vehicle.vin, + noCoverage: this.noCoverageForSelectedVehicle, + deductible: this.deductibleForSelectedVehicle, + repairWaived: this.repairWaivedForSelectedVehicle + + }); + this.policyVinFound = false; + return this.navigateForward(); + } + this.bailout = true; return this.navigateForward(); } @@ -192,12 +213,13 @@ export default { }, navigateForward() { if (this.bailout) { - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - this.$route, - {}, - {} - ); + this.$router + .navigate( + this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, + this.$route, + {}, + {} + ); } else if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) { this.$router .navigate( @@ -211,6 +233,13 @@ export default { this.navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS, this.$route ); + } else if (!this.policyVinFound) { + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND, + this.$route, + {}, + {} + ); } else { this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD_LISTED_VEHICLE, @@ -225,7 +254,8 @@ export default { return await useMainStore().lookupVehicleByVin(vin); } catch (responseError) { return { - error: true + error: true, + status: responseError.status }; } } diff --git a/src/layouts/vin-lookup/vin-lookup.spec.js b/src/layouts/vin-lookup/vin-lookup.spec.js index d28d172e..71e93dd3 100644 --- a/src/layouts/vin-lookup/vin-lookup.spec.js +++ b/src/layouts/vin-lookup/vin-lookup.spec.js @@ -477,6 +477,43 @@ describe('vin-lookup.vue', () => { ); }); }); + + // eslint-disable-next-line max-len + test('Policy Vehicle with invalid vin corrected. Click "Continue", execute navigate with navigationScenario.CORRECTED_VIN_FROM_POLICY_VEHICLE', async () => { + const user = userEvent.setup(); + mountOptions.global.stubs.vinQuestion = false; + + mountOptions.data = () => ({ + vinWithNonMatchingCarId: true, + isCarIdDifferentFromTheStore: false, + vin: mockValidVin + }); + + getPartsOrQuestions.mockResponse = vehicleWithNoAdditionalPartsOrQuestionsMockResponse; + + 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.CORRECTED_VIN_FROM_POLICY_VEHICLE, + mockRoute + ); + }); + }); }); }); }); diff --git a/src/layouts/vin-lookup/vin-lookup.vue b/src/layouts/vin-lookup/vin-lookup.vue index d9be15f1..ab82ee60 100644 --- a/src/layouts/vin-lookup/vin-lookup.vue +++ b/src/layouts/vin-lookup/vin-lookup.vue @@ -15,7 +15,7 @@ + :displayGenericVehicleImage="carIdIsValid" /> 0 && !this.hasValidCarId() ? vehicleLookupAlertTypes.NOT_FOUND : null, needToLookupVehicle: true, vehicleFromLookup: null, - vin: this.getVinFromStore(), + vinWithNonMatchingCarId: vin?.length > 0 && !this.hasValidCarId(), + vin, forwardButtonCarStyle: '', - vinPopulatedOnPageLoad: this.getVinFromStore()?.length > 0 + vinPopulatedOnPageLoad: vin?.length > 0 && this.hasValidCarId() }; }, computed: { isCarIdDifferentFromTheStore() { return ( - this.vehicleFromLookup !== null + this.vehicleFromLookup !== null && this.hasValidCarId() && this.vehicleFromLookup.carId !== this.mainStore.vehicle.carId ); }, isTwoIdenticalYMMVehicleFound() { + if (this.vehicleFromLookup === null) { + return false; + } const vinYmmFound = `${this.vehicleFromLookup.year} ${this.vehicleFromLookup.make} ${this.vehicleFromLookup.model}`; const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`; @@ -138,6 +143,9 @@ export default { return `!X!X!X!X!X!X!X!X!X!X!X${lastSixChars}`; } return 'XXXXXXXXXXXXXXXXX'; + }, + carIdIsValid() { + return this.hasValidCarId(); } }, watch: { @@ -155,10 +163,13 @@ export default { getVinFromStore() { return this.mainStore.vehicle.vin; }, + hasValidCarId() { + return this.mainStore.vehicle.carId && this.mainStore.vehicle.carId !== '0'; + }, backButtonAction() { /** - * this.navigationScenarios comes from base-mixin - */ + * this.navigationScenarios comes from base-mixin + */ this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); }, // NOTE: If form is not valid, this method is not called when 'Continue' button is clicked @@ -228,6 +239,14 @@ export default { this.mainStore.updateVehicle(this.vehicleFromLookup); } + if (this.vinWithNonMatchingCarId) { + this.$router.navigate( + this.navigationScenarios.CORRECTED_VIN_FROM_POLICY_VEHICLE, + this.$route + ); + return null; + } + const partsOrQuestionsResponse = await this.getPartsOrQuestions(); if (partsOrQuestionsResponse.error) { // To Do: Need requirement on what to do here diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js index ce2bece9..1c6c1a13 100644 --- a/src/router/router-constants/navigation-scenarios.js +++ b/src/router/router-constants/navigation-scenarios.js @@ -25,6 +25,7 @@ const navigationScenarios = Object.freeze({ // Policy Vehicle CLICKED_FORWARD_LISTED_VEHICLE: 'CLICKED_FORWARD_LISTED_VEHICLE', + CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND: 'CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND', CLICKED_FORWARD_NON_LISTED_VEHICLE: 'CLICKED_FORWARD_NON_LISTED_VEHICLE', CLICKED_FORWARD_WITH_ENDORSEMENTS: 'CLICKED_FORWARD_WITH_ENDORSEMENTS', @@ -36,6 +37,7 @@ const navigationScenarios = Object.freeze({ CLICKED_FORWARD_WITHOUT_VIN: 'CLICKED_FORWARD_WITHOUT_VIN', CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES: 'CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES', SELECTED_VIN_WITH_MISMATCHED_GLASS: 'SELECTED_VIN_WITH_MISMATCHED_GLASS', + CORRECTED_VIN_FROM_POLICY_VEHICLE: 'CORRECTED_VIN_FROM_POLICY_VEHICLE', SELECTED_MANUAL_VIN: 'SELECTED_MANUAL_VIN', SELECTED_LICENSE_PLATE: 'SELECTED_LICENSE_PLATE', SELECTED_HOME_ADDRESS: 'SELECTED_HOME_ADDRESS', diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js index e125f45f..9ee532d5 100644 --- a/src/router/router-constants/routing-table.js +++ b/src/router/router-constants/routing-table.js @@ -86,6 +86,10 @@ const routingTable = () => [ scenario: navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS, destinationIssPageValue: issPageValues.VEHICLE_DAMAGE }, + { + scenario: navigationScenarios.CORRECTED_VIN_FROM_POLICY_VEHICLE, + destinationIssPageValue: issPageValues.VEHICLE_DAMAGE + }, { scenario: navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS, destinationIssPageValue: issPageValues.PART_QUESTIONS @@ -110,6 +114,10 @@ const routingTable = () => [ scenario: issPageValues.VEHICLE_LOOKUP, destinationIssPageValue: issPageValues.VEHICLE_LOOKUP }, + { + scenario: issPageValues.BAILOUT_PAGE, + destinationIssPageValue: issPageValues.BAILOUT_PAGE + }, { scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, destinationIssPageValue: issPageValues.BAILOUT_PAGE @@ -447,6 +455,10 @@ const routingTable = () => [ scenario: navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE, destinationIssPageValue: issPageValues.VEHICLE_SELECTION }, + { + scenario: navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND, + destinationIssPageValue: issPageValues.VIN_LOOKUP + }, { scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, destinationIssPageValue: issPageValues.BAILOUT_PAGE @@ -455,7 +467,6 @@ const routingTable = () => [ scenario: navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS, destinationIssPageValue: issPageValues.POLICY_ENDORSEMENTS } - ] }, { @@ -681,6 +692,10 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD, destinationIssPageValue: issPageValues.VEHICLE_DAMAGE + }, + { + scenario: navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND, + destinationIssPageValue: issPageValues.VIN_LOOKUP } ] }