diff --git a/package-lock.json b/package-lock.json index d1fa847c..08d9d17a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3250,9 +3250,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.7.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.15.tgz", - "integrity": "sha512-XnjpaI8Bgc3eBag2Aw4t2Uj/49lLBSStHWfqKvIuXD7FIrZyMLWp8KuAFHAqxMZYTF9l08N1ctUn9YNybZJVmQ==", + "version": "20.3.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.2.tgz", + "integrity": "sha512-vOBLVQeCQfIcF/2Y7eKFTqrMnizK5lRNQ7ykML/5RuwVXVWxYkgwS7xbt4B6fKCUPgbSL5FSsjHQpaGQP/dQmw==", "dev": true }, "node_modules/@types/normalize-package-data": { diff --git a/src/constants/coverage-statuses.js b/src/constants/coverage-statuses.js new file mode 100644 index 00000000..76d46f2e --- /dev/null +++ b/src/constants/coverage-statuses.js @@ -0,0 +1,7 @@ +const coverageStatuses = { + PENDING: "Pending", + NO_COMP: "No Comp", + VERIFIED: "Verified", +}; + +export { coverageStatuses }; \ No newline at end of file diff --git a/src/constants/endorsement-options.js b/src/constants/endorsement-options.js new file mode 100644 index 00000000..c343da16 --- /dev/null +++ b/src/constants/endorsement-options.js @@ -0,0 +1,9 @@ +const endorsementOptions = { + EDUCATOR: "Educator", + OEM_APPROVED: "OEM Approved", + FULL_GLASS: "Full Glass Coverage", + PARKING_GUARD: "Parking Guard", + REPAIR_WAIVED: "Repair Waived" +}; + +export { endorsementOptions }; \ No newline at end of file diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index ca2c297f..be1ad311 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -125,7 +125,11 @@ const endpoints = { CoveragePolicyInfo: { url: '/coverage/api/v1/coverage/get-policy-information', method: 'POST' -} + }, + RegisterClaim: { + url: '/coverage/api/v1/coverage/register-claim', + method: 'POST' + } }; export { endpoints }; diff --git a/src/helpers/data-generation.js b/src/helpers/data-generation.js new file mode 100644 index 00000000..635ab9b4 --- /dev/null +++ b/src/helpers/data-generation.js @@ -0,0 +1,28 @@ +import { randomUUID } from "crypto"; + +export function getRandomString(minLength = 1, maxLength = 100) { + const length = getRandomInt(minLength, maxLength + 1); + let result = ''; + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + const charactersLength = characters.length; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * charactersLength)); + } + return result; +} + +export function getRandomInt(min = 0, max = 1000) { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min) + min); // The maximum is exclusive and the minimum is inclusive +} + +export function getRandomGuid() { + return randomUUID(); +} + +export function getRandomBoolean() { + const bools = [true, false]; + const index = getRandomInt(0,2); + return bools[index]; +} \ No newline at end of file diff --git a/src/layouts/coverage-statement/coverage-statement.spec.js b/src/layouts/coverage-statement/coverage-statement.spec.js index 92cc7215..7ca5a0da 100644 --- a/src/layouts/coverage-statement/coverage-statement.spec.js +++ b/src/layouts/coverage-statement/coverage-statement.spec.js @@ -10,6 +10,8 @@ import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin"; import { applicationConfig } from "@/constants/application-config"; import { fetchCmsContentForPage, setupModalLinks } from "@/helpers/cms-content-helper"; import { settleAllPromises } from "@/helpers/layout-helper.js"; +import { createTestingPinia } from '@pinia/testing'; +import { getRandomString } from '@/helpers/data-generation.js'; jest.mock("@/helpers/damage-helper", () => ({ getDamageString: jest.fn(), @@ -31,7 +33,8 @@ describe("coverage-statement.vue...", () => { test("Should return true for valid page requisites if vin exists", () => { // Arrange const { wrapper } = setupMocks({}); - + useMainStore().order.vehicle.vin = getRandomString(5,20); + // Act let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); @@ -76,7 +79,7 @@ describe("coverage-statement.vue...", () => { useMainStore().order.damage.isRepair = true; // Assert - expect(wrapper.vm.unverifiedNonADASRepairBodyText).toEqual("NonADASRepairTestReturn"); + expect(wrapper.vm.bodyText).toEqual("NonADASRepairTestReturn"); }) test("If damage is NonADAS Replace, display NonADASReplace coverage statement", async () => { @@ -85,8 +88,16 @@ describe("coverage-statement.vue...", () => { mixins: [mockMixin], }); + useMainStore().lineItems.glassParts = + [ + { + requiresRecalibration: false, + } + ]; + useMainStore().order.damage.isRepair = false; + // Assert - expect(wrapper.vm.unverifiedNonADASNextStepsBodyText).toEqual("NonADASReplaceTestReturn"); + expect(wrapper.vm.bodyText).toEqual("NonADASReplaceTestReturn"); }) test("If damage is ADAS Replace, display ADASReplace coverage statement", async () => { @@ -94,12 +105,55 @@ describe("coverage-statement.vue...", () => { const wrapper = shallowMount(coverageStatement, { mixins: [mockMixin], }); - - useMainStore().order.lineItems.requiresRecalibration = true; + useMainStore().lineItems.glassParts = [ + { + requiresRecalibration: true, + } + ]; + useMainStore().order.damage.isRepair = false; // Assert - expect(wrapper.vm.unverifiedADASNextStepsBodyText).toEqual("ADASReplaceTestReturn"); + expect(wrapper.vm.bodyText).toEqual("ADASReplaceTestReturn"); }) + }); + describe("claim registration api call", () => { + it("claim registration not required => method not called", async () => { + // Arrange + const wrapper = setupMocks({}); + + const store = useMainStore(); + store.isClaimRegistrationRequired = false; + + const to = { + query: { issPage: getRandomString(4,10) } + }; + const next = jest.fn(); + + // SUT + coverageStatement.beforeRouteEnter.call(wrapper.vm, to, undefined, next) + + // Assert + expect(store.registerClaim).not.toHaveBeenCalled(); + }); + + it("claim registration required => register claim method called", async () => { + // Arrange + const wrapper = setupMocks({}); + + const store = useMainStore(); + store.isClaimRegistrationRequired = true; + + const to = { + query: { issPage: getRandomString(4,10) } + }; + const next = jest.fn(); + + // SUT + coverageStatement.beforeRouteEnter.call(wrapper.vm, to, undefined, next) + + // Assert + expect(store.registerClaim).toHaveBeenCalled(); + }); }) }); @@ -142,30 +196,6 @@ mountOptionsMockData = {}, }, }; - useMainStore().order = { - vehicle: { - vin: "TESTVIN", - }, - lineItems: { - glassParts: [ - { - canSafeliteRecalibrate: false, - childParts: null, - color: "Green Tint", - description: "solar, driver side, encap", - partNumber: "DQ12204GTYNOEM", - partType: "DRIVER REAR QUARTER GLASS", - recalibrationType: null, - requiresCapabilityQuestions: false, - requiresRecalibration: false, - } - ] - }, - damage: { - isRepair: false, - } - }; - const apiPromise = Promise.resolve(apiResponses); settleAllPromises.mockImplementation(() => apiPromise); @@ -174,12 +204,14 @@ mountOptionsMockData = {}, const mountOptions = getMountOptions({}); mountOptions.mixins = [baseMixin, vehicleQuestionsMixin]; + mountOptions.global = { + plugins: [createTestingPinia()] + } const wrapper = shallowMount(coverageStatement, mountOptions); wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; return { wrapper }; - } diff --git a/src/layouts/coverage-statement/coverage-statement.vue b/src/layouts/coverage-statement/coverage-statement.vue index f1128f8a..f50a0b84 100644 --- a/src/layouts/coverage-statement/coverage-statement.vue +++ b/src/layouts/coverage-statement/coverage-statement.vue @@ -76,14 +76,14 @@ export default { }, computed: { bodyText() { - if (useMainStore().order.damage.isRepair) { + if (useMainStore().damage.isRepair) { return this.unverifiedNonADASRepairBodyText; } else { - let parts = useMainStore().order.lineItems.glassParts; + let parts = useMainStore().lineItems.glassParts; // if ADAS, display ADASNextSteps - if (parts.filter(part => part.requiresRecalibration).length > 0) { + if (parts != null && parts.filter(part => part.requiresRecalibration).length > 0) { return this.unverifiedADASNextStepsBodyText; } // if non-ADAS, display NonADASNextSteps @@ -121,15 +121,23 @@ export default { }, ]; + if (useMainStore().isClaimRegistrationRequired){ + const registerClaimResponse = await useMainStore().registerClaim(); + promiseResultMap.push({ + resultKey: 'registerClaim', + promise: registerClaimResponse, + }); + } + const resultMap = await settleAllPromises(promiseResultMap); next((vm) => { - vm.setCmsContent(resultMap.cmsContent); + vm.setCmsContent(resultMap.cmsContent); }); }, methods: { arePagePrerequisitesValid() { - if (useMainStore().order.vehicle.vin) { + if (useMainStore().vehicle.vin) { return true; } return false; diff --git a/src/layouts/policy-vehicles/policy-vehicles.spec.js b/src/layouts/policy-vehicles/policy-vehicles.spec.js index 5cecac66..8b585428 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.spec.js +++ b/src/layouts/policy-vehicles/policy-vehicles.spec.js @@ -1,28 +1,34 @@ -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 { navigationScenarios } from "@/router/router-constants/navigation-scenarios"; +import policyVehicles from '@/layouts/policy-vehicles/policy-vehicles.vue'; +import { settleAllPromises } from '@/helpers/layout-helper'; +import { shallowMount } from '@vue/test-utils'; +import { getMountOptions } from '@/helpers/unit-test-helper'; +import { useMainStore } from '@/store'; +import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; +import { navigationScenarios } from '@/router/router-constants/navigation-scenarios'; +import baseMixin from '@/mixins/base-mixin'; +import { getRandomString, getRandomInt } from '@/helpers/data-generation'; +import { endorsementOptions } from '@/constants/endorsement-options'; +import { createTestingPinia } from '@pinia/testing'; +import { issPageValues } from "@/router/router-constants/issPage-values"; +import { vehicleSelectionOptions } from "@/constants/vehicle-selection-options"; // Mock fetchCmsContentForPage -jest.mock("@/helpers/cms-content-helper", () => ({ +jest.mock('@/helpers/cms-content-helper', () => ({ fetchCmsContentForPage: jest.fn(), doesCopyContainRouterLink: jest.fn(), - splitCopyOnCMSPlaceHolder: jest.fn().mockImplementation(() => "test"), + 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", () => ({ +jest.mock('@/helpers/layout-helper.js', () => ({ settleAllPromises: jest.fn(), })); -describe("policy-vehicles.vue", () => { - test("Should navigate to CLICKED_BACK if backButtonAction is run", async () => { +describe('policy-vehicles.vue', () => { + test('Should navigate to CLICKED_BACK if backButtonAction is run', async () => { // Arrange const { wrapper } = setupMocks({}); @@ -32,126 +38,376 @@ describe("policy-vehicles.vue", () => { //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({}); + describe('forwardButtonAction', () => { - // Act - await wrapper.setData({ - selectedVehicleVin: "5NMS3CADXLH233004", - bailout: true + test('Selected VIN matches vehicle listed in system => update vehicle and navigate forward with CLICKED_FORWARD_LISTED_VEHICLE scenario.', async () => { + //Arrange + const { wrapper } = setupMocks({}); + + const vin = getRandomString(17,17); + await wrapper.setData({ + selectedVehicleVin: vin, + policyVehicles: [ + { vin: vin }, + ], + bailout: false + }); + + const year = getRandomInt(1998, 2023); + const lookupVehicleResponse = { + data: { + year: year, + } + }; + const store = useMainStore(); + store.lookupVehicleByVin.mockReturnValue(Promise.resolve(lookupVehicleResponse)); + + const expectedInput = { + year: year, + vin: vin, + noCompensation: true, + deductible: 0, + repairWaived: false + } + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.bailout).toBeFalsy(); + expect(store.updateVehicle).toHaveBeenCalledWith(expectedInput); + expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_LISTED_VEHICLE, + undefined, + {}, + {} + ); }); - await wrapper.vm.forwardButtonAction(); - // Assert - expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, - undefined, - {}, - {} - ); + test('Error in lookupVehicleByVin call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario.', async () => { + //Arrange + const { wrapper } = setupMocks({}); + + const vin = getRandomString(17,17); + await wrapper.setData({ + selectedVehicleVin: vin, + bailout: false + }); + + const store = useMainStore(); + store.lookupVehicleByVin.mockReturnValue(Promise.reject()); + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.bailout).toBeTruthy(); + expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, + undefined, + {}, + {} + ); + }); + + test('vehicle not listed => navigate forward with CLICKED_FORWARD_NON_LISTED_VEHICLE scenario.', async () => { + // Arrange + const { wrapper } = setupMocks({}); + + await wrapper.setData({ + selectedVehicleVin: vehicleSelectionOptions.VEHICLE_NOT_LISTED, + }); + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE, + undefined, + {}, + {} + ); + }); + }) + + describe('noCompensationForSelectedVehicle computed property', () => { + it('No vehicle match => returns true', async () => { + // Arrange + const selectedVin = getRandomString(17,17); + const otherVin = getRandomString(17,17); + const testValues = { + selectedVehicleVin: selectedVin, + policyVehicles: [ + { + vin: otherVin, + }, + ], + } + + // Act + const result = policyVehicles.computed.noCompensationForSelectedVehicle.call(testValues); + + // Assert + expect(result).toBeTruthy(); + }); + + it('Coverages list empty => true', () => { + // Arrange + const vin = getRandomString(17,17); + const testValues = { + selectedVehicleVin: vin, + policyVehicles: [ + { + vin: vin, + coverages: [] + }, + ], + } + + // Act + const result = policyVehicles.computed.noCompensationForSelectedVehicle.call(testValues); + + // Assert + expect(result).toBeTruthy(); + }); + + it('Coverages list non-empty => false', () => { + // Arrange + const vin = getRandomString(17,17); + const testValues = { + selectedVehicleVin: vin, + policyVehicles: [ + { + vin: vin, + coverages: [ + { + deductible: 0 + } + ] + }, + ], + } + + // Act + const result = policyVehicles.computed.noCompensationForSelectedVehicle.call(testValues); + + // Assert + expect(result).toBeFalsy(); + }); + }) + + describe('deductibleForSelectedVehicle computed property', () => { + + it('No vehicle match => undefined returned', () => { + // Arrange + const selectedVin = getRandomString(17,17); + const otherVin = getRandomString(17,17); + const testValues = { + selectedVehicleVin: selectedVin, + policyVehicles: [ + { + vin: otherVin, + }, + ], + } + + // Act + const result = policyVehicles.computed.deductibleForSelectedVehicle.call(testValues); + + // Assert + expect(result).toBe(undefined); + }); + + it('Vehicle match with empty coverages list => 0 returned', () => { + // Arrange + const vin = getRandomString(17,17); + const testValues = { + selectedVehicleVin: vin, + policyVehicles: [ + { + vin: vin, + coverages: [] + }, + ], + } + + // Act + const result = policyVehicles.computed.deductibleForSelectedVehicle.call(testValues); + + // Assert + expect(result).toBe(0); + }); + + it('Coverages list non empty => deductible from first coverage returned', () => { + // Arrange + const vin = getRandomString(17,17); + const firstDeductible = getRandomInt(1,1000); + const secondDeductible = getRandomInt(1,1000); + const testValues = { + selectedVehicleVin: vin, + policyVehicles: [ + { + vin: vin, + coverages: [ + { + deductible: firstDeductible + }, + { + deductible: secondDeductible + } + ] + }, + ], + } + + // Act + const result = policyVehicles.computed.deductibleForSelectedVehicle.call(testValues); + + // Assert + expect(result).toBe(firstDeductible); + }); }); - 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: "Vehicle not listed", - }); - await wrapper.vm.forwardButtonAction(); + describe('repairWaivedForSelectedVehicle computed property', () => { - // Assert - expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE, - undefined, - {}, - {} - ); + it('No vehicle match => false returned', () => { + // Arrange + const selectedVin = getRandomString(17,17); + const otherVin = getRandomString(17,17); + const testValues = { + selectedVehicleVin: selectedVin, + policyVehicles: [ + { + vin: otherVin, + }, + ], + } + + // Act + const result = policyVehicles.computed.repairWaivedForSelectedVehicle.call(testValues); + + // Assert + expect(result).toBe(false); + }); + + it('Endorsements list empty => false returned', () => { + // Arrange + const vin = getRandomString(17,17); + const testValues = { + selectedVehicleVin: vin, + policyVehicles: [ + { + vin: vin, + endorsements: [] + }, + ], + } + + // Act + const result = policyVehicles.computed.repairWaivedForSelectedVehicle.call(testValues); + + // Assert + expect(result).toBe(false); + }); + + it('Endorsements list non-empty, not containing repair waived => false returned', () => { + // Arrange + const vin = getRandomString(17,17); + const testValues = { + selectedVehicleVin: vin, + policyVehicles: [ + { + vin: vin, + endorsements: [ endorsementOptions.EDUCATOR, endorsementOptions.PARKING_GUARD ] + }, + ], + } + + // Act + const result = policyVehicles.computed.repairWaivedForSelectedVehicle.call(testValues); + + // Assert + expect(result).toBe(false); + }); + + it('Endorsements list contains repair waived => true returned', () => { + // Arrange + const vin = getRandomString(17,17); + const testValues = { + selectedVehicleVin: vin, + policyVehicles: [ + { + vin: vin, + endorsements: [ + endorsementOptions.EDUCATOR, + endorsementOptions.REPAIR_WAIVED, + endorsementOptions.PARKING_GUARD + ] + }, + ], + } + + // Act + const result = policyVehicles.computed.repairWaivedForSelectedVehicle.call(testValues); + + // Assert + expect(result).toBe(true); + }); }); - test("first vehicle is auto-selected if only one vehicle on policy", async () => { - //Arrange - const { wrapper } = setupMocks({}); + + test('first vehicle is auto-selected if only one vehicle on policy', async () => { + // Arrange + const vin = getRandomString(17,17); + useMainStore().applicationUser = { + pageData: { + [issPageValues.POLICY_VEHICLES]: [ { vin: vin } ], + } + }; + + const { wrapper } = setupMocks(); // Act await wrapper.vm.$nextTick(); // Assert - expect(wrapper.vm.selectedVehicleVin).toBe("5NMS3CADXLH233004"); + expect(wrapper.vm.selectedVehicleVin).toBe(vin); }); }); -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 mockMixin = { + methods: { + getCmsContent: jest.fn(), + }, + computed: { + dynamicStrings() { + return { ROUTER_LINK: 'routerLink:' }; + }, + }, +}; + +function setupMocks() +{ 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, + mixins: [mockMixin], + global: { + mocks: { + $route: { + params: { + id: 1 + } }, - vin: "5NMS3CADXLH233004", + $router: { + navigate: jest.fn() + } }, - }, - }, - ); + plugins: [createTestingPinia()] + } + }); const apiResponses = { cmsContent: {}, @@ -159,24 +415,11 @@ function setupMocks({ 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(() => ""); + const wrapper = shallowMount(policyVehicles, mountOptions); + wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ''); wrapper.vm.setCmsContent = jest.fn(); + wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; wrapper.vm.$refs.siteFooter.updateButtonText = jest.fn(); wrapper.vm.$refs.siteFooter.removeLoader = jest.fn(); return { wrapper }; diff --git a/src/layouts/policy-vehicles/policy-vehicles.vue b/src/layouts/policy-vehicles/policy-vehicles.vue index f38516f6..4a92c1c2 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.vue +++ b/src/layouts/policy-vehicles/policy-vehicles.vue @@ -41,7 +41,8 @@ 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 {vehicleSelectionOptions} from "@/constants/vehicle-selection-options"; +import { vehicleSelectionOptions } from "@/constants/vehicle-selection-options"; +import { endorsementOptions } from "@/constants/endorsement-options"; // DEFINE VALIDATION RULES defineRule("option-required", required(errorMessages.OPTION_REQUIRED)); @@ -49,14 +50,15 @@ export default { name: "policy-vehicles", mixins: [BaseFormMixin], data() { - return { - selectedVehicleVin: "", - displayGeneric: true, - bailout: false - } - }, - mounted() { - this.autoSelectIfOneVehicle(); + const policyVehicles = useMainStore().pageData(issPageValues.POLICY_VEHICLES); + return { + policyVehicles: policyVehicles, + selectedVehicleVin: (policyVehicles?.length ?? 0) == 1 + ? policyVehicles[0].vin + : "", + displayGeneric: true, + bailout: false + } }, async beforeRouteEnter(to, from, next) { @@ -67,28 +69,36 @@ export default { { resultKey: "cmsContent", promise: cmsContentPromise, - },]; - //use resultMap to populate layout content. - let resultMap = await settleAllPromises(promiseResultMap); - next((vm) => { - vm.setCmsContent(resultMap.cmsContent); - }); - }, + }, + ]; + //use resultMap to populate layout content. + let resultMap = await settleAllPromises(promiseResultMap); + next((vm) => { + vm.setCmsContent(resultMap.cmsContent); + }); + }, methods: { backButtonAction() { - this.mainStore.issConfig.disabledFields.policyNumber = true; + useMainStore().issConfig.disabledFields.policyNumber = true; this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); }, - async forwardButtonAction() { - if(this.selectedVehicleVin != vehicleSelectionOptions.VEHICLE_NOT_LISTED){ + async forwardButtonAction() { + if (this.selectedVehicleVin != vehicleSelectionOptions.VEHICLE_NOT_LISTED){ const vehicleLookupResponse = await this.lookupVehicleByVin(this.selectedVehicleVin); if (vehicleLookupResponse.error) { - this.bailout = true; + this.bailout = true; return this.navigateForward(); } - this.vehicleFromLookup = Object.assign(vehicleLookupResponse.data, { vin: this.selectedVehicleVin }); - this.mainStore.updateVehicle(this.vehicleFromLookup); + + this.vehicleFromLookup = Object.assign(vehicleLookupResponse.data, { + vin: this.selectedVehicleVin, + noCompensation: this.noCompensationForSelectedVehicle, + deductible: this.deductibleForSelectedVehicle, + repairWaived: this.repairWaivedForSelectedVehicle + }); + + useMainStore().updateVehicle(this.vehicleFromLookup); } return this.navigateForward(); }, @@ -116,28 +126,21 @@ export default { ); }, async lookupVehicleByVin(vin) { - try { - return await this.mainStore.lookupVehicleByVin(vin); - } - catch (responseError) { - return { - error: { - status: responseError.status, - }, - }; - } - }, - autoSelectIfOneVehicle() { - if (this.VehiclesFromApi.length == 1) { - this.selectedVehicleVin = this.VehiclesFromApi[0].vin; - } - } + try { + return await useMainStore().lookupVehicleByVin(vin); + } + catch (responseError) { + return { + error: true, + }; + } + }, }, computed:{ VehiclesForQuestions() { // Map API result data, to address-vehicles data structure - const vehicles = this.VehiclesFromApi; - const mappedData = vehicles.map((v) => { + const vehicles = this.policyVehicles; + const mappedData = vehicles?.map((v) => { const maskSymbol = "X"; const vinStart = maskSymbol.repeat(v.vin.length - 6); const vinEnd = v.vin.substring(v.vin.length - 6); @@ -148,9 +151,27 @@ export default { Name: v.vin, SubText: "VIN " + vinStart + vinEnd, }; - }); + }) ?? []; return mappedData; }, + noCompensationForSelectedVehicle() { + const vehicle = this.policyVehicles.find((vehicle) => { return vehicle.vin == this.selectedVehicleVin; }); + return (vehicle?.coverages?.length ?? 0) == 0; + }, + deductibleForSelectedVehicle() { + const vehicle = this.policyVehicles.find((vehicle) => { return vehicle?.vin == this.selectedVehicleVin; }); + if (!vehicle){ + return undefined; + } + + return vehicle.coverages?.length ?? false + ? vehicle?.coverages[0].deductible + : 0; + }, + repairWaivedForSelectedVehicle() { + const vehicle = this.policyVehicles.find((vehicle) => { return vehicle.vin == this.selectedVehicleVin; }); + return vehicle?.endorsements?.includes(endorsementOptions.REPAIR_WAIVED) ?? false; + }, VehiclesFromApi() { return useMainStore().pageData(issPageValues.POLICY_VEHICLES); }, @@ -161,7 +182,7 @@ export default { }, watch: { async selectedVehicleVin(value) { - if (value === "Vehicle not listed") { + if (value === vehicleSelectionOptions.VEHICLE_NOT_LISTED) { // clear previously selected vehicle and image this.mainStore.resetVehicleState(); this.displayGeneric = true; @@ -172,9 +193,10 @@ export default { const vehicle = await this.lookupVehicleByVin(value); // handle error in case vehicle info doesn't come back for selected VIN - if (vehicle.error) { + if (vehicle?.error ?? true) { this.mainStore.resetVehicleState(); this.displayGeneric = true; + return; } // save selected vehicle to the store @@ -189,13 +211,7 @@ export default { ) // if there is more than 1 style for the selected vehicle, display generic/blurred image - if (styleOptions?.data?.length > 1) { - this.displayGeneric = true; - } - else { - // display clear image of vehicle - this.displayGeneric = false; - } + this.displayGeneric = styleOptions?.data?.length > 1; } } }, diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue index 7ee5102c..8d850808 100644 --- a/src/layouts/vehicle-damage/vehicle-damage.vue +++ b/src/layouts/vehicle-damage/vehicle-damage.vue @@ -316,170 +316,170 @@ export default { this.selectedGlassToReplace(), this.selectedWindshieldOptions.selectedWindshieldChipCount); return this.navigateForward(); - }, + }, - navigateForward() { - if (this.mainStore.damage.isRepair) { + navigateForward() { + if (this.mainStore.damage.isRepair) { + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD_WITH_REPAIR, + this.$route + ); + } + else { + // If vin already exists, navigate directly to vin-lookup + if (this.mainStore.order.vehicle.vin) { this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_REPAIR, + this.navigationScenarios.CLICKED_FORWARD_WITH_VIN, + this.$route + ); + } else { + this.$router.navigate( + this.navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN, this.$route ); } - else { - // If vin already exists, navigate directly to vin-lookup - if (this.mainStore.order.vehicle.vin) { - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITH_VIN, - this.$route - ); - } else { - this.$router.navigate( - this.navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN, - this.$route - ); - } - } - }, - - selectedGlassToReplace() { - const selectedGlassToReplace = []; - if (this.isWindshieldDamageLocation && !this.isWindshieldRepair) { - this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.forEach( - (wsItem) => { - selectedGlassToReplace.push({ - glassLocation: damageLocationsSelected.WINDSHIELD, - glassName: wsItem, - }); - } - ); - } - - if (this.isDriverSideReplace) { - this.sideDoorOptionsData.selectedDriverSideReplaceOptions.forEach((driverItem) => { - selectedGlassToReplace.push({ - glassLocation: damageLocationsSelected.DRIVER, - glassName: driverItem, - }); - }); - } - - if (this.isPassengerSideReplace) { - this.sideDoorOptionsData.selectedPassengerSideReplaceOptions.forEach( - (passengerItem) => { - selectedGlassToReplace.push({ - glassLocation: damageLocationsSelected.PASSENGER, - glassName: passengerItem, - }); - } - ); - } - - if (this.isRearWindowDamageLocation) { - selectedGlassToReplace.push({ - glassLocation: damageLocationsSelected.REAR, - glassName: this.selectedRearReplaceOptions, - }); - } - - return selectedGlassToReplace; - }, - }, - computed: { - isWindshieldDamageLocation() { - return this.selectedDamageLocations.some((selectedDamages) => { - return selectedDamages.toUpperCase() === damageLocationsCms.WINDSHIELD; - }); - }, - isSideDoorDamageLocation() { - return this.selectedDamageLocations.some((selectedDamages) => { - return selectedDamages.toUpperCase() === damageLocationsCms.SIDEDOOR; - }); - }, - isRearWindowDamageLocation() { - return this.selectedDamageLocations.some((selectedDamages) => { - return selectedDamages.toUpperCase() === damageLocationsCms.REARWINDOW; - }); - }, - isWindshieldRepair() { - return ( - this.isWindshieldDamageLocation && - this.selectedWindshieldOptions.selectedWindshieldDamageType === - damageLocationsSelected.REPAIR - ); - }, - isDriverSideReplace() { - if (!this.isSideDoorDamageLocation) return false; - - return this.sideDoorOptionsData.selectedDoorSides.some((selectedDriverSide) => { - return selectedDriverSide.toUpperCase() === damageLocationsCms.DRIVERSIDE; - }); - }, - isPassengerSideReplace() { - if (!this.isSideDoorDamageLocation) return false; - - return this.sideDoorOptionsData.selectedDoorSides.some((selectedPassengerSide) => { - return selectedPassengerSide.toUpperCase() === damageLocationsCms.PASSENGERSIDE; - }); - }, - hasRepairReplaceConflict() { - return ( - this.isWindshieldDamageLocation && - this.selectedDamageLocations.length > 1 && - this.isWindshieldRepair - ); - }, - hasSplitSingleConflict() { - if ( - !this.selectedDamageLocations?.includes("Windshield") || - this.selectedWindshieldOptions.selectedWindshieldDamageType === - damageLocationsSelected.REPAIR || - !this.selectedWindshieldOptions.selectedWindshieldReplaceOptions - ) - return false; - - return ( - this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some( - (selectedSingleWindshield) => { - return ( - selectedSingleWindshield.toUpperCase() === - damageLocationsSelected.SINGLE.toUpperCase() - ); - } - ) && - (this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some( - (selectedDriverWindshield) => { - return ( - selectedDriverWindshield.toUpperCase() === - damageLocationsSelected.DRIVER.toUpperCase() - ); - } - ) || - this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some( - (selectedPassengerWindshield) => { - return ( - selectedPassengerWindshield.toUpperCase() === - damageLocationsSelected.PASSENGER.toUpperCase() - ); - } - )) - ); - }, - shouldDisplayVehicleChangeAlert() { - return this.$route.params[this.routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]; } }, - components: { - siteHeader, - siteFooter, - vehicleBanner, - siteSubHeader, - sideDoorOptions, - damageLocationQuestion, - windshieldOptions, - replaceOptionsQuestion, - Form, - alert, + selectedGlassToReplace() { + const selectedGlassToReplace = []; + if (this.isWindshieldDamageLocation && !this.isWindshieldRepair) { + this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.forEach( + (wsItem) => { + selectedGlassToReplace.push({ + glassLocation: damageLocationsSelected.WINDSHIELD, + glassName: wsItem, + }); + } + ); + } + + if (this.isDriverSideReplace) { + this.sideDoorOptionsData.selectedDriverSideReplaceOptions.forEach((driverItem) => { + selectedGlassToReplace.push({ + glassLocation: damageLocationsSelected.DRIVER, + glassName: driverItem, + }); + }); + } + + if (this.isPassengerSideReplace) { + this.sideDoorOptionsData.selectedPassengerSideReplaceOptions.forEach( + (passengerItem) => { + selectedGlassToReplace.push({ + glassLocation: damageLocationsSelected.PASSENGER, + glassName: passengerItem, + }); + } + ); + } + + if (this.isRearWindowDamageLocation) { + selectedGlassToReplace.push({ + glassLocation: damageLocationsSelected.REAR, + glassName: this.selectedRearReplaceOptions, + }); + } + + return selectedGlassToReplace; }, - }; - + }, + computed: { + isWindshieldDamageLocation() { + return this.selectedDamageLocations.some((selectedDamages) => { + return selectedDamages.toUpperCase() === damageLocationsCms.WINDSHIELD; + }); + }, + isSideDoorDamageLocation() { + return this.selectedDamageLocations.some((selectedDamages) => { + return selectedDamages.toUpperCase() === damageLocationsCms.SIDEDOOR; + }); + }, + isRearWindowDamageLocation() { + return this.selectedDamageLocations.some((selectedDamages) => { + return selectedDamages.toUpperCase() === damageLocationsCms.REARWINDOW; + }); + }, + isWindshieldRepair() { + return ( + this.isWindshieldDamageLocation && + this.selectedWindshieldOptions.selectedWindshieldDamageType === + damageLocationsSelected.REPAIR + ); + }, + isDriverSideReplace() { + if (!this.isSideDoorDamageLocation) return false; + + return this.sideDoorOptionsData.selectedDoorSides.some((selectedDriverSide) => { + return selectedDriverSide.toUpperCase() === damageLocationsCms.DRIVERSIDE; + }); + }, + isPassengerSideReplace() { + if (!this.isSideDoorDamageLocation) return false; + + return this.sideDoorOptionsData.selectedDoorSides.some((selectedPassengerSide) => { + return selectedPassengerSide.toUpperCase() === damageLocationsCms.PASSENGERSIDE; + }); + }, + hasRepairReplaceConflict() { + return ( + this.isWindshieldDamageLocation && + this.selectedDamageLocations.length > 1 && + this.isWindshieldRepair + ); + }, + hasSplitSingleConflict() { + if ( + !this.selectedDamageLocations?.includes("Windshield") || + this.selectedWindshieldOptions.selectedWindshieldDamageType === + damageLocationsSelected.REPAIR || + !this.selectedWindshieldOptions.selectedWindshieldReplaceOptions + ) + return false; + + return ( + this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some( + (selectedSingleWindshield) => { + return ( + selectedSingleWindshield.toUpperCase() === + damageLocationsSelected.SINGLE.toUpperCase() + ); + } + ) && + (this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some( + (selectedDriverWindshield) => { + return ( + selectedDriverWindshield.toUpperCase() === + damageLocationsSelected.DRIVER.toUpperCase() + ); + } + ) || + this.selectedWindshieldOptions.selectedWindshieldReplaceOptions?.some( + (selectedPassengerWindshield) => { + return ( + selectedPassengerWindshield.toUpperCase() === + damageLocationsSelected.PASSENGER.toUpperCase() + ); + } + )) + ); + }, + shouldDisplayVehicleChangeAlert() { + return this.$route.params[this.routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]; + } + }, + + components: { + siteHeader, + siteFooter, + vehicleBanner, + siteSubHeader, + sideDoorOptions, + damageLocationQuestion, + windshieldOptions, + replaceOptionsQuestion, + Form, + alert, + }, +}; + diff --git a/src/layouts/vehicle-lookup/vehicle-lookup.spec.js b/src/layouts/vehicle-lookup/vehicle-lookup.spec.js index 085136f3..398ce254 100644 --- a/src/layouts/vehicle-lookup/vehicle-lookup.spec.js +++ b/src/layouts/vehicle-lookup/vehicle-lookup.spec.js @@ -1,12 +1,11 @@ /* eslint-env jest */ import { mount } from '@vue/test-utils'; import { navigationScenarios } from '@/router/router-constants/navigation-scenarios'; -import { issPageValues } from '@/router/router-constants/issPage-values'; import { queryStrings } from '@/constants/query-strings'; import { GaActions } from "@/constants/analytics"; import VehicleLookup from './vehicle-lookup.vue'; import {vinLookupMethodSelections} from '@/constants/vin-lookup-methods'; -import { useMainStore } from '../../store'; +import { useMainStore } from '@/store'; import { createTestingPinia } from '@pinia/testing'; import { mapStores } from "pinia"; diff --git a/src/store/index.js b/src/store/index.js index e6789340..7a58d4f3 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -6,6 +6,7 @@ import { experimentTriggers } from '@/constants/experiments'; import { applicationConfig } from '@/constants/application-config'; import { issPageValues } from '@/router/router-constants/issPage-values'; import { damageLocationsSelected } from '@/constants/damage-locations-selected'; +import { coverageStatuses } from '@/constants/coverage-statuses'; const storeId = 'main'; @@ -48,7 +49,12 @@ const getDefaultState = () => { damageCause: null, damageState: null, damageCity: null, - isDamageGlassOnly: null + isDamageGlassOnly: null, + noCompensation: null, + deductible: { + repair: null, // numerical value; how much customer owes on deductible in repair case + replace: null // numerical value; how much customer owes on deductible in replace case, + } }, customer: { address: { @@ -79,7 +85,8 @@ const getDefaultState = () => { payment: { isInsurance: true, insuranceCoverage: { - isVerified: false + isVerified: false, + coverageStatus: coverageStatuses.PENDING } }, referralNumber: null, @@ -125,7 +132,10 @@ export const useMainStore = defineStore({ vehicle: (state) => state.order.vehicle, damage: (state) => state.order.damage, lineItems: (state) => state.order.lineItems, + payment: (state) => state.order.payment, + policy: (state) => state.order.policy, hasAnyNonWindshieldGlassParts: (state) => !state.order.policy.isDamageGlassOnly, + isClaimRegistrationRequired: (state) => state.issConfig.isClaimRegistrationRequired, eventBusItem: (state) => ( eventCategory, eventSubCategory) => { const matchedEvent = state.applicationUser.eventBus.find( @@ -327,7 +337,7 @@ export const useMainStore = defineStore({ } }, getCoveragePolicyInfo({accountNumber, policyNumber, dateOfLoss, zipCode}){ - //todo: replace place holder correlationId with the real thing + //TODO: replace place holder correlationId with the real thing const placeHolderCorrelationId = "00000000-0000-0000-0000-000000000000"; try{ const response = globalMethods.callHttpClient({ @@ -350,6 +360,69 @@ export const useMainStore = defineStore({ }; } }, + registerClaim() { + // TODO: replace place holder correlationId with the real thing + const placeHolderCorrelationId = "00000000-0000-0000-0000-000000000000"; + globalMethods.callHttpClient({ + method: endpoints.RegisterClaim.method, + endpoint: endpoints.RegisterClaim.url, + payload: + { + correlationId: placeHolderCorrelationId, + accountNumber: this.issConfig.accountNumber?.toString() ?? "", + 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.order.customer.phoneNumber + } + }, + caller: { + homePhone: {} + }, + policyInfo: { + policyNumber: this.order.policy.policyNumber, + safelitePolicy: { + policies: [] + } + }, + lossInfo: { + dateOfLoss: this.order.policy.dateOfLoss, + location: { + city: this.order.policy.damageCity, + state: this.order.policy.damageState, + country: "US" // TODO set from store + }, + vehicle: { + year: this.order.vehicle.year?.toString() ?? "", + make: this.order.vehicle.make, + model: this.order.vehicle.model, + vin: this.order.vehicle.vin + }, + }, + damageDescription: this.order.policy.damageCause + } + }).then((response) => { + const registerClaimFailed = response.data.isError; + this.order.payment.insuranceCoverage.isVerified = !registerClaimFailed; + this.order.payment.insuranceCoverage.coverageStatus = registerClaimFailed + ? coverageStatuses.PENDING + : this.policy.noCompensation + ? coverageStatuses.NO_COMP + : coverageStatuses.VERIFIED; + },(error) => { + this.order.payment.insuranceCoverage.isVerified = false; + this.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.PENDING; + }); + }, async lookupVinByPlate(licensePlate, licenseState) { try { const response = await globalMethods.callHttpClient({ @@ -483,7 +556,7 @@ export const useMainStore = defineStore({ console.error(error); return []; }); - }, + }, async getRainDefense() { return globalMethods @@ -593,18 +666,18 @@ export const useMainStore = defineStore({ } }); }, - + setVehicle() { return globalMethods - .callHttpClient({ - methods: endpoints.GetVehicle.method, - endpoint: `${endpoints.GetVehicle.url}/${this.order.vehicle.year}/${this.order.vehicle.make}/${this.order.vehicle.model}/${this.order.vehicle.style}`, - payload: {} - }) - .then((response) => { - this.updateVehicle(response.data); - return response; - }); + .callHttpClient({ + methods: endpoints.GetVehicle.method, + endpoint: `${endpoints.GetVehicle.url}/${this.order.vehicle.year}/${this.order.vehicle.make}/${this.order.vehicle.model}/${this.order.vehicle.style}`, + payload: {} + }) + .then((response) => { + this.updateVehicle(response.data); + return response; + }); }, saveVehicleDamage(isWindshieldRepair, selectedGlassToReplace, selectedWindshieldChipCount) { @@ -681,8 +754,7 @@ export const useMainStore = defineStore({ updateVehicle(vehicle) { // Assuming that the method caller pass all the properties. // otherwise need to check for undefined for every property. - - this.order.vehicle.carId = vehicle.carId; + this.vehicle.carId = vehicle.carId; this.order.vehicle.category = vehicle.category; this.order.vehicle.year = vehicle.year; this.order.vehicle.make = vehicle.make; @@ -693,6 +765,11 @@ export const useMainStore = defineStore({ this.order.vehicle.imageVifNumber = vehicle.imageVifNumber; this.order.vehicle.imageColor = vehicle.imageVifColor; + // These could be undefined + this.order.policy.noCompensation = vehicle.noCoverage; + this.order.policy.deductible.replace = vehicle.deductible; + this.order.policy.deductible.repair = vehicle?.repairWaived ?? false ? 0 : vehicle.deductible; + this.resetSupportingItemsState(); this.resetVapsState(); }, diff --git a/src/store/store.spec.js b/src/store/store.spec.js index e282f849..03b06257 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -1,19 +1,21 @@ import { useMainStore } from '@/store'; import { createApp } from 'vue'; -import { createPinia } from "pinia"; +import { setActivePinia, createPinia } from "pinia"; import globalMethods from "@/global-methods"; import App from '@/App.vue'; - +import { getRandomString, getRandomGuid, getRandomInt, getRandomBoolean } from '@/helpers/data-generation'; +import { coverageStatuses } from "@/constants/coverage-statuses.js"; describe("Store", () => { let store; const vueApp = createApp(App); - const pinia = createPinia(); - vueApp.use(pinia); beforeEach(() => { + const pinia = createPinia(); + setActivePinia(pinia); + vueApp.use(pinia); store = useMainStore(); store.applicationUser.eventBus = []; jest.resetAllMocks(); @@ -27,31 +29,47 @@ describe("Store", () => { }); it("Should add events to the bus", () => { + // Arrange + const category = getRandomString(1, 25); + const subCategory = getRandomString(5, 20); + const isDismissible = getRandomBoolean(); + const copy = getRandomString(5, 25); + const headline = getRandomString(5, 25); + const type = getRandomString(5, 15); let event = { - category: "TestCategory", - subCategory: "TestSubCategory", + category: category, + subCategory: subCategory, eventValue: { - isDismissible: true, - messageCopy: "You can get a quote by starting on this page.", - messageHeadline: "We're sorry, something went wrong.", - type: "testType", + isDismissible: isDismissible, + messageCopy: copy, + messageHeadline: headline, + type: type, }, }; + // Act store.addEventToBus(event); + // Assert expect(store.applicationUser.eventBus[0]).toEqual(event); }); it("Should remove events from the bus", () => { + // Arrange + const category = getRandomString(1, 25); + const subCategory = getRandomString(5, 20); + const isDismissible = getRandomBoolean(); + const copy = getRandomString(5, 25); + const headline = getRandomString(5, 25); + const type = getRandomString(5, 15); let event = { - category: "TestCategory", - subCategory: "TestSubCategory", + category: category, + subCategory: subCategory, eventValue: { - isDismissible: true, - messageCopy: "You can get a quote by starting on this page.", - messageHeadline: "We're sorry, something went wrong.", - type: "testType", + isDismissible: isDismissible, + messageCopy: copy, + messageHeadline: headline, + type: type, }, }; @@ -59,164 +77,350 @@ describe("Store", () => { expect(store.applicationUser.eventBus.length).toBe(1); + // Act store.removeEventFromBus({ category: event.category, subCategory: event.subCategory }) + // Assert expect(store.applicationUser.eventBus.length).toBe(0); }); it("Should return correct event using the getter function eventBusItem", () => { + // Arrange + const category = getRandomString(1, 25); + const subCategory = getRandomString(5, 20); + const isDismissible = getRandomBoolean(); + const copy = getRandomString(5, 25); + const headline = getRandomString(5, 25); + const type = getRandomString(5, 15); let event = { - category: "TestCategory", - subCategory: "TestSubCategory", + category: category, + subCategory: subCategory, eventValue: { - isDismissible: true, - messageCopy: "You can get a quote by starting on this page.", - messageHeadline: "We're sorry, something went wrong.", - type: "testType", + isDismissible: isDismissible, + messageCopy: copy, + messageHeadline: headline, + type: type, }, }; store.addEventToBus(event); + // Act const actual = store.eventBusItem(event.category, event.subCategory) + // Assert expect(actual).toEqual(event.eventValue); }); it("UpdateVehicle should merge vehicle with response object", () => { - store.order.vehicle = { - year: 2020, - make: "Honda", - model: "Civic", - style: "4 door sedan", - carId: null, - category: null, - vin: null, - imageUrl: null, - imageVifNumber: null, - imageColor: null, + // Arrange + const carId = getRandomString(10,14); + const category = getRandomString(3,7); + const year = getRandomInt(1960, 2023); + const make = getRandomString(4,10); + const model = getRandomString(4,10); + const style = getRandomString(4,15); + const imageUrl = getRandomString(50,100); + const imageVifNumber = getRandomInt(10000,99999).toString(); + const imageColor = getRandomString(4,10); + const providedVehicle = { + carId: carId, + category: category, + year: year, + make: make, + model: model, + style: style, + imageUrl: imageUrl, + imageVifNumber: imageVifNumber, + imageVifColor: imageColor }; - const response = {data: { - "carId": "CR00069299", - "category": "CAR", - "year": 2020, - "make": "Honda", - "model": "Civic", - "style": "4 door sedan", - "imageUrl": "https://dbhdyzvm8lm25.cloudfront.net/color_0320_032/MY2020/13996/13996_cc0320_032_WX.jpg", - "imageVifNumber": "13996", - "imageVifColor": "white" + const expectedVehicle = { + carId: carId, + category: category, + year: year, + make: make, + model: model, + style: style, + imageUrl: imageUrl, + imageVifNumber: imageVifNumber, + imageColor: imageColor + } + + // Act + store.updateVehicle(providedVehicle); + + // Assert + expect(store.order.vehicle).toMatchObject(expectedVehicle); + }); + + it("UpdateVehicle should set policy values appropriately with repair waived", () => { + // Arrange + const noCompensation = getRandomBoolean(); + const deductible = getRandomInt(1,500); + const vehicle = { + noCoverage: noCompensation, + deductible: deductible, + repairWaived: true + }; + + const expectedPolicy = { + noCompensation: noCompensation, + deductible: { + replace: deductible, + repair: 0 } }; - store.updateVehicle(response.data); + // Act + store.updateVehicle(vehicle); - expect(store.order.vehicle.imageUrl).toEqual(response.data.imageUrl); - expect(store.order.vehicle.imageVifNumber).toEqual(response.data.imageVifNumber); - expect(store.order.vehicle.style).toEqual(response.data.style); + // Assert + expect(store.order.policy).toMatchObject(expectedPolicy); }); - it("setVehicle should call globalMethods.callHttpClient", () => { - store.order.vehicle = jest.fn(); + it("UpdateVehicle should set policy values appropriately with repair not waived", () => { + // Arrange + const noCompensation = getRandomBoolean(); + const deductible = getRandomInt(1,500); + const vehicle = { + noCoverage: noCompensation, + deductible: deductible, + repairWaived: false + }; - const response = {data: { - "carId": "CR00069299", - "category": "CAR", - "year": 2020, - "make": "Honda", - "model": "Civic", - "style": "4 door sedan", - "imageUrl": "https://dbhdyzvm8lm25.cloudfront.net/color_0320_032/MY2020/13996/13996_cc0320_032_WX.jpg", - "imageVifNumber": "13996", - "imageVifColor": "white" + const expectedPolicy = { + noCompensation: noCompensation, + deductible: { + replace: deductible, + repair: deductible + } + }; + + // Act + store.updateVehicle(vehicle); + + // Assert + expect(store.order.policy).toMatchObject(expectedPolicy); + }); + + // TODO update test to work also checking store values + it("setVehicle should call globalMethods.callHttpClient", () => { + // Arrange + const carId = getRandomString(10,14); + const category = getRandomString(3,7); + const year = getRandomInt(1960, 2023); + const make = getRandomString(4,10); + const model = getRandomString(4,10); + const style = getRandomString(4,15); + const imageUrl = getRandomString(50,100); + const imageVifNumber = getRandomInt(10000,99999).toString(); + const imageColor = getRandomString(4,10); + const response = { + data: { + carId: carId, + category: category, + year: year, + make: make, + model: model, + style: style, + imageUrl: imageUrl, + imageVifNumber: imageVifNumber, + imageVifColor: imageColor } }; globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(response)); - - store.setVehicle(); - - expect(globalMethods.callHttpClient).toHaveBeenCalled(); - }); - - it("saveVehicleDamage, should update damage", () => { - // Arrange - store.order.damage = { - glassToReplace: [{ glassName: "Single", glassLocation: "Windshield" }], - }; // Act - store.saveVehicleDamage( false, - [{ glassName: "Rear", glassLocation: "quarter" }], - 0); + const returned = store.setVehicle(); // Assert - expect(store.order.damage.glassToReplace).toEqual([{ glassName: "Rear", glassLocation: "quarter" }]); - expect(store.order.damage.isRepair).toEqual(false); - expect(store.order.damage.numberOfChips).toEqual(null); + expect(globalMethods.callHttpClient).toHaveBeenCalled(); + expect(returned).resolves.toMatchObject(response); + }); + + it("saveVehicleDamage with windshield repair should update damage with number of chips not null", () => { + // Arrange + const glassName = getRandomString(4,10); + const glassLocation = getRandomString(5,15); + const isWindshieldRepair = true; + const selectedGlassToReplace = [{ + glassName: glassName, + glassLocation: glassLocation + }]; + const chipCount = getRandomInt(0,3); + + // Act + store.saveVehicleDamage(isWindshieldRepair, selectedGlassToReplace, chipCount); + + // Assert + expect(store.order.damage.glassToReplace).toEqual(selectedGlassToReplace); + expect(store.order.damage.isRepair).toEqual(isWindshieldRepair); + expect(store.order.damage.numberOfChips).toEqual(chipCount); + }); + + it("saveVehicleDamage without windshield repair should update damage with number of chips null", () => { + // Arrange + const glassName = getRandomString(4,10); + const glassLocation = getRandomString(5,15); + const isWindshieldRepair = false; + const selectedGlassToReplace = [{ + glassName: glassName, + glassLocation: glassLocation + }]; + const chipCount = getRandomInt(0,3); + + const expectedChipCount = null; + + // Act + store.saveVehicleDamage(isWindshieldRepair, selectedGlassToReplace, chipCount); + + // Assert + expect(store.order.damage.glassToReplace).toEqual(selectedGlassToReplace); + expect(store.order.damage.isRepair).toEqual(isWindshieldRepair); + expect(store.order.damage.numberOfChips).toEqual(expectedChipCount); }); it("should return registration data if available", () => { //Arrange + const streetAddress = getRandomString(5,15); + const city = getRandomString(5,15); + const state = getRandomString(5,10); + const zipCode = getRandomInt(10000,99999).toString(); + const firstName = getRandomString(5,20); + const lastName = getRandomString(5,20); const expected = { addressQuestions: { - streetAddress: "test", - city: "city", - state: "state", - zipCode: "zip", + streetAddress: streetAddress, + city: city, + state: state, + zipCode: zipCode, }, - firstName: "1stName", - lastName: "Surname", + firstName: firstName, + lastName: lastName, } store.order.vehicle.registration = { licensePlate: null, - address: "test", - city: "city", - state: "state", - zipCode: "zip", - firstName: "1stName", - lastName: "Surname", + address: streetAddress, + city: city, + state: state, + zipCode: zipCode, + firstName: firstName, + lastName: lastName, }; //Act const actual = store.customerData; - //Assert + //Assert expect(actual).toEqual(expected); }); it("should return customer data if registration data unavailable", () => { //Arrange + const address = getRandomString(1,25); + const city = getRandomString(5,20); + const state = getRandomString(4,20); + const zipCode = getRandomInt(10000, 99999).toString(); + const firstName = getRandomString(5,25); + const lastName = getRandomString(5,25); const expected = { addressQuestions: { - streetAddress: "test", - city: "city", - state: "state", - zipCode: "zip", + streetAddress: address, + city: city, + state: state, + zipCode: zipCode, }, - firstName: "1stName", - lastName: "Surname", + firstName: firstName, + lastName: lastName, } store.order.vehicle.registration.address = null; store.order.customer = { - licensePlate: null, - address: "test", - city: "city", - state: "state", - zipCode: "zip", - firstName: "1stName", - lastName: "Surname", + address: { + streetAddress: address, + city: city, + state: state, + zipCode: zipCode + }, + firstName: firstName, + lastName: lastName }; //Act const actual = store.customerData; - //Assert - expect(actual).toEqual(expected); + //Assert + expect(actual).toMatchObject(expected); }); + describe("registerClaim method", () => { + it("successful response with no coverage => isVerified true and coverage status no comp", async () => { + // Arrange + const response = { + data: { + claimantId: null, + claimNumber: getRandomString(9, 9), + correlationId: getRandomGuid(), + isSuccess: true, + isError: false, + successMessage: getRandomString(9, 9), + deductible: 0 + } + }; + store.policy.noCompensation = true; + + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(response)); + + // Act + await store.registerClaim(); + + // Asserts + expect(globalMethods.callHttpClient).toHaveBeenCalled(); + expect(store.payment.insuranceCoverage.isVerified).toBe(true); + expect(store.payment.insuranceCoverage.coverageStatus).toBe(coverageStatuses.NO_COMP); + }); + + it("successful response with coverage => isVerified true and coverage status verified", async () => { + // Arrange + const response = { + data: { + claimantId: null, + claimNumber: getRandomString(9, 9), + correlationId: getRandomGuid(), + isSuccess: true, + isError: false, + successMessage: getRandomString(9, 9), + deductible: 0 + } + }; + store.policy.noCompensation = false; + + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(response)); + + // Act + await store.registerClaim(); + + // Asserts + expect(globalMethods.callHttpClient).toHaveBeenCalled(); + expect(store.payment.insuranceCoverage.isVerified).toBe(true); + expect(store.payment.insuranceCoverage.coverageStatus).toBe(coverageStatuses.VERIFIED); + }) + + it("Call to client returns exception, resulting in object with error property being returned", async () => { + // Arrange + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject()); + + // Act + await store.registerClaim(); + + // Asserts + expect(globalMethods.callHttpClient).toHaveBeenCalled(); + expect(store.payment.insuranceCoverage.isVerified).toBe(false); + expect(store.payment.insuranceCoverage.coverageStatus).toBe(coverageStatuses.PENDING); + }); + }) }); \ No newline at end of file