diff --git a/src/constants/schedule-constants.js b/src/constants/schedule-constants.js index af0c6206..7737bbb5 100644 --- a/src/constants/schedule-constants.js +++ b/src/constants/schedule-constants.js @@ -1,7 +1,7 @@ const AppointmentTypeStrings = { IN_SHOP: 'Inshop', MOBILE: 'Mobile', - MOBILE_NOT_ITAC: 'Mobile-Not-ITAC', + MOBILE_NOT_ITAC_AND_NOT_NOCOMP: 'Mobile-Not-ITAC-And-Not-NoComp', DROP_OFF: 'Dropoff' }; const PREMIUM_FEE_PART_TYPE = 'EARLY BIRD'; diff --git a/src/digital-components/date-picker/date-picker.vue b/src/digital-components/date-picker/date-picker.vue index b57dcdc1..f30f9b7d 100644 --- a/src/digital-components/date-picker/date-picker.vue +++ b/src/digital-components/date-picker/date-picker.vue @@ -669,7 +669,6 @@ export default { } }, async updateSelectableDates(monthStart, monthEnd) { - console.log(this.mainStore); const moreSelectableDates = await this.customSelectableDatesCallback( monthStart, monthEnd, diff --git a/src/layouts/policy-endorsements/policy-endorsements.spec.js b/src/layouts/policy-endorsements/policy-endorsements.spec.js index 86b8fa73..c01818f5 100644 --- a/src/layouts/policy-endorsements/policy-endorsements.spec.js +++ b/src/layouts/policy-endorsements/policy-endorsements.spec.js @@ -7,6 +7,24 @@ import { getMountOptions } from '@/helpers/unit-test-helper.js'; import navigationScenarios from '@/router/router-constants/navigation-scenarios'; import { createTestingPinia } from '@pinia/testing'; import { useMainStore } from '@/store'; +import { getRandomString } from '@/helpers/data-generation'; + +function getMountedComponent(mainInitialState = {}) { + const mountOptions = getMountOptions({ + router: { + navigate: jest.fn() + } + }); + + mountOptions.global.plugins = [createTestingPinia({ + initialState: { + main: mainInitialState + } + })]; + + const wrapper = shallowMount(policyEndorsements, mountOptions); + return { wrapper }; +} describe('policyEndorsements.vue', () => { describe('Rendering', () => { @@ -30,26 +48,70 @@ describe('policyEndorsements.vue', () => { // Assert expect(siteSubHeader.exists()).toBe(true); }); - test('Should render schoolProperty buttonQuestion component', () => { + test('Should render schoolProperty buttonQuestion component if policy vehicle has Educator endorsement', () => { // Arrange - const wrapper = shallowMount(policyEndorsements, getMountOptions()); - - // Act + const mainInitialState = { + order: { + policy: { + endorsements: ['Educator'] + } + } + }; + const { wrapper } = getMountedComponent(mainInitialState); const buttonQuestion = wrapper.findComponent({ ref: 'schoolPropertyQuestion' }); // Assert + expect(wrapper.vm.educatorEndorsement).toBeTruthy(); expect(buttonQuestion.exists()).toBe(true); }); - test('Should render parkingLot buttonQuestion component', () => { + test('Should not render schoolProperty buttonQuestion component if policy vehicle does not have Educator endorsement', () => { // Arrange - const wrapper = shallowMount(policyEndorsements, getMountOptions()); + const mainInitialState = { + order: { + policy: { + endorsements: [getRandomString(20, 30)] + } + } + }; + const { wrapper } = getMountedComponent(mainInitialState); + const buttonQuestion = wrapper.findComponent({ ref: 'schoolPropertyQuestion' }); - // Act + // Assert + expect(wrapper.vm.educatorEndorsement).toBeFalsy(); + expect(buttonQuestion.exists()).toBe(false); + }); + test('Should render parkingLot buttonQuestion component if policy vehicle has Parking Guard endorsement', () => { + // Arrange + const mainInitialState = { + order: { + policy: { + endorsements: ['Parking Guard'] + } + } + }; + const { wrapper } = getMountedComponent(mainInitialState); const buttonQuestion = wrapper.findComponent({ ref: 'parkingLotQuestion' }); // Assert + expect(wrapper.vm.parkingGuardEndorsement).toBeTruthy(); expect(buttonQuestion.exists()).toBe(true); }); + test('Should not render parkingLot buttonQuestion component if policy vehicle does not have Parking Guard endorsement', () => { + // Arrange + const mainInitialState = { + order: { + policy: { + endorsements: [getRandomString(20, 30)] + } + } + }; + const { wrapper } = getMountedComponent(mainInitialState); + const buttonQuestion = wrapper.findComponent({ ref: 'parkingLotQuestion' }); + + // Assert + expect(wrapper.vm.parkingGuardEndorsement).toBeFalsy(); + expect(buttonQuestion.exists()).toBe(false); + }); test('Should render site footer', () => { // Arrange const wrapper = shallowMount(policyEndorsements, getMountOptions()); diff --git a/src/layouts/policy-endorsements/policy-endorsements.vue b/src/layouts/policy-endorsements/policy-endorsements.vue index 22154818..1d4e1972 100644 --- a/src/layouts/policy-endorsements/policy-endorsements.vue +++ b/src/layouts/policy-endorsements/policy-endorsements.vue @@ -16,6 +16,7 @@ cmsWidgetName="SiteSubHeaderWidget" class="mt-5" /> { vin, noCoverage: true, deductible: 0, - repairWaived: false + repairWaived: false, + endorsements: [] }; // Act @@ -135,6 +136,55 @@ describe('policy-vehicles.vue', () => { } ); + test( + // eslint-disable-next-line max-len + 'Selected VIN matches vehicle listed in system and policy vehicle has endorsements => update vehicle and navigate forward with CLICKED_FORWARD_WITH_ENDORSEMENTS scenario.', + async () => { + // Arrange + const { wrapper } = setupMocks({}); + + const vin = getRandomString(17, 17); + const endorsements = [getRandomString(10, 20)]; + await wrapper.setData({ + selectedVehicleVin: vin, + policyVehicles: [ + { + vin, + endorsements + } + ] + }); + + const year = getRandomInt(1998, 2023); + const lookupVehicleResponse = { + data: { + year + } + }; + const store = useMainStore(); + store.lookupVehicleByVin.mockReturnValue(Promise.resolve(lookupVehicleResponse)); + + const expectedInput = { + year, + vin, + noCoverage: true, + deductible: 0, + repairWaived: false, + endorsements + }; + + // Act + await wrapper.vm.forwardButtonAction(); + + // Assert + expect(store.updateVehicle).toHaveBeenCalledWith(expectedInput); + expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS, + undefined + ); + } + ); + test( 'Error in lookupVehicleByVin call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario.', async () => { @@ -408,6 +458,120 @@ describe('policy-vehicles.vue', () => { }); }); + describe('endorsementsForSelectedVehicle computed property', () => { + it('policyVehicles is null => empty endorsements array returned', () => { + // Arrange + const testValues = { + policyVehicles: null + }; + + // Act + const result = policyVehicles.computed.endorsementsForSelectedVehicle.call(testValues); + const expected = []; + + // Assert + expect(result).toStrictEqual(expected); + }); + + it('policyVehicles is empty => empty endorsements array returned', () => { + // Arrange + const testValues = { + policyVehicles: [] + }; + + // Act + const result = policyVehicles.computed.endorsementsForSelectedVehicle.call(testValues); + const expected = []; + + // Assert + expect(result).toStrictEqual(expected); + }); + + it('selectedVehicleVin does not match any vin in policyVehicles => empty endorsements array returned', () => { + // Arrange + const selectedVin = getRandomString(17, 17); + const otherVin = getRandomString(17, 17); + const testValues = { + selectedVehicleVin: selectedVin, + policyVehicles: [ + { + vin: otherVin + } + ] + }; + + // Act + const result = policyVehicles.computed.endorsementsForSelectedVehicle.call(testValues); + const expected = []; + + // Assert + expect(result).toStrictEqual(expected); + }); + + it('vehicle VIN match with endorsements null => empty endorsements array returned', () => { + // Arrange + const vin = getRandomString(17, 17); + const testValues = { + selectedVehicleVin: vin, + policyVehicles: [ + { + vin, + endorsements: null + } + ] + }; + + // Act + const result = policyVehicles.computed.endorsementsForSelectedVehicle.call(testValues); + const expected = []; + + // Assert + expect(result).toStrictEqual(expected); + }); + + it('vehicle VIN match with empty endorsements => empty endorsements array returned', () => { + // Arrange + const vin = getRandomString(17, 17); + const testValues = { + selectedVehicleVin: vin, + policyVehicles: [ + { + vin, + endorsements: [] + } + ] + }; + + // Act + const result = policyVehicles.computed.endorsementsForSelectedVehicle.call(testValues); + const expected = []; + + // Assert + expect(result).toStrictEqual(expected); + }); + + it('vehicle VIN match with endorsements => endorsements array returned', () => { + // Arrange + const vin = getRandomString(17, 17); + const endorsements = [getRandomString(10, 20)]; + const testValues = { + selectedVehicleVin: vin, + policyVehicles: [ + { + vin, + endorsements + } + ] + }; + + // Act + const result = policyVehicles.computed.endorsementsForSelectedVehicle.call(testValues); + + // Assert + expect(result).toStrictEqual(endorsements); + }); + }); + test('first vehicle is auto-selected if only one vehicle on policy', async () => { // Arrange const vin = getRandomString(17, 17); diff --git a/src/layouts/policy-vehicles/policy-vehicles.vue b/src/layouts/policy-vehicles/policy-vehicles.vue index 8e08bd97..8ea67941 100644 --- a/src/layouts/policy-vehicles/policy-vehicles.vue +++ b/src/layouts/policy-vehicles/policy-vehicles.vue @@ -102,12 +102,12 @@ export default { return mappedData; }, noCoverageForSelectedVehicle() { - const vehicle = this.policyVehicles.find((policyVehicle) => + const vehicle = this.policyVehicles?.find((policyVehicle) => policyVehicle.vin === this.selectedVehicleVin); return (vehicle?.coverages?.length ?? 0) === 0; }, deductibleForSelectedVehicle() { - const vehicle = this.policyVehicles.find((policyVehicle) => + const vehicle = this.policyVehicles?.find((policyVehicle) => policyVehicle?.vin === this.selectedVehicleVin); if (!vehicle) { return undefined; @@ -117,16 +117,16 @@ export default { ? vehicle?.coverages[0].deductible : 0; }, - selectedVehicleHasEndorsements() { - const vehicle = this.policyVehicles.find((policyVehicle) => + endorsementsForSelectedVehicle() { + const vehicle = this.policyVehicles?.find((policyVehicle) => policyVehicle?.vin === this.selectedVehicleVin); - if (!vehicle) { - return false; + if (vehicle?.endorsements?.length > 0) { + return vehicle.endorsements; } - return vehicle.endorsements?.length > 0; + return []; }, repairWaivedForSelectedVehicle() { - const vehicle = this.policyVehicles.find((policyVehicle) => + const vehicle = this.policyVehicles?.find((policyVehicle) => policyVehicle.vin === this.selectedVehicleVin); return vehicle?.endorsements?.includes(endorsementOptions.REPAIR_WAIVED) ?? false; }, @@ -161,7 +161,7 @@ export default { }, beforeMount() { if (this.policyVehicles?.length === 1) { - this.selectedVehicleVin = this.policyVehicles[0].vin; + this.selectedVehicleVin = this.policyVehicles[0]?.vin; } }, methods: @@ -182,7 +182,8 @@ export default { vin: this.selectedVehicleVin, noCoverage: this.noCoverageForSelectedVehicle, deductible: this.deductibleForSelectedVehicle, - repairWaived: this.repairWaivedForSelectedVehicle + repairWaived: this.repairWaivedForSelectedVehicle, + endorsements: this.endorsementsForSelectedVehicle }); useMainStore().updateVehicle(this.vehicleFromLookup); @@ -205,7 +206,7 @@ export default { {}, {} ); - } else if (this.selectedVehicleHasEndorsements) { + } else if (this.endorsementsForSelectedVehicle?.length > 0) { this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS, this.$route diff --git a/src/layouts/schedule-page/schedule-page.vue b/src/layouts/schedule-page/schedule-page.vue index 27dd855a..10718c95 100644 --- a/src/layouts/schedule-page/schedule-page.vue +++ b/src/layouts/schedule-page/schedule-page.vue @@ -131,7 +131,7 @@ const getAvailableDates = async ( } if (appointmentType === AppointmentTypeStrings.MOBILE - || appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC) { + || appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) { storeActionConfig = { storeAction: GET_MOBILE_TIME_SLOTS, payload: { @@ -326,7 +326,7 @@ export default { && serviceLocation.zipCodeCtu && serviceLocation.appointmentType && ((serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE - || serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC) + || serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) || serviceLocation.provider.providerNumber); const supportingItems = useMainStore().lineItems.supportingItems !== null; const damageInfo = diff --git a/src/layouts/schedule-page/time-slot-modal-question/time-slot-modal-question.vue b/src/layouts/schedule-page/time-slot-modal-question/time-slot-modal-question.vue index d671bb1b..d482f9f4 100644 --- a/src/layouts/schedule-page/time-slot-modal-question/time-slot-modal-question.vue +++ b/src/layouts/schedule-page/time-slot-modal-question/time-slot-modal-question.vue @@ -166,7 +166,7 @@ export default { } if (this.appointmentType === AppointmentTypeStrings.MOBILE - || this.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC) { + || this.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) { appointmentTypeCmsWidgetName = this.selectedRouteCode?.includes(PREMIUM_TIME_SLOT_ID_FLAG) ? this.mobilePremiumCmsWidgetName : this.mobileCmsWidgetName; @@ -275,7 +275,7 @@ export default { }, durationTextBlockCopy() { if (this.appointmentType === AppointmentTypeStrings.MOBILE - || this.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC) { + || this.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) { return null; } if (this.appointmentType === AppointmentTypeStrings.IN_SHOP) { return this.inshopDurationText; @@ -321,7 +321,7 @@ export default { if (this.appointmentType === AppointmentTypeStrings.DROP_OFF) { return this.getAvailableTimeSlotsForDropOff(this.timeSlotsForSelectedDate.timeSlots); } if (this.appointmentType === AppointmentTypeStrings.MOBILE - || this.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC) { + || this.appointmentType === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP) { return this.getAvailableTimeSlotsForMobile(this.timeSlotsForSelectedDate.timeSlots); } return this.getAvailableTimeSlotsForInshop(this.timeSlotsForSelectedDate.timeSlots); diff --git a/src/layouts/service-location/appointment-type-question/appointment-type-question.vue b/src/layouts/service-location/appointment-type-question/appointment-type-question.vue index dc7dadb2..19dae4e5 100644 --- a/src/layouts/service-location/appointment-type-question/appointment-type-question.vue +++ b/src/layouts/service-location/appointment-type-question/appointment-type-question.vue @@ -51,15 +51,15 @@ export default { return this.getCmsContent(this.cmsWidgetName, 'Answers'); }, answersToDisplay() { - const shouldShowMobile = this.isServiceableMobile && this.isITAC; - const shouldShowMobileNotITAC = this.isServiceableMobile && !this.isITAC; + const shouldShowMobile = this.isServiceableMobile && this.isItacOrNoComp; + const shouldShowMobileNotITACNotNoComp = this.isServiceableMobile && !this.isItacOrNoComp; const shouldShowInshop = this.isServiceableInshop; const shouldShowDropoff = this.isServiceableInshop && !useMainStore().damage.isRepair; return this.answersFromCms ? this.answersFromCms.filter((answer) => ( (answer.Name === AppointmentTypeStrings.IN_SHOP && shouldShowInshop) || (answer.Name === AppointmentTypeStrings.MOBILE && shouldShowMobile) - || (answer.Name === AppointmentTypeStrings.MOBILE_NOT_ITAC && shouldShowMobileNotITAC) + || (answer.Name === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP && shouldShowMobileNotITACNotNoComp) || (answer.Name === AppointmentTypeStrings.DROP_OFF && shouldShowDropoff) )) : []; @@ -75,6 +75,9 @@ export default { isITAC() { return useMainStore().policy.isITAC; }, + isItacOrNoComp() { + return useMainStore().policy.isITAC || useMainStore().policy.noCoverage; + }, isMobileOnly() { return this.isServiceableMobile && !this.isServiceableInshop; } @@ -86,12 +89,12 @@ export default { if ( newValue.length === 1 && newValue.findIndex((answer) => (answer.Name === AppointmentTypeStrings.MOBILE - || answer.Name === AppointmentTypeStrings.MOBILE_NOT_ITAC)) !== -1 + || answer.Name === AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP)) !== -1 ) { - if (this.isITAC) { + if (this.isItacOrNoComp) { this.selectedValues = AppointmentTypeStrings.MOBILE; } else { - this.selectedValues = AppointmentTypeStrings.MOBILE_NOT_ITAC; + this.selectedValues = AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP; } } }, @@ -100,10 +103,10 @@ export default { isMobileOnly: { handler(newValue) { if (newValue) { - if (this.isITAC) { + if (this.isItacOrNoComp) { this.selectedValues = AppointmentTypeStrings.MOBILE; } else { - this.selectedValues = AppointmentTypeStrings.MOBILE_NOT_ITAC; + this.selectedValues = AppointmentTypeStrings.MOBILE_NOT_ITAC_AND_NOT_NOCOMP; } } } diff --git a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue index 3829dd76..f9ea85b2 100644 --- a/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue +++ b/src/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions.vue @@ -31,7 +31,7 @@ @@ -43,13 +43,14 @@ :onModalOpenedCallback="onModalOpened" :onModalClosedCallback="onModalClosed" @isModalOpened="setModalStatus" - @footer-button-event="setMobileLocation"> + @footerButtonEvent="setMobileLocation">