diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 3e6a617e..8f21cdd9 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -117,7 +117,7 @@ const endpoints = Object.freeze({ method: 'GET' }, TaxOrderItems: { - url: '/price/api/v1/price/taxed-order-items', + url: `${PRICE_BASE_URL}/taxed-order-items`, method: 'GET' }, LogExperimentExposureIfAssigned: { diff --git a/src/helpers/price-calculator.js b/src/helpers/price-calculator.js new file mode 100644 index 00000000..b44abc5a --- /dev/null +++ b/src/helpers/price-calculator.js @@ -0,0 +1,12 @@ +function getPriceOfLineItem(lineItem) { + return (lineItem?.kitPrice ?? 0) + + (lineItem?.laborAmount ?? 0) + + (lineItem?.sellingPrice ?? 0); +} + +export default function getPriceOfLineItems(lineItems) { + return lineItems.reduce( + (accumulator, lineItem) => accumulator + getPriceOfLineItem(lineItem), + 0 + ); +} diff --git a/src/helpers/price-calculator.spec.js b/src/helpers/price-calculator.spec.js new file mode 100644 index 00000000..4d09128b --- /dev/null +++ b/src/helpers/price-calculator.spec.js @@ -0,0 +1,56 @@ +import getPriceOfLineItems from "@/helpers/price-calculator.js"; + +describe('getPriceOfLineItems', () => { + test('Returns zero when no line items', () => { + // Arrange + const lineItems = []; + + // Act + const result = getPriceOfLineItems(lineItems); + + // Assert + expect(result).toBe(0); + }); + test('Returns expected when one line item', () => { + // Arrange + const lineItems = [ + { + kitPrice: 1, + laborAmount: 2, + sellingPrice: 3 + } + ]; + const expected = 6; + + // Act + const result = getPriceOfLineItems(lineItems); + + // Assert + expect(result).toBe(expected); + }); + test('Returns expected when multiple line items', () => { + // Arrange + const lineItems = [ + { + kitPrice: 1, + laborAmount: 2, + sellingPrice: 3 + }, + { + kitPrice: 1 + }, + { + kitPrice: 10, + laborAmount: 100, + sellingPrice: 1000 + } + ]; + const expected = 1117; + + // Act + const result = getPriceOfLineItems(lineItems); + + // Assert + expect(result).toBe(expected); + }); +}); diff --git a/src/iss-components/vehicle-banner/vehicle-banner.vue b/src/iss-components/vehicle-banner/vehicle-banner.vue index 387118c1..fd4010f9 100644 --- a/src/iss-components/vehicle-banner/vehicle-banner.vue +++ b/src/iss-components/vehicle-banner/vehicle-banner.vue @@ -23,12 +23,9 @@ export default { return this.genericVehicleImage; } - let imageUrl = ''; - if (this.mainStore.hasSubmittedOrder()) { - imageUrl = this.mainStore.submittedOrder.vehicle.imageUrl; - } else { - imageUrl = this.mainStore.order.vehicle.imageUrl; - } + const imageUrl = (this.mainStore.hasSubmittedOrder()) + ? this.mainStore.submittedOrder.vehicle.imageUrl + : this.mainStore.order.vehicle.imageUrl; if (!imageUrl || imageUrl === 'NULL') { return this.getUnmatchedVehicleIcon(); diff --git a/src/layouts/coverage-statement/__snapshots__/coverage-statement.spec.js.snap b/src/layouts/coverage-statement/__snapshots__/coverage-statement.spec.js.snap new file mode 100644 index 00000000..aca040ba --- /dev/null +++ b/src/layouts/coverage-statement/__snapshots__/coverage-statement.spec.js.snap @@ -0,0 +1,29 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`coverageStatement.vue-working returns the initial data 1`] = ` +Object { + "baseServiceLineItems": Array [], + "currencyFormatter": NumberFormat {}, + "deductibleText": "Your deductible is", + "isNoComp": false, + "isRepair": true, + "loadingText": Array [ + "Connecting to your insurance company", + "Nearly there", + "Finishing up", + ], + "policyLookupSuccessful": true, + "rules": Object { + "selectionRequired": "option-required", + }, + "selectedProvider": "", + "supportingItems": null, + "widget": Object { + "explanatoryText": "ExplanatoryTextWidget", + "nextStep": "NextStepsWidget", + "serviceProviderQuestion": "ServiceProviderQuestion", + "subheader": "SiteSubHeaderWidget", + "verifiedItacAlert": "VerifiedITACAlert", + }, +} +`; diff --git a/src/layouts/coverage-statement/coverage-statement.spec.js b/src/layouts/coverage-statement/coverage-statement.spec.js index a597b7f9..65cfd347 100644 --- a/src/layouts/coverage-statement/coverage-statement.spec.js +++ b/src/layouts/coverage-statement/coverage-statement.spec.js @@ -3,17 +3,18 @@ import coverageStatement from '@/layouts/coverage-statement/coverage-statement.v // Supporting Files import { nextTick } from 'vue'; -import { mount } from '@vue/test-utils'; +import { shallowMount } from '@vue/test-utils'; import { getMountOptions } from '@/helpers/unit-test-helper.js'; import { createTestingPinia } from '@pinia/testing'; import navigationScenarios from '@/router/router-constants/navigation-scenarios.js'; import { getRandomString, getRandomInt } from '@/helpers/data-generation.js'; import settleAllPromises from '@/helpers/layout-helper.js'; import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; -import routerParams from '@/router/router-constants/router-params'; import { useMainStore } from '@/store/index.js'; +import getPriceOfLineItems from '@/helpers/price-calculator.js'; jest.mock('@/helpers/layout-helper.js', () => jest.fn()); +jest.mock('@/helpers/price-calculator.js', () => jest.fn()); jest.mock('@/helpers/cms-content-helper', () => ({ fetchCmsContentForPage: jest.fn(), @@ -21,10 +22,15 @@ jest.mock('@/helpers/cms-content-helper', () => ({ processIfStatements: jest.fn() })); +const SAFELITE_PROVIDER = 'Safelite'; + const mockMixin = { methods: { getCmsContent: jest.fn().mockImplementation(() => ''), - setCmsContent: jest.fn() + setCmsContent: jest.fn(), + onSubmit: jest.fn(), + onInvalidSubmit: jest.fn(), + navigateBackByVehicleQuestions: jest.fn() } }; @@ -51,6 +57,7 @@ function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRu }); mountOptions.global.stubs = { + ...mountOptions.global.stubs, siteFooter: footerStub, siteHeader: true, recalModal: true, @@ -79,44 +86,28 @@ function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRu settleAllPromises.mockImplementation(() => apiPromise); fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); - const wrapper = mount(coverageStatement, mountOptions); + const wrapper = shallowMount(coverageStatement, mountOptions); return { wrapper }; } -describe.skip('coverageStatement.vue', () => { - describe('method arePagePrerequisitesValid...', () => { - test('Should return true for valid page requisites if vin exists', () => { - // Arrange - const { wrapper } = getMountedComponent({ - order: { - vehicle: { - vin: getRandomString(5, 20) - } +describe('coverageStatement.vue-working', () => { + test('returns the initial data', () => { + // Arrange + const mainInitialState = { + order: { + damage: { + isRepair: true + }, + policy: { + policyLookupSuccessful: true, + noCoverage: false } - }); + } + }; + const { wrapper } = getMountedComponent(mainInitialState); - // Act - const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); - - // Assert - expect(arePagePrerequisitesValid).toBeTruthy(); - }); - test('Should return false for valid page requisites if vin is missing', () => { - // Arrange - const { wrapper } = getMountedComponent({ - order: { - vehicle: { - vin: null - } - } - }); - - // Act - const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); - - // Assert - expect(arePagePrerequisitesValid).not.toBeTruthy(); - }); + // Assert + expect(wrapper.vm.$data).toMatchSnapshot(); }); describe('Rendering', () => { test('Should render site header', () => { @@ -171,663 +162,920 @@ describe.skip('coverageStatement.vue', () => { expect(footer.exists()).toBe(true); }); }); - describe('Verified ITAC scenario', () => { - test('If policy lookup successful, not No Comp, and deductible > service price, verifiedITAC returns true', () => { - // Arrange - const sellingPrice = getRandomInt(100, 500); - const deductible = sellingPrice + 1; - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - noCoverage: false, - deductible: { - repair: deductible - }, - policyLookupSuccessful: true - } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - wrapper.setData({ - availableLineItems: [ - { - sellingPrice, - kitPrice: 0, - laborAmount: 0 - } - ] - }); - - // Assert - expect(wrapper.vm.verifiedITAC).toBeTruthy(); - }); - test('If policy lookup unsuccessful, verifiedITAC returns false', () => { - // Arrange - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - policyLookupSuccessful: false - } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - - // Assert - expect(wrapper.vm.verifiedITAC).toBeFalsy(); - }); - test('If No Comp, verifiedITAC returns false', () => { - // Arrange - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - noCoverage: true - } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - - // Assert - expect(wrapper.vm.verifiedITAC).toBeFalsy(); - }); - test('If deductible < service price, verifiedITAC returns false', () => { - // Arrange - const sellingPrice = getRandomInt(100, 500); - const deductible = sellingPrice - 1; - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - noCoverage: false, - deductible: { - repair: deductible + describe('Computed', () => { + describe('verifiedNoComp', () => { + test.each([true, false])('returns false when policyLookupSuccessful false', (isNoComp) => { + // Arrange + const mainInitialState = { + order: { + policy: { + noCoverage: isNoComp, + policyLookupSuccessful: false } } - } - }; + }; + const { wrapper } = getMountedComponent(mainInitialState); - const { wrapper } = getMountedComponent(mainInitialState); - wrapper.setData({ - availableLineItems: [ - { - sellingPrice, - kitPrice: 0, - laborAmount: 0 - } - ] + // Act + const result = wrapper.vm.verifiedNoComp; + + // Assert + expect(result).toBeFalsy(); }); - - // Assert - expect(wrapper.vm.verifiedITAC).toBeFalsy(); - }); - }); - describe('Verified Deductible scenario', () => { - test('If policy lookup successful, not No Comp, and service price > deductible, verifiedITAC returns true', () => { - // Arrange - const sellingPrice = getRandomInt(100, 500); - const deductible = sellingPrice - 1; - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - noCoverage: false, - deductible: { - repair: deductible - }, - policyLookupSuccessful: true - } - } - }; - - const { wrapper } = getMountedComponent(mainInitialState); - wrapper.setData({ - availableLineItems: [ - { - sellingPrice, - kitPrice: 0, - laborAmount: 0 - } - ] - }); - - // Assert - expect(wrapper.vm.verifiedDeductible).toBeTruthy(); - }); - test('If policy lookup unsuccessful, verifiedDeductible returns false', () => { - // Arrange - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - policyLookupSuccessful: false - } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - - // Assert - expect(wrapper.vm.verifiedDeductible).toBeFalsy(); - }); - test('If No Comp, verifiedDeductible returns false', () => { - // Arrange - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - noCoverage: true - } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - - // Assert - expect(wrapper.vm.verifiedDeductible).toBeFalsy(); - }); - test('If service price < deductible, verifiedDeductible returns false', () => { - const sellingPrice = getRandomInt(100, 500); - const deductible = sellingPrice + 1; - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - noCoverage: false, - deductible: { - repair: deductible + test.each([true, false])('returns false when isNoComp false', (policyLookupSuccessful) => { + // Arrange + const mainInitialState = { + order: { + policy: { + noCoverage: false, + policyLookupSuccessful } } - } - }; + }; + const { wrapper } = getMountedComponent(mainInitialState); - const { wrapper } = getMountedComponent(mainInitialState); - wrapper.setData({ - availableLineItems: [ - { - sellingPrice, - kitPrice: 0, - laborAmount: 0 - } - ] + // Act + const result = wrapper.vm.verifiedNoComp; + + // Assert + expect(result).toBeFalsy(); }); - - // Assert - expect(wrapper.vm.verifiedDeductible).toBeFalsy(); - }); - test('If deductible is null, verifiedDeductible returns false', () => { - const sellingPrice = getRandomInt(100, 500); - const deductible = null; - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - noCoverage: false, - deductible: { - repair: deductible + test('returns true when policyLookupSuccessful true and policyLookupSuccessful true', () => { + // Arrange + const mainInitialState = { + order: { + policy: { + noCoverage: true, + policyLookupSuccessful: true } } - } - }; + }; + const { wrapper } = getMountedComponent(mainInitialState); - const { wrapper } = getMountedComponent(mainInitialState); - wrapper.setData({ - availableLineItems: [ - { - sellingPrice, - kitPrice: 0, - laborAmount: 0 - } - ] + // Act + const result = wrapper.vm.verifiedNoComp; + + // Assert + expect(result).toBeTruthy(); }); - - // Assert - expect(wrapper.vm.verifiedDeductible).toBeFalsy(); }); - }); - describe('No Comp scenario', () => { - test('If noCoverage = true, verifiedNoComp returns true', () => { - // Arrange - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - noCoverage: true, - policyLookupSuccessful: true - } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - - // Assert - expect(wrapper.vm.verifiedNoComp).toBeTruthy(); - }); - test('If noCoverage = false, verifiedNoComp returns false', () => { - // Arrange - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - noCoverage: false - } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - - // Assert - expect(wrapper.vm.verifiedNoComp).toBeFalsy(); - }); - }); - describe('Unverified scenario', () => { - test('If policyLookupSuccessful false, unverified returns true', () => { - // Arrange - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - policyLookupSuccessful: false - } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - - // Assert - expect(wrapper.vm.unverified).toBeTruthy(); - }); - test('If policyLookupSuccessful is true, unverified returns false', () => { - // Arrange - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - policyLookupSuccessful: true - } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - - // Assert - expect(wrapper.vm.unverified).toBeFalsy(); - }); - }); - describe('Navigation', () => { - test('If Unverified, navigate forward with CLICKED_FORWARD scenario', () => { - // Arrange - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - policyLookupSuccessful: false - } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - - // Act - wrapper.vm.forwardButtonAction(); - - // Assert - expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD, - undefined, - {}, - { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } - ); - }); - test('If Verified Deductible, navigate forward with CLICKED_FORWARD scenario', () => { - // Arrange - const sellingPrice = getRandomInt(100, 500); - const deductible = sellingPrice - 1; - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - noCoverage: false, - deductible: { - repair: deductible + describe('verifiedITAC', () => { + const priceOfLineItems = 213; + test.each([true, false])('returns false when policyLookupSuccessful false', (isNoComp) => { + // Arrange + const mainInitialState = { + order: { + policy: { + noCoverage: isNoComp, + policyLookupSuccessful: false }, - policyLookupSuccessful: true + currentDeductible: priceOfLineItems + 1 } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - wrapper.setData({ - availableLineItems: [ - { - sellingPrice, - kitPrice: 0, - laborAmount: 0 - } - ] + }; + getPriceOfLineItems.mockImplementationOnce(() => priceOfLineItems); + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.verifiedITAC; + + // Assert + expect(result).toBeFalsy(); }); - - // Act - wrapper.vm.forwardButtonAction(); - - // Assert - expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD, - undefined, - {}, - { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } - ); - }); - test('If Verified ITAC and selected Safelite, navigate forward with CLICKED_FORWARD_WITH_SAFELITE scenario', () => { - // Arrange - const sellingPrice = getRandomInt(100, 500); - const deductible = sellingPrice + 1; - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - noCoverage: false, - deductible: { - repair: deductible + test.each([true, false])('returns false when isNoComp true', (policyLookupSuccessful) => { + // Arrange + const mainInitialState = { + order: { + policy: { + noCoverage: true, + policyLookupSuccessful }, - policyLookupSuccessful: true + currentDeductible: priceOfLineItems + 1 } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - wrapper.setData({ - availableLineItems: [ - { - sellingPrice, - kitPrice: 0, - laborAmount: 0 - } - ], - selectedProvider: 'Safelite' + }; + getPriceOfLineItems.mockImplementationOnce(() => priceOfLineItems); + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.verifiedITAC; + + // Assert + expect(result).toBeFalsy(); }); - - // Act - wrapper.vm.forwardButtonAction(); - - // Assert - expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE, - undefined, - {}, - { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } - ); - }); - test('If Verified ITAC, selected other shop, and TPA enabled, navigate forward w/ CLICKED_FORWARD_WITH_TPA_ENABLED', () => { - // Arrange - const sellingPrice = getRandomInt(100, 500); - const deductible = sellingPrice + 1; - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - noCoverage: false, - deductible: { - repair: deductible + test.each([ + [true, false], + [true, true], + [false, false], + [false, true] + ])('returns false when deductibleValue equals totalServicePrice', (noCoverage, policyLookupSuccessful) => { + // Arrange + const mainInitialState = { + order: { + policy: { + noCoverage, + policyLookupSuccessful }, - policyLookupSuccessful: true + currentDeductible: priceOfLineItems } - }, - issConfig: { - enableTPAFlow: true - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - wrapper.setData({ - availableLineItems: [ - { - sellingPrice, - kitPrice: 0, - laborAmount: 0 - } - ], - selectedProvider: 'Other' + }; + getPriceOfLineItems.mockImplementationOnce(() => priceOfLineItems); + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.verifiedITAC; + + // Assert + expect(result).toBeFalsy(); }); + test.each([ + [true, false], + [true, true], + [false, false], + [false, true] + ])( + 'returns false when deductibleValue less than totalServicePrice when noCoverage is %p and policyLookupSuccessful is %p', + (noCoverage, policyLookupSuccessful) => { + // Arrange + const mainInitialState = { + order: { + policy: { + noCoverage, + policyLookupSuccessful + }, + currentDeductible: priceOfLineItems - 1 + } + }; + getPriceOfLineItems.mockImplementationOnce(() => priceOfLineItems); + const { wrapper } = getMountedComponent(mainInitialState); - // Act - wrapper.vm.forwardButtonAction(); + // Act + const result = wrapper.vm.verifiedITAC; - // Assert - expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD_WITH_TPA_ENABLED, - undefined, - {}, - { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } - ); - }); - test('If Verified ITAC, selected other shop, and TPA disabled, navigate forward w/ CLICKED_FORWARD_WITH_TPA_DISABLED', () => { - // Arrange - const sellingPrice = getRandomInt(100, 500); - const deductible = sellingPrice + 1; - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - noCoverage: false, - deductible: { - repair: deductible + // Assert + expect(result).toBeFalsy(); + } + ); + test('returns true when deductibleValue more than totalServicePrice, noComp is false, and policyLookupSuccessful true', () => { + // Arrange + const mainInitialState = { + order: { + policy: { + noCoverage: false, + policyLookupSuccessful: true }, - policyLookupSuccessful: true + currentDeductible: priceOfLineItems + 1 } - }, - issConfig: { - enableTPAFlow: false - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - wrapper.setData({ - availableLineItems: [ - { - sellingPrice, - kitPrice: 0, - laborAmount: 0 - } - ], - selectedProvider: 'Other' + }; + getPriceOfLineItems.mockImplementationOnce(() => priceOfLineItems); + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.verifiedITAC; + + // Assert + expect(result).toBeTruthy(); }); - - // Act - wrapper.vm.forwardButtonAction(); - - // Assert - expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED, - undefined, - {}, - { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } - ); }); - test('If No Comp and selected Safelite, navigate forward with CLICKED_FORWARD scenario', () => { - // Arrange - const mainInitialState = { - order: { - damage: { - isRepair: true - }, - policy: { - noCoverage: true, - policyLookupSuccessful: true - } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - wrapper.setData({ - isVerified: false, - selectedProvider: 'Safelite' + describe('verifiedDeductible', () => { + const servicePrice = 123; + describe('claim registration required', () => { + const issConfig = { isClaimRegistrationRequired: true }; + let verifiedDeductibleStoreState; + beforeEach(() => { + verifiedDeductibleStoreState = { + issConfig, + order: { + payment: { + insuranceCoverage: { + isVerified: true + } + }, + policy: { + noCoverage: false, + policyLookupSuccessful: false + }, + currentDeductible: servicePrice - 1 + } + }; + getPriceOfLineItems.mockImplementation(() => servicePrice); + }); + test('returns false when deductible is null', () => { + // Arrange + const mainInitialState = verifiedDeductibleStoreState; + mainInitialState.order.currentDeductible = null; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.verifiedDeductible; + + // Assert + expect(result).toBeFalsy(); + }); + test('returns false when register claim not successful', () => { + // Arrange + const mainInitialState = verifiedDeductibleStoreState; + mainInitialState.order.payment.insuranceCoverage.isVerified = false; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.verifiedDeductible; + + // Assert + expect(result).toBeFalsy(); + }); + test('returns true when register claim successful, isNoComp false, and service price over deductible', () => { + // Arrange + const { wrapper } = getMountedComponent(verifiedDeductibleStoreState); + + // Act + const result = wrapper.vm.verifiedDeductible; + + // Assert + expect(result).toBeTruthy(); + }); }); + describe('claim registration not required', () => { + const issConfig = { isClaimRegistrationRequired: false }; + let verifiedDeductibleStoreState; + beforeEach(() => { + verifiedDeductibleStoreState = { + issConfig, + order: { + payment: { + insuranceCoverage: { + isVerified: false + } + }, + policy: { + noCoverage: false, + policyLookupSuccessful: true + }, + currentDeductible: servicePrice - 1 + } + }; + }); + test('returns false when policy lookup not successful', () => { + // Arrange + const mainInitialState = verifiedDeductibleStoreState; + mainInitialState.order.policy.policyLookupSuccessful = false; + const { wrapper } = getMountedComponent(mainInitialState); - // Act - wrapper.vm.forwardButtonAction(); + // Act + const result = wrapper.vm.verifiedDeductible; - // Assert - expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith( - navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE, - undefined, - {}, - { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } - ); - }); - }); - describe('ADAS', () => { - test('if Replace and parts require recalibration, isADAS should return true', () => { - // Arrange - const mainInitialState = { - order: { - damage: { - isRepair: false + // Assert + expect(result).toBeFalsy(); + }); + test('returns true when policyLookupSuccessful, isNoComp false, and deductible over service price', () => { + // Arrange + const { wrapper } = getMountedComponent(verifiedDeductibleStoreState); + + // Act + const result = wrapper.vm.verifiedDeductible; + + // Assert + expect(result).toBeTruthy(); + }); + }); + test('returns false when isNoComp true', () => { + // Arrange + const isNoComp = true; + const storeState = { + issConfig: { + isClaimRegistrationRequired: false }, - lineItems: { - glassParts: [ - { - requiresRecalibration: true + order: { + payment: { + insuranceCoverage: { + isVerified: true } - ] + }, + policy: { + noCoverage: isNoComp, + policyLookupSuccessful: true + }, + currentDeductible: servicePrice - 1 } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); + }; + const { wrapper } = getMountedComponent(storeState); - // Assert - expect(wrapper.vm.isADAS).toBeTruthy(); - }); - test('if Replace and parts do not require recalibration, isADAS should return false', () => { - // Arrange - const mainInitialState = { - order: { - damage: { - isRepair: false + // Act + const result = wrapper.vm.verifiedDeductible; + + // Assert + expect(result).toBeFalsy(); + }); + test('returns false when service price below deductible', () => { + // Arrange + const storeState = { + issConfig: { + isClaimRegistrationRequired: false }, - lineItems: { - glassParts: [ - { - requiresRecalibration: false + order: { + payment: { + insuranceCoverage: { + isVerified: true } - ] + }, + policy: { + noCoverage: false, + policyLookupSuccessful: true + }, + currentDeductible: servicePrice + 1 } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); + }; + const { wrapper } = getMountedComponent(storeState); - // Assert - expect(wrapper.vm.isADAS).toBeFalsy(); + // Act + const result = wrapper.vm.verifiedDeductible; + + // Assert + expect(result).toBeFalsy(); + }); }); - test('if Repair, isADAS should return true', () => { - // Arrange - const mainInitialState = { - order: { - damage: { - isRepair: true + describe('isADAS', () => { + test('returns false when glassParts null', () => { + // Arrange + const mainInitialState = { + order: { + lineItems: { + glassParts: null + } } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); + }; + const { wrapper } = getMountedComponent(mainInitialState); - // Assert - expect(wrapper.vm.isADAS).toBeFalsy(); + // Act + const result = wrapper.vm.isADAS; + + // Assert + expect(result).toBeFalsy(); + }); + test('returns false when glassParts empty', () => { + // Arrange + const mainInitialState = { + order: { + lineItems: { + glassParts: [] + } + } + }; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.isADAS; + + // Assert + expect(result).toBeFalsy(); + }); + test('returns false when no parts in glassParts require recalibration', () => { + // Arrange + const mainInitialState = { + order: { + lineItems: { + glassParts: [ + { + partNumber: 123, + requiresRecalibration: false + }, + { + partNumber: 111, + requiresRecalibration: false + } + ] + } + } + }; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.isADAS; + + // Assert + expect(result).toBeFalsy(); + }); + test('returns true when a part in glassParts require recalibration', () => { + // Arrange + const mainInitialState = { + order: { + lineItems: { + glassParts: [ + { + partNumber: 123, + requiresRecalibration: true + }, + { + partNumber: 111, + requiresRecalibration: false + } + ] + } + } + }; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.isADAS; + + // Assert + expect(result).toBeTruthy(); + }); + }); + describe('isQuoteDisplayed', () => { + test('returns false when policy lookup not successful', () => { + // Arrange + const mainInitialState = { + order: { + policy: { + policyLookupSuccessful: false + } + } + }; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.isQuoteDisplayed; + + // Assert + expect(result).toBeFalsy(); + }); + describe('not no comp', () => { + const priceOfLineItems = 341; + test('returns false when deductible equal to service price', () => { + // Arrange + const mainInitialState = { + order: { + policy: { + policyLookupSuccessful: true, + noCoverage: false + }, + currentDeductible: priceOfLineItems + } + }; + getPriceOfLineItems.mockImplementationOnce(() => priceOfLineItems); + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.isQuoteDisplayed; + + // Assert + expect(result).toBeFalsy(); + }); + test('returns false when deductible less than service price', () => { + // Arrange + const mainInitialState = { + order: { + policy: { + policyLookupSuccessful: true, + noCoverage: false + }, + currentDeductible: priceOfLineItems - 1 + } + }; + getPriceOfLineItems.mockImplementationOnce(() => priceOfLineItems); + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.isQuoteDisplayed; + + // Assert + expect(result).toBeFalsy(); + }); + test('returns true when policy lookup successful and deductible over service price', () => { + // Arrange + const mainInitialState = { + order: { + policy: { + policyLookupSuccessful: true, + noCoverage: false + }, + currentDeductible: priceOfLineItems + 1 + } + }; + getPriceOfLineItems.mockImplementationOnce(() => priceOfLineItems); + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.isQuoteDisplayed; + + // Assert + expect(result).toBeTruthy(); + }); + }); + describe('not itac', () => { + const priceOfLineItems = 23; + test('returns false when isNoComp false', () => { + // Arrange + const mainInitialState = { + order: { + policy: { + policyLookupSuccessful: true, + noCoverage: false + }, + currentDeductible: priceOfLineItems + } + }; + getPriceOfLineItems.mockImplementationOnce(() => priceOfLineItems); + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.isQuoteDisplayed; + + // Assert + expect(result).toBeFalsy(); + }); + test('returns true when isNoComp true', () => { + // Arrange + const mainInitialState = { + order: { + policy: { + policyLookupSuccessful: true, + noCoverage: true + }, + currentDeductible: priceOfLineItems + } + }; + getPriceOfLineItems.mockImplementationOnce(() => priceOfLineItems); + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.isQuoteDisplayed; + + // Assert + expect(result).toBeTruthy(); + }); + }); + }); + describe.each([[0], [12]])('shouldRegisterClaim', (policyVehicleId) => { + const servicePrice = 90; + let shouldRegisterClaimStoreStateItac; + beforeEach(() => { + shouldRegisterClaimStoreStateItac = { + order: { + payment: { + insuranceCoverage: { claimNumber: null } + }, + policy: { + policyLookupSuccessful: true, + noCoverage: false + }, + vehicle: { + policyVehicleId + }, + currentDeductible: servicePrice - 1 + }, + issConfig: { + isClaimRegistrationRequired: true + } + }; + getPriceOfLineItems.mockImplementation(() => servicePrice); + }); + test('returns false when policy lookup not successful', () => { + // Arrange + const mainInitialState = shouldRegisterClaimStoreStateItac; + mainInitialState.order.policy.policyLookupSuccessful = false; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.shouldRegisterClaim; + + // Assert + expect(result).toBeFalsy(); + }); + test('returns false when policy vehicle id is less than 0', () => { + // Arrange + const mainInitialState = shouldRegisterClaimStoreStateItac; + mainInitialState.order.vehicle.policyVehicleId = -3; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.shouldRegisterClaim; + + // Assert + expect(result).toBeFalsy(); + }); + test('returns false when policy vehicle id is null', () => { + // Arrange + const mainInitialState = shouldRegisterClaimStoreStateItac; + mainInitialState.order.vehicle.policyVehicleId = null; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.shouldRegisterClaim; + + // Assert + expect(result).toBeFalsy(); + }); + test('returns false when claim registration is not required', () => { + // Arrange + const mainInitialState = shouldRegisterClaimStoreStateItac; + mainInitialState.issConfig.isClaimRegistrationRequired = false; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.shouldRegisterClaim; + + // Assert + expect(result).toBeFalsy(); + }); + test('returns false when claim already registered', () => { + // Arrange + const mainInitialState = shouldRegisterClaimStoreStateItac; + mainInitialState.order.payment.insuranceCoverage.claimNumber = 13; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.shouldRegisterClaim; + + // Assert + expect(result).toBeFalsy(); + }); + test('returns false when no comp', () => { + // Arrange + const mainInitialState = shouldRegisterClaimStoreStateItac; + mainInitialState.order.policy.noCoverage = true; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.shouldRegisterClaim; + + // Assert + expect(result).toBeFalsy(); + }); + describe('returns true when policy lookup success, vehicleId set to %p, claim reg req, claim not yet reg', () => { + test('and itac', () => { + // Arrange + const { wrapper } = getMountedComponent(shouldRegisterClaimStoreStateItac); + + // Act + const result = wrapper.vm.shouldRegisterClaim; + + // Assert + expect(result).toBeTruthy(); + }); + test.each([ + [servicePrice], + [servicePrice + 1] + ])('and deductible case', (deductibleValue) => { + // Arrange + const mainInitialState = shouldRegisterClaimStoreStateItac; + mainInitialState.currentDeductible = deductibleValue; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.shouldRegisterClaim; + + // Assert + expect(result).toBeTruthy(); + }); + }); }); }); - describe('claim registration api call', () => { - it('claim registration not required => method not called', async () => { - // Arrange - const { wrapper } = getMountedComponent({ - issConfig: { - isClaimRegistrationRequired: false - } - }); - - // Assert - expect(wrapper.vm.mainStore.registerClaim).not.toHaveBeenCalled(); - }); - - it('claim registration required => register claim method called', async () => { - // Arrange - const sellingPrice = getRandomInt(50, 100); - const deductible = sellingPrice - 1; - const initialStore = { - order: { - policy: { - policyLookupSuccessful: true, - noCoverage: false, - deductible: { - repair: deductible + describe('methods', () => { + describe('arePagePrerequisitesValid', () => { + test('returns false if car id not set', () => { + // Arrange + const mainInitialState = { + order: { + vehicle: { + carId: null } - }, - damage: { - isRepair: true } - }, - issConfig: { - isClaimRegistrationRequired: true - } - }; - const mockStoreActions = () => { - useMainStore().getWipers = jest.fn().mockImplementation(() => Promise.resolve()); - useMainStore().getRainDefense = jest.fn().mockImplementation(() => Promise.resolve()); - useMainStore().getSupportingItems = jest.fn().mockImplementation(() => Promise.resolve()); - useMainStore().getPriceOrderItems = jest.fn().mockImplementation(() => Promise.resolve([ - { - sellingPrice, - kitPrice: 0, - laborAmount: 0 + }; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.arePagePrerequisitesValid(); + + // Assert + expect(result).toBeFalsy(); + }); + test('returns false if car id set to 0', () => { + // Arrange + const mainInitialState = { + order: { + vehicle: { + carId: 0 + } } - ])); - }; - const { wrapper } = getMountedComponent(initialStore, {}, mockStoreActions); - const next = (method) => { method(wrapper.vm); }; + }; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.arePagePrerequisitesValid(); + + // Assert + expect(result).toBeFalsy(); + }); + test.each([[-1], [1], [123]])('returns true if car id set to %p', (carId) => { + // Arrange + const mainInitialState = { + order: { + vehicle: { carId } + } + }; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + const result = wrapper.vm.arePagePrerequisitesValid(); + + // Assert + expect(result).toBeTruthy(); + }); + }); + test.each([ + [0, '$0.00'], + [1, '$1.00'], + [12, '$12.00'], + [1.2, '$1.20'], + [1.25, '$1.25'], + [1.254, '$1.25'], + [1.255, '$1.26'], + [-1, '-$1.00'] + ])('getFormattedAmount given %p returns "%p"', (value, expected) => { + // Arrange + const { wrapper } = getMountedComponent(); // Act - coverageStatement.beforeRouteEnter.call(wrapper.vm, undefined, undefined, next); - for (let i = 0; i < 7; i++) { - // eslint-disable-next-line no-await-in-loop - await nextTick(); - } + const result = wrapper.vm.getFormattedAmount(value); // Assert - expect(wrapper.vm.mainStore.registerClaim).toHaveBeenCalled(); + expect(result).toBe(expected); + }); + describe('navigateForward', () => { + const servicePrice = 82; + beforeEach(() => { + getPriceOfLineItems.mockImplementation(() => servicePrice); + }); + test('If Unverified, navigate forward with CLICKED_FORWARD scenario', () => { + // Arrange + const mainInitialState = { + order: { + damage: { + isRepair: true + }, + policy: { + policyLookupSuccessful: false + }, + currentDeductible: null + } + }; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + wrapper.vm.navigateForward(); + + // Assert + expect(wrapper.vm.$router.navigate) + .toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD, + undefined + ); + }); + test('If Verified Deductible, navigate forward with CLICKED_FORWARD scenario', () => { + // Arrange + const deductible = servicePrice - 1; + const mainInitialState = { + order: { + damage: { + isRepair: true + }, + policy: { + noCoverage: false, + policyLookupSuccessful: true + }, + currentDeductible: deductible + } + }; + const { wrapper } = getMountedComponent(mainInitialState); + + // Act + wrapper.vm.navigateForward(); + + // Assert + expect(wrapper.vm.$router.navigate) + .toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD, + undefined + ); + }); + test('If Verified ITAC and selected Safelite, navigate forward with CLICKED_FORWARD_WITH_SAFELITE scenario', () => { + // Arrange + const deductible = servicePrice + 1; + const mainInitialState = { + order: { + damage: { + isRepair: true + }, + policy: { + noCoverage: false, + policyLookupSuccessful: true + }, + currentDeductible: deductible + } + }; + const { wrapper } = getMountedComponent(mainInitialState); + wrapper.setData({ + selectedProvider: SAFELITE_PROVIDER + }); + + // Act + wrapper.vm.navigateForward(); + + // Assert + expect(wrapper.vm.$router.navigate) + .toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE, + undefined + ); + }); + test('If Verified ITAC, selected other shop, navigate forward w/ CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP', () => { + // Arrange + const deductible = servicePrice + 1; + const mainInitialState = { + order: { + damage: { + isRepair: true + }, + policy: { + noCoverage: false, + policyLookupSuccessful: true + }, + currentDeductible: deductible + } + }; + const { wrapper } = getMountedComponent(mainInitialState); + wrapper.setData({ + selectedProvider: 'Other' + }); + + // Act + wrapper.vm.navigateForward(); + + // Assert + expect(wrapper.vm.$router.navigate) + .toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, + undefined + ); + }); + test('If No Comp and selected Safelite, navigate forward with CLICKED_FORWARD_WITH_SAFELITE scenario', () => { + // Arrange + const mainInitialState = { + order: { + damage: { + isRepair: true + }, + policy: { + noCoverage: true, + policyLookupSuccessful: true + } + } + }; + const { wrapper } = getMountedComponent(mainInitialState); + wrapper.setData({ + selectedProvider: SAFELITE_PROVIDER + }); + + // Act + wrapper.vm.navigateForward(); + + // Assert + expect(wrapper.vm.$router.navigate) + .toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE, + undefined + ); + }); + test('If No comp and selected other shop, navigate forward with CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP ', () => { + // Arrange + const mainInitialState = { + order: { + damage: { + isRepair: true + }, + policy: { + noCoverage: true, + policyLookupSuccessful: true + } + } + }; + const { wrapper } = getMountedComponent(mainInitialState); + wrapper.setData({ + selectedProvider: 'other' + }); + + // Act + wrapper.vm.navigateForward(); + + // Assert + expect(wrapper.vm.$router.navigate) + .toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, + undefined + ); + }); }); }); -}); - -describe('coverageStatement.vue-working', () => { describe('ITAC flag', () => { test('ITAC flag updated once component is initialized', async () => { // Arrange @@ -903,4 +1151,74 @@ describe('coverageStatement.vue-working', () => { expect(wrapper.vm.mainStore.registerClaim).toHaveBeenCalledTimes(0); }); }); + describe('claim registration api call', () => { + it('claim registration not required => method not called', async () => { + // Arrange + const mockStoreActions = () => { + useMainStore().getWipers = jest.fn().mockImplementation(() => Promise.resolve()); + useMainStore().getRainDefense = jest.fn().mockImplementation(() => Promise.resolve()); + useMainStore().getSupportingItems = jest.fn().mockImplementation(() => Promise.resolve()); + useMainStore().getPriceOrderItems = jest.fn().mockImplementation(() => Promise.resolve()); + }; + const { wrapper } = getMountedComponent({ + issConfig: { + isClaimRegistrationRequired: false + } + }, {}, mockStoreActions); + const next = (method) => { method(wrapper.vm); }; + + // Act + coverageStatement.beforeRouteEnter.call(wrapper.vm, undefined, undefined, next); + for (let i = 0; i < 7; i++) { + // eslint-disable-next-line no-await-in-loop + await nextTick(); + } + + // Assert + expect(wrapper.vm.mainStore.registerClaim).not.toHaveBeenCalled(); + }); + + it('claim registration required => register claim method called', async () => { + // Arrange + const servicePrice = 7283; + const deductible = servicePrice - 1; + const initialStore = { + order: { + payment: { + insuranceCoverage: { claimNumber: null } + }, + policy: { + policyLookupSuccessful: true, + noCoverage: false + }, + vehicle: { + policyVehicleId: 1 + }, + currentDeductible: deductible + }, + issConfig: { + isClaimRegistrationRequired: true + } + }; + getPriceOfLineItems.mockImplementation(() => servicePrice); + const mockStoreActions = () => { + useMainStore().getWipers = jest.fn().mockImplementation(() => Promise.resolve()); + useMainStore().getRainDefense = jest.fn().mockImplementation(() => Promise.resolve()); + useMainStore().getSupportingItems = jest.fn().mockImplementation(() => Promise.resolve()); + useMainStore().getPriceOrderItems = jest.fn().mockImplementation(() => Promise.resolve()); + }; + const { wrapper } = getMountedComponent(initialStore, {}, mockStoreActions); + const next = (method) => { method(wrapper.vm); }; + + // Act + coverageStatement.beforeRouteEnter.call(wrapper.vm, undefined, undefined, next); + for (let i = 0; i < 7; i++) { + // eslint-disable-next-line no-await-in-loop + await nextTick(); + } + + // Assert + expect(wrapper.vm.mainStore.registerClaim).toHaveBeenCalled(); + }); + }); }); diff --git a/src/layouts/coverage-statement/coverage-statement.vue b/src/layouts/coverage-statement/coverage-statement.vue index 7338f1ad..51f86d5e 100644 --- a/src/layouts/coverage-statement/coverage-statement.vue +++ b/src/layouts/coverage-statement/coverage-statement.vue @@ -35,26 +35,26 @@
- {{ formattedDeductible }} + {{ deductibleForDisplay }}
- {{ formattedServicePrice }} + {{ servicePriceForDisplay }}
{{ deductibleText }}  - {{ formattedDeductible }} + {{ deductibleForDisplay }}
@@ -67,18 +67,18 @@ v-html="nextStepsBody"> @@ -88,7 +88,7 @@ cmsWidgetName="SiteFooterWidget" :isForwardActionDisabled="!meta.valid" @backClicked="navigateBackByVehicleQuestions" - @forwardClicked="forwardButtonAction" /> + @forwardClicked="navigateForward" /> @@ -126,10 +126,16 @@ import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin.js'; import globalRules from '@/constants/global-rules.js'; import baseFormMixin from '@/mixins/base-form-mixin.js'; import navigationScenarios from '@/router/router-constants/navigation-scenarios.js'; -import routerParams from '@/router/router-constants/router-params'; import issPageValues from '@/router/router-constants/issPage-values'; -import bailoutCode from '@/constants/bailoutCode'; import bailoutMessage from '@/constants/bailoutMessage'; +import widgetFields from '@/constants/cms-widget-fields.js'; +import getPriceOfLineItems from '@/helpers/price-calculator.js'; + +const SAFELITE_PROVIDER = 'Safelite'; + +function setBailout(currentRoute, message) { + useMainStore().setBailout(currentRoute, message); +} export default { name: 'coverage-statement', @@ -164,8 +170,8 @@ export default { ]; const resultMap = await settleAllPromises(promiseResultMap); - const clonedGlassParts = useMainStore().order.lineItems.glassParts - ? JSON.parse(JSON.stringify(useMainStore().order.lineItems.glassParts)) + const clonedGlassParts = useMainStore().lineItems.glassParts + ? JSON.parse(JSON.stringify(useMainStore().lineItems.glassParts)) : []; const availableLineItems = [ ...(resultMap.supportingItems ?? []), @@ -174,11 +180,14 @@ export default { let hasBailedOut = false; let pricingResults = []; - if (useMainStore().order.policy.policyLookupSuccessful && useMainStore().order.vehicle.policyVehicleId >= 0) { + if (useMainStore().policy.policyLookupSuccessful && useMainStore().vehicle.policyVehicleId >= 0) { await useMainStore().getFinalDeductible(); pricingResults = await useMainStore().getPriceOrderItems(availableLineItems) .catch((err) => { - useMainStore().setBailout(to, bailoutMessage.pricingResponseError(availableLineItems.map((li) => li.partNumber), { code: err.code, message: err.message, data: err.data })); + setBailout(to, bailoutMessage.pricingResponseError( + availableLineItems.map((li) => li.partNumber), + { code: err.code, message: err.message, data: err.data } + )); hasBailedOut = true; next(`/?issPage=${issPageValues.BAILOUT_PAGE}`); }); @@ -190,22 +199,27 @@ export default { vm.setCmsContent(resultMap.cmsContent); vm.setSupportingItems(resultMap.supportingItems); // eslint-disable-next-line no-param-reassign - vm.availableLineItems = pricingResults; + vm.setBaseServiceLineItems(pricingResults); vm.$refs.loadingModal.showModal(); - vm.initializeComponent(availableLineItems); + vm.initializeComponent(); if (!vm.unverified) { useMainStore().disableKeyFields(); } }); } }, - setup() { - const mainStore = useMainStore(); - return { mainStore }; - }, data() { + const { isRepair } = useMainStore().damage; + const { policyLookupSuccessful, noCoverage } = useMainStore().policy; return { - availableLineItems: [], + currencyFormatter: new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD' + }), + isRepair, + policyLookupSuccessful, + isNoComp: noCoverage ?? false, + baseServiceLineItems: [], selectedProvider: '', deductibleText: 'Your deductible is', // TODO update when design team gives appropriate text @@ -217,84 +231,90 @@ export default { rules: { selectionRequired: globalRules.OPTION_REQUIRED }, - supportingItems: null + supportingItems: null, + widget: { + subheader: 'SiteSubHeaderWidget', + verifiedItacAlert: 'VerifiedITACAlert', + explanatoryText: 'ExplanatoryTextWidget', + nextStep: 'NextStepsWidget', + serviceProviderQuestion: 'ServiceProviderQuestion' + } }; }, computed: { - verifiedITACAlertHeader() { - return this.getCmsContent( - 'VerifiedITACAlert', - 'HeadlineText' + coverageStatementSubHeader() { + return this.getTextFromCmsWithCustomIfStatements( + this.widget.subheader, + widgetFields.SUB_HEADER_WIDGET.SUB_HEADER_TEXT ); }, - verifiedITACAlertBody() { + verifiedItacAlertHeader() { return this.getCmsContent( - 'VerifiedITACAlert', - 'BodyText' - )?.replaceAll('{custom:costSavings}', this.costSavings); + this.widget.verifiedItacAlert, + widgetFields.ALERT_WIDGET.HEADLINE_TEXT + ); }, - coverageStatementSubHeader() { - return this.getSubheaderTextFromCms('SiteSubHeaderWidget'); + verifiedItacAlertBody() { + return this.getCmsContent( + this.widget.verifiedItacAlert, + widgetFields.ALERT_WIDGET.BODY_TEXT + )?.replaceAll('{custom:costSavings}', this.itacCostSavingsForDisplay); }, secondaryText() { - return this.getSecondaryTextFromCms('SiteSubHeaderWidget'); + return this.getTextFromCmsWithCustomIfStatements( + this.widget.subheader, + widgetFields.SUB_HEADER_WIDGET.SECONDARY_TEXT + ); }, explanatoryText() { - return this.getExplantoryTextFromCms('ExplanatoryTextWidget'); + return this.getTextFromCmsWithCustomIfStatements( + this.widget.explanatoryText, + widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT + ); }, nextStepsHeader() { - return this.getHeaderTextFromCms('NextStepsWidget'); + return this.getTextFromCmsWithCustomIfStatements( + this.widget.nextStep, + widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT + ); }, nextStepsBody() { - return this.getBodyTextFromCms('NextStepsWidget')?.replaceAll('{custom:damage}', this.damageText); - }, - continueWithSchedulingBodyText() { - return this.getCmsContent('continueWithSchedulingCopy', 'BodyText'); - }, - unverifiedADASNextStepsBodyText() { - return this.getCmsContent('UnverifiedADASNextStepsWidget', 'BodyText')?.replaceAll('{custom:damage}', this.damageText); - }, - unverifiedNonADASNextStepsBodyText() { - return this.getCmsContent('UnverifiedNonADASNextStepsWidget', 'BodyText')?.replaceAll('{custom:damage}', this.damageText); - }, - unverifiedNonADASRepairBodyText() { - return this.getCmsContent('UnverifiedNonADASRepairWidget', 'BodyText'); + return this.getTextFromCmsWithCustomIfStatements( + this.widget.nextStep, + widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT + )?.replaceAll('{custom:damage}', this.damageText); }, damageText() { const damageString = getDamageString(); return damageString === 'match' ? '' : damageString; }, - vehicleDeductible() { - const deductible = useMainStore().order.currentDeductible; - return deductible; + deductibleValue() { + return useMainStore().order.currentDeductible; }, - formattedDeductible() { - return this.getDeductibleString(this.vehicleDeductible); - }, - isDeductibleZero() { - return this.vehicleDeductible === 0; - }, - policyLookupSuccessful() { - return useMainStore().order.policy.policyLookupSuccessful; + deductibleForDisplay() { + return this.getFormattedAmount(this.deductibleValue); }, registerClaimSuccessful() { return useMainStore().payment.insuranceCoverage.isVerified; }, verifiedNoComp() { - return this.policyLookupSuccessful ? useMainStore().order.policy.noCoverage : false; + return this.policyLookupSuccessful && this.isNoComp; }, verifiedITAC() { return this.policyLookupSuccessful - && !this.verifiedNoComp - && this.vehicleDeductible > this.totalServicePrice; + && !this.isNoComp + && this.deductibleValue > this.totalServicePrice; }, coveredAndServicePriceAboveOrEqualDeductible() { - return !this.verifiedNoComp && this.totalServicePrice >= this.vehicleDeductible; + return !this.verifiedNoComp && this.totalServicePrice >= this.deductibleValue; }, verifiedDeductible() { return useMainStore().isClaimRegistrationRequired - ? this.registerClaimSuccessful && this.coveredAndServicePriceAboveOrEqualDeductible && this.vehicleDeductible !== null - : this.policyLookupSuccessful && this.coveredAndServicePriceAboveOrEqualDeductible; + ? this.registerClaimSuccessful + && this.coveredAndServicePriceAboveOrEqualDeductible + && this.deductibleValue !== null + : this.policyLookupSuccessful + && this.coveredAndServicePriceAboveOrEqualDeductible; }, unverified() { return !this.verifiedDeductible && !this.verifiedITAC && !this.verifiedNoComp; @@ -303,41 +323,46 @@ export default { const parts = useMainStore().order.lineItems.glassParts; return parts !== null && !!parts.find((part) => part.requiresRecalibration); }, - isRepair() { - return useMainStore().order.damage.isRepair; - }, totalServicePrice() { - let total = 0; - this.availableLineItems.forEach((lineItem) => { - total += this.getTotalLineItemPrice(lineItem); - }); - return total; + return getPriceOfLineItems(this.baseServiceLineItems); }, - formattedServicePrice() { - return this.getServicePriceString(this.totalServicePrice); + servicePriceForDisplay() { + return this.getFormattedAmount(this.totalServicePrice); }, - costSavings() { - const savings = this.getITACCostSavings(this.vehicleDeductible, this.totalServicePrice); - const formattedSavings = parseFloat(savings).toFixed(2); - return `$${formattedSavings}`; + itacCostSavings() { + return this.deductibleValue - this.totalServicePrice; }, - questionText() { - return this.getCmsContent('ServiceProviderQuestion', 'QuestionText'); + itacCostSavingsForDisplay() { + return this.getFormattedAmount(this.itacCostSavings); }, - answersFromCms() { - return this.getCmsContent('ServiceProviderQuestion', 'Answers'); + serviceProviderQuestionText() { + return this.getCmsContent( + this.widget.serviceProviderQuestion, + widgetFields.INPUT_QUESTION_WIDGET.QUESTION_TEXT + ); }, - displayQuote() { + serviceProviderQuestionAnswers() { + return this.getCmsContent( + this.widget.serviceProviderQuestion, + widgetFields.INPUT_QUESTION_WIDGET.ANSWERS + ); + }, + isQuoteDisplayed() { return this.verifiedITAC || this.verifiedNoComp; + }, + shouldRegisterClaim() { + return this.policyLookupSuccessful + && useMainStore().vehicle.policyVehicleId != null + && useMainStore().vehicle.policyVehicleId >= 0 + && useMainStore().isClaimRegistrationRequired + && !useMainStore().isClaimAlreadyRegistered + && (this.coveredAndServicePriceAboveOrEqualDeductible || this.verifiedITAC); } }, watch: { selectedProvider() { - if (this.selectedProvider === 'Safelite') { - this.$refs.siteFooter.updateButtonText('Continue with Safelite'); - } else { - this.$refs.siteFooter.updateButtonText('Continue'); - } + const buttonText = this.selectedProvider === SAFELITE_PROVIDER ? 'Continue with Safelite' : 'Safelite'; + this.$refs.siteFooter.updateButtonText(buttonText); }, nextStepsBody(newValue, oldValue) { if (newValue !== oldValue) { @@ -353,78 +378,43 @@ export default { arePagePrerequisitesValid() { return !!useMainStore().vehicle.carId; }, + getFormattedAmount(amount) { + return this.currencyFormatter.format(amount); + }, async initializeComponent() { useMainStore().updatePolicyITACFlag(this.verifiedITAC); - if (this.policyLookupSuccessful - && useMainStore().order.vehicle.policyVehicleId >= 0 - && useMainStore().isClaimRegistrationRequired - && !useMainStore().isClaimAlreadyRegistered - && (this.coveredAndServicePriceAboveOrEqualDeductible || this.verifiedITAC)) { + if (this.shouldRegisterClaim) { await useMainStore().registerClaim()?.catch(() => {}); } this.$refs.loadingModal.hideModal(); }, - async forwardButtonAction() { - return this.navigateForward(); - }, async navigateForward() { if (this.unverified || this.verifiedDeductible) { useMainStore().updateSupportingItems(this.supportingItems); - this.$router.navigate( - navigationScenarios.CLICKED_FORWARD, - this.$route, - {}, - { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } - ); + this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD); } else if (this.verifiedITAC || this.verifiedNoComp) { - useMainStore().updateIsSafeliteProvider(this.selectedProvider === 'Safelite'); - if (this.selectedProvider === 'Safelite') { + useMainStore().updateIsSafeliteProvider(this.selectedProvider === SAFELITE_PROVIDER); + if (this.selectedProvider === SAFELITE_PROVIDER) { useMainStore().updateSupportingItems(this.supportingItems); - this.$router.navigate( - navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE, - this.$route, - {}, - { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } - ); + this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE); } else { - this.mainStore.setBailout(this.$router.currentRoute, bailoutMessage.RequestCallback()); - this.$router.navigate( - navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, - this.$route, - {}, - { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } - ); + this.setBailoutWithMessage(bailoutMessage.RequestCallback()); + this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP); } } else { - this.mainStore.setBailout(this.$router.currentRoute, bailoutMessage.coverageStatementInvalidState()); - this.$router.navigate( - navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE, - this.$route, - {}, - { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } - ); + this.setBailoutWithMessage(bailoutMessage.coverageStatementInvalidState()); + this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE); } }, - processIfStatements, - getHeaderTextFromCms(cmsWidgetName) { - const header = this.getCmsContent(cmsWidgetName, 'HeaderText'); - return this.processIfStatements(header, 'custom', this.getCustomValueFromString); + setBailoutWithMessage(message) { + setBailout(this.$router.currentRoute, message); }, - getSubheaderTextFromCms(cmsWidgetName) { - const subHeader = this.getCmsContent(cmsWidgetName, 'SubHeaderText'); - return this.processIfStatements(subHeader, 'custom', this.getCustomValueFromString); + navigateWithScenario(scenario) { + this.$router.navigate(scenario, this.$route); }, - getBodyTextFromCms(cmsWidgetName) { - const bodyText = this.getCmsContent(cmsWidgetName, 'BodyText'); - return this.processIfStatements(bodyText, 'custom', this.getCustomValueFromString); - }, - getSecondaryTextFromCms(cmsWidgetName) { - const secondaryText = this.getCmsContent(cmsWidgetName, 'SecondaryText'); - return this.processIfStatements(secondaryText, 'custom', this.getCustomValueFromString); - }, - getExplantoryTextFromCms(cmsWidgetName) { - const explanatoryText = this.getCmsContent(cmsWidgetName, 'BodyText'); - return this.processIfStatements(explanatoryText, 'custom', this.getCustomValueFromString); + getTextFromCmsWithCustomIfStatements(widgetName, widgetField) { + const rawText = this.getCmsContent(widgetName, widgetField); + return processIfStatements(rawText, 'custom', this.getCustomValueFromString); }, getCustomValueFromString(str) { switch (str) { @@ -443,29 +433,18 @@ export default { case 'nonADASRepair': return this.isRepair; case 'deductibleOverZero': - return this.verifiedDeductible && !this.isDeductibleZero; // TODO what if deductible is negative? + return this.verifiedDeductible && this.deductibleValue !== 0; // TODO what if deductible is negative? case 'isDeductibleZero': - return this.verifiedDeductible && this.isDeductibleZero; + return this.verifiedDeductible && this.deductibleValue === 0; default: return null; } }, - getTotalLineItemPrice(lineItem) { - return lineItem.kitPrice + lineItem.laborAmount + lineItem.sellingPrice; - }, - getDeductibleString(deductible) { - const formattedDeductibleFloat = parseFloat(deductible).toFixed(2); - return `$${formattedDeductibleFloat}`; - }, - getServicePriceString(price) { - const formattedPriceFloat = parseFloat(price).toFixed(2); - return `$${formattedPriceFloat}`; - }, - getITACCostSavings(vehicleDeductible, totalServicePrice) { - return vehicleDeductible - totalServicePrice; - }, setSupportingItems(newSupportingItems) { this.supportingItems = newSupportingItems; + }, + setBaseServiceLineItems(lineItems) { + this.baseServiceLineItems = lineItems; } } }; @@ -475,7 +454,7 @@ export default { .cost { color: $green; font-size: 2rem; - font-weight: 300; + font-weight: $font-weight-light; line-height: 2.75rem; } diff --git a/src/layouts/duplicate-check/duplicate-check.spec.js b/src/layouts/duplicate-check/duplicate-check.spec.js index 13d00c9f..22c60c38 100644 --- a/src/layouts/duplicate-check/duplicate-check.spec.js +++ b/src/layouts/duplicate-check/duplicate-check.spec.js @@ -136,7 +136,8 @@ describe('duplicateCheck.vue', () => { expect(wrapper.vm.duplicateOrders[0]).toStrictEqual({ Text: duplicateOrderText, Name: referralNumber, - SubText: expectedSubtext + SubText: expectedSubtext, + value: useMainStore().applicationUser.duplicateOrders[0] }); }); test('duplicateOrder in store with null vehicle make => returns order with only date subtext', () => { @@ -173,7 +174,8 @@ describe('duplicateCheck.vue', () => { expect(wrapper.vm.duplicateOrders[0]).toStrictEqual({ Text: duplicateOrderText, Name: referralNumber, - SubText: expectedSubtext + SubText: expectedSubtext, + value: useMainStore().applicationUser.duplicateOrders[0] }); }); test('duplicateOrder in store with null vehicle model => returns order with only date subtext', () => { @@ -210,7 +212,8 @@ describe('duplicateCheck.vue', () => { expect(wrapper.vm.duplicateOrders[0]).toStrictEqual({ Text: duplicateOrderText, Name: referralNumber, - SubText: expectedSubtext + SubText: expectedSubtext, + value: useMainStore().applicationUser.duplicateOrders[0] }); }); test('duplicateOrder in store with all vehicle info => returns order with year, make, model and date in subtext', () => { @@ -248,7 +251,8 @@ describe('duplicateCheck.vue', () => { expect(wrapper.vm.duplicateOrders[0]).toStrictEqual({ Text: duplicateOrderText, Name: referralNumber, - SubText: `${expectedVehicle}, ${expectedDate}` + SubText: `${expectedVehicle}, ${expectedDate}`, + value: useMainStore().applicationUser.duplicateOrders[0] }); }); }); @@ -352,7 +356,7 @@ describe('duplicateCheck.vue', () => { test('Selected duplicate => load session called', async () => { // Arrange const newOrderSelectionName = getRandomString(6, 6); - const selectedAnswer = getRandomString(6, 6); + const selectedAnswer = {}; const mountOptions = getMountOptions({ router: { navigate: jest.fn() } @@ -405,7 +409,7 @@ describe('duplicateCheck.vue', () => { test('Load session throws error => still navigate forward', async () => { // Arrange const newOrderSelectionName = getRandomString(6, 6); - const selectedAnswer = getRandomString(6, 6); + const selectedAnswer = {}; const mountOptions = getMountOptions({ router: { navigate: jest.fn() } diff --git a/src/layouts/duplicate-check/duplicate-check.vue b/src/layouts/duplicate-check/duplicate-check.vue index 7245adef..4a842ba1 100644 --- a/src/layouts/duplicate-check/duplicate-check.vue +++ b/src/layouts/duplicate-check/duplicate-check.vue @@ -75,7 +75,7 @@ export default { }, data() { return { - selectedAnswer: '', + selectedAnswer: null, widget: { siteHeader: 'SiteHeaderWidget', siteSubHeader: 'SiteSubHeaderWidget', @@ -113,7 +113,8 @@ export default { return { Text: duplicateOrderText, Name: o.referralNumber, - SubText: toTitleCase(subtext) + SubText: toTitleCase(subtext), + value: o }; }) ?? []; }, @@ -126,13 +127,16 @@ export default { * @summary Steps to perform when forward button clicked. */ async forwardButtonAction() { - if (this.selectedAnswer !== this.getNewOrderSelectionName) { - await useMainStore().loadSession() - .then(() => {}, () => {}) - .finally(() => { this.navigateForward(); }); - } else { - this.navigateForward(); + if (this.selectedAnswer !== null && typeof this.selectedAnswer === 'object') { + await useMainStore().loadSession(this.selectedAnswer) + .catch(() => {}) + .finally(() => { + this.navigateForward(); + }); + return; } + + this.navigateForward(); }, navigateForward() { if (!this.mainStore.order.policy.policyLookupSuccessful) { diff --git a/src/layouts/order-confirmation/order-confirmation.spec.js b/src/layouts/order-confirmation/order-confirmation.spec.js index 5883b9d1..99ba8fa7 100644 --- a/src/layouts/order-confirmation/order-confirmation.spec.js +++ b/src/layouts/order-confirmation/order-confirmation.spec.js @@ -15,7 +15,7 @@ jest.mock('@/helpers/cms-content-helper', () => ({ fetchCmsContentForPage: jest.fn(), processIfStatements: jest.fn() })); -const wordingText = 'wording Text {custom:address}'; +const wordingText = 'wording text {custom:address}'; const mockMixin = { methods: { @@ -67,6 +67,55 @@ const initialStore = { } }; +const sessionStorage = { + schedule: { + date: '2024-03-01', + startTime: '09:00', + endTime: '10:00', + jobMinMinutes: 60, + jobMaxMinutes: 90 + }, + serviceLocation: { + address: '123 Test Way', + address2: '#1', + city: 'Mesa', + state: 'AZ', + zipCode: '12345', + appointmentType: 'Inshop', + provider: { + address: { + streetAddress: '123 Safelite Street', + city: 'Mesa', + state: 'AZ', + zipCode: '12345' + } + } + } +}; + +const sessionStorageMock = (() => { + let sessionStore = {}; + + return { + getItem(key) { + return sessionStore[key] || null; + }, + setItem(key, value) { + sessionStore[key] = value.toString(); + }, + removeItem(key) { + delete sessionStore[key]; + }, + clear() { + sessionStore = {}; + } + }; +})(); + +Object.defineProperty(window, 'sessionStorage', { + value: sessionStorageMock +}); + function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}) { const mountOptions = getMountOptions({ router: { @@ -107,9 +156,16 @@ function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRu } describe('OrderConfirmation.vue', () => { + beforeEach(() => { + window.sessionStorage.clear(); + }); + afterEach(() => { + window.sessionStorage.removeItem('submittedOrder'); + }); describe('Rendering', () => { test('Should render Site Header', () => { // Arrange + window.sessionStorage.setItem('submittedOrder', JSON.stringify(sessionStorage)); const { wrapper } = getMountedComponent(initialStore); // Act @@ -120,6 +176,7 @@ describe('OrderConfirmation.vue', () => { }); test('Should render Vehicle Banner', () => { // Arrange + window.sessionStorage.setItem('submittedOrder', JSON.stringify(sessionStorage)); const { wrapper } = getMountedComponent(initialStore); // Act @@ -130,6 +187,7 @@ describe('OrderConfirmation.vue', () => { }); test('If Advanced flow, should display Site Footer', () => { // Arrange + window.sessionStorage.setItem('submittedOrder', JSON.stringify(sessionStorage)); const testStore = { order: { schedule: { @@ -154,6 +212,7 @@ describe('OrderConfirmation.vue', () => { }); test('If Essential flow, should not display Site Footer', () => { // Arrange + window.sessionStorage.setItem('submittedOrder', JSON.stringify(sessionStorage)); const { wrapper } = getMountedComponent(initialStore); // Act @@ -166,6 +225,7 @@ describe('OrderConfirmation.vue', () => { describe('Navigation', () => { test('If Advanced flow, forward button action navigates to carrier URL', () => { // Arrange + window.sessionStorage.setItem('submittedOrder', JSON.stringify(sessionStorage)); const carrierReturnUrl = 'testURL'; const testStore = { order: { @@ -193,6 +253,7 @@ describe('OrderConfirmation.vue', () => { describe('Computed properties', () => { test('appointmentDateFormatted should return date in expected format', () => { // Arrange + window.sessionStorage.setItem('submittedOrder', JSON.stringify(sessionStorage)); const { wrapper } = getMountedComponent(initialStore); // Act @@ -203,19 +264,18 @@ describe('OrderConfirmation.vue', () => { }); test('appointmentTimeFormatted should return Mobile time in expected format', () => { // Arrange - const testStore = { - order: { - schedule: { - date: '2019-01-01', - startTime: '09:00', - endTime: '10:00' - }, - serviceLocation: { - appointmentType: 'Mobile' - } + const testSessionStorage = { + schedule: { + date: '2019-01-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + appointmentType: 'Mobile' } }; - const { wrapper } = getMountedComponent(testStore); + window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); + const { wrapper } = getMountedComponent(); // Act const testValue = wrapper.vm.appointmentTimeFormatted; @@ -225,19 +285,26 @@ describe('OrderConfirmation.vue', () => { }); test('appointmentTimeFormatted should return Drop Off time in expected format', () => { // Arrange - const testStore = { - order: { - schedule: { - date: '2019-01-01', - startTime: '09:00', - endTime: '10:00' - }, - serviceLocation: { - appointmentType: 'Dropoff' + const testSessionStorage = { + schedule: { + date: '2019-01-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + appointmentType: 'Dropoff', + provider: { + address: { + streetAddress: '123 Safelite Street', + city: 'Mesa', + state: 'AZ', + zipCode: '12345' + } } } }; - const { wrapper } = getMountedComponent(testStore); + window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); + const { wrapper } = getMountedComponent(); // Act const testValue = wrapper.vm.appointmentTimeFormatted; @@ -247,6 +314,7 @@ describe('OrderConfirmation.vue', () => { }); test('appointmentTimeFormatted should return In Shop time in expected format', () => { // Arrange + window.sessionStorage.setItem('submittedOrder', JSON.stringify(sessionStorage)); const { wrapper } = getMountedComponent(initialStore); // Act @@ -257,111 +325,162 @@ describe('OrderConfirmation.vue', () => { }); test('appointmentWordingText should return Mobile text in expected format', () => { // Arrange - const testStore = { - order: { - schedule: { - date: '2019-01-01', - startTime: '09:00', - endTime: '10:00' - }, - serviceLocation: { - address: '123 Test Way', - address2: '#1', - city: 'Mesa', - state: 'AZ', - zipCode: '12345', - appointmentType: 'Mobile' - } + const testSessionStorage = { + schedule: { + date: '2019-01-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + address: '123 Test Way', + address2: '#1', + city: 'Mesa', + state: 'AZ', + zipCode: '12345', + appointmentType: 'Mobile' } }; - const { wrapper } = getMountedComponent(testStore); + window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); + const { wrapper } = getMountedComponent(); // Act const testValue = wrapper.vm.appointmentWordingText; // Assert - expect(testValue).toEqual('wording Text
123 Test Way, #1,
Mesa, AZ 12345
'); + expect(testValue).toEqual('wording text 123 Test Way, #1,
Mesa, AZ 12345'); }); test('appointmentWordingText should return Drop Off text in expected format', () => { // Arrange - const testStore = { - order: { - schedule: { - date: '2019-01-01', - startTime: '09:00', - endTime: '10:00' + const testSessionStorage = { + schedule: { + date: '2019-01-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + provider: { + address: { + streetAddress: '123 Safelite Street', + city: 'Mesa', + state: 'AZ', + zipCode: '12345' + } }, - serviceLocation: { - provider: { - address: { - streetAddress: '123 Safelite Street', - city: 'Mesa', - state: 'AZ', - zipCode: '12345' - } - }, - appointmentType: 'Dropoff' - } + appointmentType: 'Dropoff' } }; - const { wrapper } = getMountedComponent(testStore); + window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); + const { wrapper } = getMountedComponent(); // Act const testValue = wrapper.vm.appointmentWordingText; // Assert - expect(testValue).toEqual('wording Text
123 Safelite Street,
Mesa, AZ 12345
'); + expect(testValue).toEqual('wording text 123 Safelite Street,
Mesa, AZ 12345'); }); test('appointmentWordingText should return In Shop text in expected format', () => { // Arrange - const { wrapper } = getMountedComponent(initialStore); + const testSessionStorage = { + schedule: { + date: '2019-01-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + provider: { + address: { + streetAddress: '123 Safelite Street', + city: 'Mesa', + state: 'AZ', + zipCode: '12345' + } + }, + appointmentType: 'Inshop' + } + }; + window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); + const { wrapper } = getMountedComponent(); // Act const testValue = wrapper.vm.appointmentWordingText; // Assert - expect(testValue).toEqual('wording Text
123 Safelite Street,
Mesa, AZ 12345
'); + expect(testValue).toEqual('wording text 123 Safelite Street,
Mesa, AZ 12345'); }); test('serviceLocationFullAddress should return text in expected format', () => { // Arrange - const { wrapper } = getMountedComponent(initialStore); + const testSessionStorage = { + schedule: { + date: '2019-01-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + address: '123 Test Way', + address2: '#1', + city: 'Mesa', + state: 'AZ', + zipCode: '12345', + appointmentType: 'Mobile' + } + }; + window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); + const { wrapper } = getMountedComponent(); // Act const testValue = wrapper.vm.serviceLocationFullAddress; // Assert - expect(testValue).toEqual('
123 Test Way, #1,
Mesa, AZ 12345
'); + expect(testValue).toEqual('123 Test Way, #1,
Mesa, AZ 12345'); }); test('providerFullAddress should return text in expected format', () => { // Arrange - const { wrapper } = getMountedComponent(initialStore); + const testSessionStorage = { + schedule: { + date: '2019-01-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + provider: { + address: { + streetAddress: '123 Safelite Street', + city: 'Mesa', + state: 'AZ', + zipCode: '12345' + } + }, + appointmentType: 'Inshop' + } + }; + window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); + const { wrapper } = getMountedComponent(); // Act const testValue = wrapper.vm.providerFullAddress; // Assert - expect(testValue).toEqual('
123 Safelite Street,
Mesa, AZ 12345
'); + expect(testValue).toEqual('123 Safelite Street,
Mesa, AZ 12345'); }); test('appointmentWordingText2 should return Mobile text in expected format', () => { // Arrange - const testStore = { - order: { - schedule: { - date: '2019-01-01', - startTime: '09:00', - endTime: '10:00' - }, - serviceLocation: { - address: '123 Test Way', - address2: '#1', - city: 'Mesa', - state: 'AZ', - zipCode: '12345', - appointmentType: 'Mobile' - } + const testSessionStorage = { + schedule: { + date: '2019-01-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + address: '123 Test Way', + address2: '#1', + city: 'Mesa', + state: 'AZ', + zipCode: '12345', + appointmentType: 'Mobile' } }; - const { wrapper } = getMountedComponent(testStore); + window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); + const { wrapper } = getMountedComponent(); // Act const testValue = wrapper.vm.appointmentWordingText2; @@ -371,27 +490,26 @@ describe('OrderConfirmation.vue', () => { }); test('appointmentWordingText2 should return Drop Off and text in expected format', () => { // Arrange - const testStore = { - order: { - schedule: { - date: '2019-01-01', - startTime: '09:00', - endTime: '10:00' + const testSessionStorage = { + schedule: { + date: '2019-01-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + provider: { + address: { + streetAddress: '123 Safelite Street', + city: 'Mesa', + state: 'AZ', + zipCode: '12345' + } }, - serviceLocation: { - provider: { - address: { - streetAddress: '123 Safelite Street', - city: 'Mesa', - state: 'AZ', - zipCode: '12345' - } - }, - appointmentType: 'Dropoff' - } + appointmentType: 'Dropoff' } }; - const { wrapper } = getMountedComponent(testStore); + window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); + const { wrapper } = getMountedComponent(); // Act const testValue = wrapper.vm.appointmentWordingText2; @@ -402,7 +520,26 @@ describe('OrderConfirmation.vue', () => { }); test('appointmentWordingText2 should return In Shop text in expected format', () => { // Arrange - const { wrapper } = getMountedComponent(initialStore); + const testSessionStorage = { + schedule: { + date: '2019-01-01', + startTime: '09:00', + endTime: '10:00' + }, + serviceLocation: { + provider: { + address: { + streetAddress: '123 Safelite Street', + city: 'Mesa', + state: 'AZ', + zipCode: '12345' + } + }, + appointmentType: 'Inshop' + } + }; + window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); + const { wrapper } = getMountedComponent(); // Act const testValue = wrapper.vm.appointmentWordingText2; diff --git a/src/layouts/order-confirmation/order-confirmation.vue b/src/layouts/order-confirmation/order-confirmation.vue index 6d94001d..fa7a4c14 100644 --- a/src/layouts/order-confirmation/order-confirmation.vue +++ b/src/layouts/order-confirmation/order-confirmation.vue @@ -180,7 +180,7 @@ export default { }, providerFullAddress() { // eslint-disable-next-line max-len - return `
${this.providerAddress},
${this.providerCity}, ${this.providerState} ${this.providerZipCode}
`; + return `${this.providerAddress},
${this.providerCity}, ${this.providerState} ${this.providerZipCode}`; }, appointmentWordingText() { switch (this.appointmentType) { diff --git a/src/layouts/payment-page/payment-page.spec.js b/src/layouts/payment-page/payment-page.spec.js index 07822757..4aea7c28 100644 --- a/src/layouts/payment-page/payment-page.spec.js +++ b/src/layouts/payment-page/payment-page.spec.js @@ -264,44 +264,6 @@ describe('payment-page.vue', () => { }); }); - describe('beforeRouteEnter', () => { - test('Properly initializes fields and kicks off hop', async () => { - // Arrange - const vmMock = { - setCmsContent: jest.fn(), - $refs: { - cart: { - cartItems: [] - } - }, - getPiaLineItems: jest.fn(), - fetchSignatureInfo: jest.fn(), - setIFrameListener: jest.fn(), - - $nextTick: (f) => { - f(); - } - }; - - const nextF = (f) => { - f(vmMock); - }; - - // Act - await payment.beforeRouteEnter.call( - vmMock, - { query: { issPage: 'payment-page' } }, - undefined, - nextF - ); - - // Assert - expect(vmMock.setCmsContent).toBeCalled(); - expect(vmMock.fetchSignatureInfo).toBeCalled(); - expect(vmMock.setIFrameListener).toBeCalled(); - }); - }); - describe('payment type mapping', () => { describe('getPaymentType', () => { test('Maps AFTERPAY -> hopPaymentMethods.AFTERPAY', () => { diff --git a/src/layouts/payment-page/payment-page.vue b/src/layouts/payment-page/payment-page.vue index 85c7f707..b230d5ee 100644 --- a/src/layouts/payment-page/payment-page.vue +++ b/src/layouts/payment-page/payment-page.vue @@ -637,13 +637,7 @@ export default { }, getPayInAdvanceLineItems(cartItems) { const { glassParts } = useMainStore().order.lineItems; - let lineItems = []; - - if (glassParts === null) { - lineItems = ['Labor|0|1', 'Repair supplies|0|1']; - } else { - lineItems = ['Parts and labor|0|1']; - } + const lineItems = (glassParts === null) ? ['Labor|0|1', 'Repair supplies|0|1'] : ['Parts and labor|0|1']; cartItems.forEach((item) => { if (item.name !== null && item.category !== 'promos') { @@ -695,8 +689,7 @@ export default { handleIFrameContentWindowMessage(event) { if (typeof event.data === 'string') { if (event.data.indexOf('afterpayClosed') > -1) { - console.log('trigger back button...'); - // this.backButtonAction(); + this.backButtonAction(); } if (event.data.indexOf('creditCardSubmit') > -1) { this.setUIBlock(true); diff --git a/src/router/index.js b/src/router/index.js index 36f212b5..8b1e4823 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -138,12 +138,12 @@ router.beforeEach(async (to, from) => { const notToPayInAdvanceReturn = toQueryPage !== issPageValues.PAYMENT_RETURN; const isInIframe = fromQueryPage === issPageValues.PAYMENT_PAGE; - // fromPaymentToConfirmation workaround for navigating from an iframe but + // isFromPaymentPageToOrderConfirmation workaround for navigating from an iframe but // isInIframe evaluates to false for some reason when navigating from payment to confirmation - const fromPaymentToConfirmation = + const isFromPaymentPageToOrderConfirmation = fromQueryPage === issPageValues.PAYMENT_PAGE && toQueryPage === issPageValues.ORDER_CONFIRMATION; - if ((isInIframe && notToPayInAdvanceReturn) || fromPaymentToConfirmation) { + if ((isInIframe && notToPayInAdvanceReturn) || isFromPaymentPageToOrderConfirmation) { // need to set window.top.location.href directly when navigating out of an iframe // especially when navigating with browser buttons const newUrl = `${window.top.location.origin}${to.href}`; diff --git a/src/store/index.js b/src/store/index.js index f23804ad..41d4aa63 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -11,7 +11,7 @@ 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'; -import { AppointmentTypeStrings, PREMIUM_FEE_PART_TYPE } from '@/constants/schedule-constants'; +import { AppointmentTypeStrings } from '@/constants/schedule-constants'; import { deepClone } from '@/helpers/object-helper'; import { convertDateStringToDate, getDateDifferenceInDays, militaryToTwelveHourTime } from '@/helpers/date-helper'; import { paymentMethods } from '@/constants/payment-method-constants'; @@ -902,10 +902,8 @@ export const useMainStore = defineStore({ }, async getWipers() { const { carId } = this.order.vehicle; - // WARNING - // TODO: this is temp test code until serviceLocation is complete. const serviceZipCode = this.order.serviceLocation.zipCode; - // const serviceZipCode = '44902'; + return globalMethods .callHttpClient({ method: endpoints.GetWipers.method, @@ -1287,20 +1285,17 @@ export const useMainStore = defineStore({ }); }, - async loadSession() { + async loadSession(duplicate) { const { applicationUser, order, issConfig } = this; - // TODO how to get savedSessionId for a duplicate referral? - try { const response = await globalMethods.callHttpClient({ method: endpoints.LoadSession.method, endpoint: endpoints.LoadSession.url, payload: { - savedSessionId: applicationUser.savedSessionId?.toString(), - referralNumber: order.referralNumber?.toString(), - referralDate: order.referralDate?.toString(), + referralNumber: duplicate.referralNumber, + referralDate: duplicate.responseDate, parentAccountNumber: issConfig.parentAccountNumber, - referralCorrelationId: order.referralCorrelationId + referralCorrelationId: duplicate.referralCorrelationId } }); const { data } = response; @@ -1309,10 +1304,6 @@ export const useMainStore = defineStore({ return response; } - applicationUser.crmCustomerId = data.applicationUser?.crmCustomerId; - applicationUser.experiments = data.applicationUser?.experiments ?? []; - applicationUser.savedSessionId = data.applicationUser?.savedSessionId; - if (order.policy.policyLookupSuccessful) { order.customer.emailAddress = data.customer?.emailAddress; order.customer.firstName = data.customer?.firstName; diff --git a/src/store/store.spec.js b/src/store/store.spec.js index c15352b7..736b9cbf 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -961,10 +961,7 @@ describe('Store', () => { describe('loadSession method', () => { describe('successful method call', () => { const applicationUser = { - crmCustomerId: getRandomString(6, 6), - experiments: getRandomString(6, 6), - pageData: getRandomString(6, 6), - savedSessionId: getRandomString(6, 6) + experiments: getRandomString(6, 6) }; const vehicle = { year: getRandomString(6, 6), @@ -1006,9 +1003,13 @@ describe('Store', () => { it('calls load session api endpoint', async () => { // Arrange globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({ data: {} })); + const duplicate = { + referralNumber: getRandomString(6, 6), + referralCorrelationId: getRandomGuid() + }; // Act - store.loadSession(); + store.loadSession(duplicate); // Asserts expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining({ @@ -1020,34 +1021,31 @@ describe('Store', () => { // Arrange const response = { data: { ReferralNumber: getRandomString(6, 6) } }; globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(response)); + const duplicate = { + referralNumber: getRandomString(6, 6), + referralCorrelationId: getRandomGuid() + }; // Act - const result = store.loadSession(); + const result = store.loadSession(duplicate); // Asserts await expect(result).resolves.toBe(response.data); }); - it('sets expected application user data', async () => { - // Arrange - globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(fullApiResponse)); - - // Act - await store.loadSession(); - - // Asserts - expect(store.applicationUser.experiments).toEqual(applicationUser.experiments); - expect(store.applicationUser.savedSessionId).toBe(applicationUser.savedSessionId); - expect(store.applicationUser.crmCustomerId).toBe(applicationUser.crmCustomerId); - }); it('sets expected vehicle data', async () => { // Arrange globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(fullApiResponse)); + const duplicate = { + referralNumber: getRandomString(6, 6), + referralCorrelationId: getRandomGuid() + }; + store.order.policy.policyLookupSuccessful = true; store.policy.vehicles = [{ vin: vehicle.vin }]; // Act - await store.loadSession(); + await store.loadSession(duplicate); // Asserts expect(store.vehicle.year).toBe(vehicle.year); @@ -1061,11 +1059,16 @@ describe('Store', () => { // Arrange globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(fullApiResponse)); + const duplicate = { + referralNumber: getRandomString(6, 6), + referralCorrelationId: getRandomGuid() + }; + store.order.policy.policyLookupSuccessful = true; store.policy.vehicles = [{ vin: vehicle.vin }]; // Act - await store.loadSession(); + await store.loadSession(duplicate); // Asserts expect(store.order.customer.address.streetAddress).toBe(customer.address.streetAddress); @@ -1081,10 +1084,14 @@ describe('Store', () => { it('sets expected remaining order data', async () => { // Arrange globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(fullApiResponse)); - const originalWorkOrderNumber = store.order.workOrderNumber; + + const duplicate = { + referralNumber: getRandomString(6, 6), + referralCorrelationId: getRandomGuid() + }; // Act - await store.loadSession(); + await store.loadSession(duplicate); // Asserts expect(store.order.referralNumber).toBe(fullApiResponse.data.referralNumber); @@ -1092,7 +1099,6 @@ describe('Store', () => { expect(store.order.referralCorrelationId).toBe(fullApiResponse.data.referralCorrelationId); expect(store.order.referralSequenceNumber).toBe(fullApiResponse.data.referralSequenceNumber); expect(store.order.eon).toBe(fullApiResponse.data.eon); - expect(store.order.workOrderNumber).toBe(originalWorkOrderNumber); }); }); it('api call throws exception', async () => { @@ -1100,8 +1106,13 @@ describe('Store', () => { const error = 'load session error'; globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error)); + const duplicate = { + referralNumber: getRandomString(6, 6), + referralCorrelationId: getRandomGuid() + }; + // Act - await store.loadSession().catch((e) => { + await store.loadSession(duplicate).catch((e) => { expect(e).toEqual(error); });