diff --git a/src/constants/error-messages.js b/src/constants/error-messages.js index 3fcd5848..11e1d050 100644 --- a/src/constants/error-messages.js +++ b/src/constants/error-messages.js @@ -37,7 +37,12 @@ const errorMessages = { POLICYHOLDER_FIRST_NAME_REQUIRED: "Please enter the policyholder first name", POLICYHOLDER_LAST_NAME_REQUIRED: "Please enter the policyholder last name", - ACKNOWLEDGEMENT_REQUIRED: "You must agree to the terms to continue" + ACKNOWLEDGEMENT_REQUIRED: "You must agree to the terms to continue", + + YEAR_REQUIRED: "Please select your vehicle year", + MAKE_REQUIRED: "Please select your vehicle make", + MODEL_REQUIRED: "Please select your vehicle model", + STYLE_REQUIRED: "Please select your vehicle style" }; diff --git a/src/constants/vehicle-selection-options.js b/src/constants/vehicle-selection-options.js new file mode 100644 index 00000000..01897ea9 --- /dev/null +++ b/src/constants/vehicle-selection-options.js @@ -0,0 +1,6 @@ +const vehicleSelectionOptions = Object.freeze({ + VEHICLE_NOT_LISTED: 'Vehicle not listed', + }); + + export {vehicleSelectionOptions}; + \ No newline at end of file diff --git a/src/layouts/entry-page/entry-page.vue b/src/layouts/entry-page/entry-page.vue index 8fa467d5..4f06bb03 100644 --- a/src/layouts/entry-page/entry-page.vue +++ b/src/layouts/entry-page/entry-page.vue @@ -147,6 +147,11 @@ this.mainStore.issConfig.disabledFields.policyNumber = true; break; + case "policyzipcode": + this.mainStore.order.policy.policyZipCode = value; + this.mainStore.issConfig.disabledFields.policyZipCode = true; + break; + case "lossdate": // NOTE: May need some date parsing logic in here depending on client. this.mainStore.order.policy.dateOfLoss = value; @@ -167,7 +172,7 @@ break; } } - }, + } }, computed:{ }, diff --git a/src/layouts/policy-vehicles/policy-vehicles-question/policy-vehicles-question.spec.js b/src/layouts/policy-vehicles/policy-vehicles-question/policy-vehicles-question.spec.js new file mode 100644 index 00000000..c9efabf3 --- /dev/null +++ b/src/layouts/policy-vehicles/policy-vehicles-question/policy-vehicles-question.spec.js @@ -0,0 +1,33 @@ +import policyVehiclesQuestion from "@/layouts/policy-vehicles/policy-vehicles-question/policy-vehicles-question.vue"; +import { shallowMount } from "@vue/test-utils"; + + +describe("policy-vehicles-question.vue", () => { + test("Selected vehicle is emitted upon selection", async () => { + //Arrange + const vehicleToSelect = "Test1"; + const wrapper = shallowMount(policyVehiclesQuestion, { + mixins: [mockMixin], + propsData: { + vehicles: ["Test1", "Test2"] + }, + }); + + //Act + wrapper.setValue({ modelValue: vehicleToSelect }); + await wrapper.vm.$nextTick(); + + //Assert + expect(wrapper.emitted()["update:modelValue"][0]).toEqual([ + { modelValue: "Test1" }, + ]); + }); +}); + +const mockMixin = { + methods: { + getCmsContent: jest.fn(()=>{ + return [ "vehicle not listed" ]; + }) + }, +}; \ No newline at end of file diff --git a/src/layouts/policy-vehicles/policy-vehicles-question/policy-vehicles-question.vue b/src/layouts/policy-vehicles/policy-vehicles-question/policy-vehicles-question.vue index 62f2522e..6d19b5da 100644 --- a/src/layouts/policy-vehicles/policy-vehicles-question/policy-vehicles-question.vue +++ b/src/layouts/policy-vehicles/policy-vehicles-question/policy-vehicles-question.vue @@ -5,7 +5,9 @@ buttonTypeString="listButton" isOverflowScrollable :answers="answers" - isRequired /> + v-model="selectedVehicleVin" + isRequired + :validation-rules="validationRules" /> diff --git a/src/layouts/policy-vehicles/policy-vehicles.spec.js b/src/layouts/policy-vehicles/policy-vehicles.spec.js new file mode 100644 index 00000000..1d2f6875 --- /dev/null +++ b/src/layouts/policy-vehicles/policy-vehicles.spec.js @@ -0,0 +1,185 @@ +import policyVehicles from "@/layouts/policy-vehicles/policy-vehicles"; +import { settleAllPromises } from "@/helpers/layout-helper.js"; +import { shallowMount } from "@vue/test-utils"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import { useMainStore } from "@/store"; +import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; +import {vehicleSelectionOptions} from "@/constants/vehicle-selection-options"; +import { navigationScenarios } from "@/router/router-constants/navigation-scenarios"; + +// Mock fetchCmsContentForPage +jest.mock("@/helpers/cms-content-helper", () => ({ + fetchCmsContentForPage: jest.fn(), + doesCopyContainRouterLink: jest.fn(), + splitCopyOnCMSPlaceHolder: jest.fn().mockImplementation(() => "test"), + getRouterLinkRouteFromCopy: jest.fn(), + getRouterLinkDisplayTextFromCopy: jest.fn(), + splitCMSCopyOnParagraphTag: jest.fn(), +})); + +// Mock our module for promises. +jest.mock("@/helpers/layout-helper.js", () => ({ + settleAllPromises: jest.fn(), +})); + +describe("policy-vehicles.vue", () => { + test("Should navigate to CLICKED_BACK if backButtonAction is run", async () => { + // Arrange + const { wrapper } = setupMocks({}); + + // Act + await wrapper.vm.backButtonAction(); + + //Assert + expect(wrapper.vm.$router.navigate).toBeCalled(); + }); + test("Should navigate to CLICKED_BACK if backButtonAction is run", async () => { + // Arrange + const { wrapper } = setupMocks({}); + + // Act + await wrapper.vm.backButtonAction(); + + //Assert + expect(wrapper.vm.$router.navigate).toBeCalled(); + }); + + test("Selected vehicle VIN do match vehicles listed in our system (CarIDs) found then navigate forward to vehicle-damage page.", async () => { + //Arrange + const { wrapper } = setupMocks({}); + + // Act + await wrapper.setData({ + selectedVehicleVin: "5NMS3CADXLH233004" + }); + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_LISTED_VEHICLE, + undefined, + {}, + {} + ); + }); + test("Selected vehicle VIN do not match vehicles listed in our system (CarIDs) found then navigate forward to bailout page.", async () => { + //Arrange + const { wrapper } = setupMocks({}); + + // Act + await wrapper.setData({ + selectedVehicleVin: "5NMS3CADXLH233004", + bailout: true + }); + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, + undefined, + {}, + {} + ); + }); + test("if user select vehicle not listed option then navigate forward to vehicle-selection page.", async () => { + //Arrange + const { wrapper } = setupMocks({}); + + // Act + await wrapper.setData({ + selectedVehicleVin: vehicleSelectionOptions.VEHICLE_NOT_LISTED, + }); + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE, + undefined, + {}, + {} + ); + }); +}); + +function setupMocks({ + route = null, + lookupVehicleByVinResponse +}) +{ + useMainStore().applicationUser = { + pageData: { + "policy-vehicles": + [ + { + vehicleMake: "Hyundai", + vehicleModel: "Santa Fe", + vehicleStyle: "4 door utility", + vehicleYear: 2020, + vin: "5NMS3CADXLH233004", + }, + ], + } + }; + useMainStore().lookupVehicleByVin = jest.fn().mockImplementation(() => { + return Promise.resolve({ + data: lookupVehicleByVinResponse + ? lookupVehicleByVinResponse : { + vehicle: { + carId: "CARID" + }, + }, + }) + }); + const mountOptions = getMountOptions({ + route: route ? route : undefined, + router: { + navigate: jest.fn(), + }, + mainStore: { + order: { + vehicle: { + carId: "CR00069309", + category: "SUV", + imageUrl: + "https://dbhdyzvm8lm25.cloudfront.net/color_0320_032/MY2020/13769/13769_cc0320_032_WW8.jpg", + imageVifColor: "white", + imageVifNumber: "13769", + make: "Hyundai", + model: "Santa Fe", + style: "4 door utility", + year: 2020, + }, + vin: "5NMS3CADXLH233004", + }, + }, + }, + ); + + const apiResponses = { + cmsContent: {}, + }; + + settleAllPromises.mockImplementation(() => apiResponses); + fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); + + + //Mock props + const mockMixin = { + methods: { + getCmsContent: jest.fn(), + }, + computed: { + dynamicStrings() { + return { ROUTER_LINK: "routerLink:" }; + }, + }, + }; + + mountOptions.mixins = [mockMixin]; + const wrapper = shallowMount(policyVehicles,mountOptions); + wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ""); + wrapper.vm.setCmsContent = jest.fn(); + wrapper.vm.$refs.siteFooter.updateButtonText = jest.fn(); + wrapper.vm.$refs.siteFooter.removeLoader = jest.fn(); + return { wrapper }; +} \ No newline at end of file diff --git a/src/layouts/policy-vehicles/policy-vehicles.vue b/src/layouts/policy-vehicles/policy-vehicles.vue index 73c0bcb8..a85cc583 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.vue +++ b/src/layouts/policy-vehicles/policy-vehicles.vue @@ -9,7 +9,13 @@
- +
@@ -29,18 +35,25 @@ import policyVehiclesQuestion from "@/layouts/policy-vehicles/policy-vehicles-qu // Supporting files import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { settleAllPromises } from "@/helpers/layout-helper"; -import { Form,defineRule } from "vee-validate"; +import { required } from "@/helpers/validation-rules"; +import { Form, defineRule } from "vee-validate"; import BaseFormMixin from '@/mixins/base-form-mixin.js'; import { issPageValues } from "@/router/router-constants/issPage-values"; import { useMainStore } from '@/store'; import { errorMessages } from "@/constants/error-messages"; -import { required } from "@/helpers/validation-rules"; +import {vehicleSelectionOptions} from "@/constants/vehicle-selection-options"; // DEFINE VALIDATION RULES defineRule("option-required", required(errorMessages.OPTION_REQUIRED)); export default { name: "policy-vehicles", mixins: [BaseFormMixin], + data() { + return { + selectedVehicleVin: "", + bailout: false + } + }, async beforeRouteEnter(to, from, next) { // Call APIs @@ -59,13 +72,57 @@ export default { }, methods: { - backButtonAction() { + backButtonAction() { + this.mainStore.issConfig.disabledFields.policyNumber = true; + this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); }, - forwardButtonAction() { + async forwardButtonAction() { + if(this.selectedVehicleVin != vehicleSelectionOptions.VEHICLE_NOT_LISTED){ + const vehicleLookupResponse = await this.lookupVehicleByVin(this.selectedVehicleVin); + if (vehicleLookupResponse.error) { + this.bailout = true; + return this.navigateForward(); + } + this.vehicleFromLookup = Object.assign(vehicleLookupResponse.data, { vin: this.selectedVehicleVin }); + this.mainStore.updateVehicle(this.vehicleFromLookup); + } return this.navigateForward(); }, navigateForward() { + if(this.bailout) + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, + this.$route, + {}, + {} + ); + else if(this.selectedVehicleVin == vehicleSelectionOptions.VEHICLE_NOT_LISTED) + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE, + this.$route, + {}, + {} + ); + else + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD_LISTED_VEHICLE, + this.$route, + {}, + {} + ); }, + async lookupVehicleByVin(vin) { + try { + return await this.mainStore.lookupVehicleByVin(vin); + } + catch (responseError) { + return { + error: { + status: responseError.status, + }, + }; + } + }, }, computed:{ @@ -88,8 +145,8 @@ export default { }, VehiclesFromApi() { return useMainStore().pageData(issPageValues.POLICY_VEHICLES); - }, - }, + }, + }, components: { siteHeader, siteFooter, diff --git a/src/layouts/service-location/service-location.vue b/src/layouts/service-location/service-location.vue index eac6e7ec..5b42579c 100644 --- a/src/layouts/service-location/service-location.vue +++ b/src/layouts/service-location/service-location.vue @@ -3,7 +3,7 @@
- +
diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue index f01ff4a4..5106203c 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.vue +++ b/src/layouts/vehicle-damage/vehicle-damage.vue @@ -95,9 +95,12 @@ export default { name: "vehicle-damage", mixins: [BaseFormMixin], async beforeRouteEnter(to, from, next) { + + const store = useMainStore(); + // Call APIs const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); - const damageOptionsPromise = useMainStore().getDamageOptions(useMainStore().order.vehicle.carId); + const damageOptionsPromise = store.getDamageOptions(store.order.vehicle.carId); // Settle promises and get results const promiseResultMap = [ diff --git a/src/layouts/vehicle-parts/vehicle-parts.vue b/src/layouts/vehicle-parts/vehicle-parts.vue index 08caf800..83d4077f 100644 --- a/src/layouts/vehicle-parts/vehicle-parts.vue +++ b/src/layouts/vehicle-parts/vehicle-parts.vue @@ -108,7 +108,7 @@ data() { }, computed: { isForwardActionDisabled() { - return this.selectedGlassPartNumbers.length !== this.PartsFromApi.partsOrQuestions.length + return this.selectedGlassPartNumbers?.length !== this.PartsFromApi.partsOrQuestions?.length }, selectedGlassPartNumbers() { // Compile all selected parts from the page. @@ -124,7 +124,7 @@ computed: { const partsData = this.PartsFromApi; // Map API result data, to vehicle-parts data structure - const mappedData = partsData.partsOrQuestions.map((g) => { + const mappedData = partsData.partsOrQuestions?.map((g) => { return { glassName: g.glassName, glassLocation: g.glassLocation, diff --git a/src/layouts/vehicle-selection/make-question/make-question.vue b/src/layouts/vehicle-selection/make-question/make-question.vue new file mode 100644 index 00000000..1ab0e45d --- /dev/null +++ b/src/layouts/vehicle-selection/make-question/make-question.vue @@ -0,0 +1,76 @@ + + + diff --git a/src/layouts/vehicle-selection/model-question/model-question.vue b/src/layouts/vehicle-selection/model-question/model-question.vue new file mode 100644 index 00000000..e1cb2a75 --- /dev/null +++ b/src/layouts/vehicle-selection/model-question/model-question.vue @@ -0,0 +1,77 @@ + + + diff --git a/src/layouts/vehicle-selection/style-question/style-question.vue b/src/layouts/vehicle-selection/style-question/style-question.vue new file mode 100644 index 00000000..b957d923 --- /dev/null +++ b/src/layouts/vehicle-selection/style-question/style-question.vue @@ -0,0 +1,76 @@ + + + diff --git a/src/layouts/vehicle-selection/vehicle-selection.vue b/src/layouts/vehicle-selection/vehicle-selection.vue new file mode 100644 index 00000000..cb53c2ea --- /dev/null +++ b/src/layouts/vehicle-selection/vehicle-selection.vue @@ -0,0 +1,183 @@ + + + diff --git a/src/layouts/vehicle-selection/year-question/year-question.vue b/src/layouts/vehicle-selection/year-question/year-question.vue new file mode 100644 index 00000000..97ba0090 --- /dev/null +++ b/src/layouts/vehicle-selection/year-question/year-question.vue @@ -0,0 +1,63 @@ + + + \ No newline at end of file diff --git a/src/layouts/welcome-page/welcome-page.vue b/src/layouts/welcome-page/welcome-page.vue index b70a24eb..32689479 100644 --- a/src/layouts/welcome-page/welcome-page.vue +++ b/src/layouts/welcome-page/welcome-page.vue @@ -3,9 +3,9 @@
- +
-
+
-
+
+ ref="policyZip" + validationRules="policy-zip-required|policy-zip-format" />
@@ -152,51 +154,49 @@