From acd68f97d958f207cc2c93f801866bbbee91006c Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Wed, 7 Feb 2024 16:02:22 -0500 Subject: [PATCH 1/4] Auto-route only as far as Quote for cash users. --- .../heritage-integration/navigation-helper.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/helpers/heritage-integration/navigation-helper.js b/src/helpers/heritage-integration/navigation-helper.js index 8fdfc40bb..bd2525ac2 100644 --- a/src/helpers/heritage-integration/navigation-helper.js +++ b/src/helpers/heritage-integration/navigation-helper.js @@ -123,17 +123,21 @@ async function getLatestPageForRedirection() { const scheduleComponent = await getLazyLoadedComponent(fmgPageValues.SCHEDULE); const skipVin = await skipVinLookup(); + const isInsuranceUser = !!store.getters.order.payment.isInsurance; if (!vehicleDamageComponent.methods.arePagePrerequisitesValid()) { return fmgPageValues.VEHICLE; } else if (!estimateComponent.methods.arePagePrerequisitesValid()) { return fmgPageValues.VEHICLE_DAMAGE; } else { - if (scheduleComponent.methods.arePagePrerequisitesValid()) { - return fmgPageValues.SCHEDULE; - } else if (serviceLocationComponent.methods.arePagePrerequisitesValid()) { - return fmgPageValues.SERVICE_LOCATION; - } else if (quoteComponent.methods.arePagePrerequisitesValid()) { + if (isInsuranceUser) { + if (scheduleComponent.methods.arePagePrerequisitesValid()) { + return fmgPageValues.SCHEDULE; + } else if (serviceLocationComponent.methods.arePagePrerequisitesValid()) { + return fmgPageValues.SERVICE_LOCATION; + } + } + if (quoteComponent.methods.arePagePrerequisitesValid()) { return fmgPageValues.QUOTE; } else if (capabilityQuestionsComponent.methods.arePagePrerequisitesValid()) { return fmgPageValues.CAPABILITY_QUESTIONS; From 11b7f52c8fd7feae1bb8684556e918d55df13793 Mon Sep 17 00:00:00 2001 From: Chloe Herd Date: Thu, 8 Feb 2024 10:51:33 -0500 Subject: [PATCH 2/4] Also remove ability for non-cash users to be routed past quote. --- src/helpers/heritage-integration/navigation-helper.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/helpers/heritage-integration/navigation-helper.js b/src/helpers/heritage-integration/navigation-helper.js index bd2525ac2..80734adf8 100644 --- a/src/helpers/heritage-integration/navigation-helper.js +++ b/src/helpers/heritage-integration/navigation-helper.js @@ -123,20 +123,12 @@ async function getLatestPageForRedirection() { const scheduleComponent = await getLazyLoadedComponent(fmgPageValues.SCHEDULE); const skipVin = await skipVinLookup(); - const isInsuranceUser = !!store.getters.order.payment.isInsurance; if (!vehicleDamageComponent.methods.arePagePrerequisitesValid()) { return fmgPageValues.VEHICLE; } else if (!estimateComponent.methods.arePagePrerequisitesValid()) { return fmgPageValues.VEHICLE_DAMAGE; } else { - if (isInsuranceUser) { - if (scheduleComponent.methods.arePagePrerequisitesValid()) { - return fmgPageValues.SCHEDULE; - } else if (serviceLocationComponent.methods.arePagePrerequisitesValid()) { - return fmgPageValues.SERVICE_LOCATION; - } - } if (quoteComponent.methods.arePagePrerequisitesValid()) { return fmgPageValues.QUOTE; } else if (capabilityQuestionsComponent.methods.arePagePrerequisitesValid()) { From 2dc8d429714fc776f45cdd56b2552b2cedcea8d7 Mon Sep 17 00:00:00 2001 From: hiteshkumar87 Date: Fri, 9 Feb 2024 20:51:11 +0530 Subject: [PATCH 3/4] CSR-1438 make email optional --- src/constants/experiments.js | 1 + .../textbox-question/textbox-question.vue | 8 +++++++- .../address-lookup/address-lookup.spec.js | 13 +++++++++++++ src/layouts/address-lookup/address-lookup.vue | 6 +++++- .../customer-questions/customer-questions.vue | 5 ++++- src/layouts/estimate/estimate.spec.js | 17 +++++++++++++++-- src/layouts/estimate/estimate.vue | 5 +++-- .../license-plate-lookup.spec.js | 14 ++++++++++++-- .../license-plate-lookup.vue | 4 +++- src/layouts/vin-lookup/vin-lookup.spec.js | 7 +++++++ src/layouts/vin-lookup/vin-lookup.vue | 5 +++-- src/mixins/vin-pages-mixin.js | 12 ++++++++++++ 12 files changed, 85 insertions(+), 12 deletions(-) diff --git a/src/constants/experiments.js b/src/constants/experiments.js index b702b11bb..18ca6387d 100644 --- a/src/constants/experiments.js +++ b/src/constants/experiments.js @@ -8,6 +8,7 @@ const experimentSettings = { DISPLAY_AVAILABILITY_INDICATORS: "DisplayAvailabilityIndicators", PIA_EXPERIENCE: "PIA Experience", SUBMIT_ORDER_ENABLE_PIA: "SubmitOrder_Enable_PIA", + IS_EMAIL_OPTIONAL: "isEmailOptional", }; const experimentTriggers = { diff --git a/src/digital-components/textbox-question/textbox-question.vue b/src/digital-components/textbox-question/textbox-question.vue index 11f232135..da41fe10b 100644 --- a/src/digital-components/textbox-question/textbox-question.vue +++ b/src/digital-components/textbox-question/textbox-question.vue @@ -121,6 +121,10 @@ export default { hideInput: Boolean, centerErrorMessage: Boolean, keyDownHandler: Function, + isEmailOptional: { + type: Boolean, + default: false, + }, }, setup(props) { const uuid = uuidv4(); @@ -193,7 +197,9 @@ export default { }, computed: { questionText() { - return this.getCmsContent(this.cmsWidgetName, "QuestionText"); + return this.isEmailOptional + ? this.getCmsContent(this.cmsWidgetName, "QuestionText") + " (optional)" + : this.getCmsContent(this.cmsWidgetName, "QuestionText"); }, value: { get: function () { diff --git a/src/layouts/address-lookup/address-lookup.spec.js b/src/layouts/address-lookup/address-lookup.spec.js index cce68d793..731e664dc 100644 --- a/src/layouts/address-lookup/address-lookup.spec.js +++ b/src/layouts/address-lookup/address-lookup.spec.js @@ -10,6 +10,7 @@ import { storeMutations } from "@/constants/store-mutations"; import { navigationScenarios } from "@/router/router-constants/navigation-scenarios"; import store from "@/store"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; +import { experimentSettings } from "@/constants/experiments"; jest.mock("@/helpers/damage-helper", () => ({ isGlassAvailableForCarId: jest.fn().mockImplementation(() => true), @@ -641,6 +642,17 @@ describe("address-lookup.vue", () => { }); }); +const mockMixin = { + methods: { + getSettingValue: jest.fn((settingName) => { + if (settingName === experimentSettings.IS_EMAIL_OPTIONAL) { + return "true"; + } + return "false"; + }), + }, +}; + function setupMocks({ isZipValid = true, isZipServiceable = true, @@ -709,6 +721,7 @@ function setupMocks({ }, }, }, + mixins: [mockMixin], }) ); diff --git a/src/layouts/address-lookup/address-lookup.vue b/src/layouts/address-lookup/address-lookup.vue index de0e8d5aa..807bdc5ce 100644 --- a/src/layouts/address-lookup/address-lookup.vue +++ b/src/layouts/address-lookup/address-lookup.vue @@ -20,7 +20,11 @@ - + + :isRequired="!isEmailOptional" + :validationRules="validationRules" + :isEmailOptional="isEmailOptional" />
@@ -79,6 +81,7 @@ export default { }), }, validationRules: String, + isEmailOptional: Boolean, }, computed: { customerModel: { diff --git a/src/layouts/estimate/estimate.spec.js b/src/layouts/estimate/estimate.spec.js index 7d395e88d..b8517d833 100644 --- a/src/layouts/estimate/estimate.spec.js +++ b/src/layouts/estimate/estimate.spec.js @@ -11,6 +11,7 @@ import { settleAllPromises } from "@/helpers/layout-helper.js"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import baseMixin from "../../mixins/base-mixin"; import { vinLookupMethodSelections } from "@/constants/vin-lookup-method-selections.js"; +import { experimentSettings } from "@/constants/experiments"; // Mock our module for promises. jest.mock("@/helpers/layout-helper.js", () => ({ @@ -273,12 +274,24 @@ function setupMocks({ Answers: cmsAnswers, FunnelFooterWidget: FunnelFooterWidget, }; - + const mockMixin = { + methods: { + getSettingValue: jest.fn((settingName) => { + if (settingName === experimentSettings.IS_EMAIL_OPTIONAL) { + return "true"; + } + return "false"; + }), + }, + }; const apiPromise = Promise.resolve({ cmsContent }); settleAllPromises.mockImplementation(() => apiPromise); fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); - const mountOptions = getMountOptions({ ...mountOptionsMockData, mixins: [baseMixin] }); + const mountOptions = getMountOptions({ + ...mountOptionsMockData, + mixins: [baseMixin, mockMixin], + }); mountOptions["attachTo"] = document.body; const wrapper = shallowMount(estimate, mountOptions); diff --git a/src/layouts/estimate/estimate.vue b/src/layouts/estimate/estimate.vue index 410c4d325..d2766f730 100644 --- a/src/layouts/estimate/estimate.vue +++ b/src/layouts/estimate/estimate.vue @@ -52,9 +52,10 @@ cmsWidgetName="EmailAddressQuestionWidget" v-model="emailAddress" inputId="emailAddress" - isRequired + :isRequired="!IsEmailOptional" disableAutoFill - validationRules="email-address-required|email-address-format" /> + :validationRules="EmailValidationRules" + :isEmailOptional="IsEmailOptional" /> "loader.gif"); jest.mock("@/assets/img/windshield.png", () => "windshield.png"); @@ -692,11 +693,20 @@ function setupMocks({ }; const apiPromise = Promise.resolve(apiResponses); - + const mockMixin = { + methods: { + getSettingValue: jest.fn((settingName) => { + if (settingName === experimentSettings.IS_EMAIL_OPTIONAL) { + return "true"; + } + return "false"; + }), + }, + }; settleAllPromises.mockImplementation(() => apiPromise); fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); - const mountOptions = getMountOptions(mountOptionsMockData); + const mountOptions = getMountOptions({ ...mountOptionsMockData, mixins: [mockMixin] }); mountOptions["attachTo"] = document.body; // append wrapper to document.body to test DOM methods const wrapper = shallowMount(licensePlateLookup, mountOptions); diff --git a/src/layouts/license-plate-lookup/license-plate-lookup.vue b/src/layouts/license-plate-lookup/license-plate-lookup.vue index 811dbea7b..37d93cc8b 100644 --- a/src/layouts/license-plate-lookup/license-plate-lookup.vue +++ b/src/layouts/license-plate-lookup/license-plate-lookup.vue @@ -34,7 +34,9 @@ cmsWidgetName="EmailAddressQuestionWidget" v-model="email" customInputId="email" - validationRules="email-address-required|email-address-format" /> + :isRequired="!IsEmailOptional" + :validationRules="EmailValidationRules" + :isEmailOptional="IsEmailOptional" /> diff --git a/src/layouts/vin-lookup/vin-lookup.spec.js b/src/layouts/vin-lookup/vin-lookup.spec.js index f25bc68db..1d7d3f493 100644 --- a/src/layouts/vin-lookup/vin-lookup.spec.js +++ b/src/layouts/vin-lookup/vin-lookup.spec.js @@ -3,6 +3,7 @@ import vinLookup from "./vin-lookup.vue"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { navigationScenarios } from "@/router/router-constants/navigation-scenarios.js"; import { settleAllPromises } from "@/helpers/layout-helper.js"; +import { experimentSettings } from "@/constants/experiments"; import store from "@/store"; @@ -378,5 +379,11 @@ function mockOutStubFunctions(wrapper) { const mockMixin = { methods: { getCmsContent: jest.fn(() => "placeholder CMS content"), + getSettingValue: jest.fn((settingName) => { + if (settingName === experimentSettings.IS_EMAIL_OPTIONAL) { + return "true"; + } + return "false"; + }), }, }; diff --git a/src/layouts/vin-lookup/vin-lookup.vue b/src/layouts/vin-lookup/vin-lookup.vue index c809bae70..ae197aeb0 100644 --- a/src/layouts/vin-lookup/vin-lookup.vue +++ b/src/layouts/vin-lookup/vin-lookup.vue @@ -53,8 +53,9 @@ cmsWidgetName="EmailAddressQuestionWidget" v-model="emailAddress" customInputId="emailAddress" - isRequired - validationRules="email-address-required|email-address-format" /> + :isRequired="!IsEmailOptional" + :validationRules="EmailValidationRules" + :isEmailOptional="IsEmailOptional" /> diff --git a/src/mixins/vin-pages-mixin.js b/src/mixins/vin-pages-mixin.js index a0a435000..98eb7a75b 100644 --- a/src/mixins/vin-pages-mixin.js +++ b/src/mixins/vin-pages-mixin.js @@ -2,8 +2,20 @@ import { storeActions } from "@/constants/store-actions.js"; import store from "@/store"; import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin"; import { saveSession } from "@/helpers/heritage-integration/order-helper.js"; +import { experimentSettings } from "@/constants/experiments"; export default { + computed: { + IsEmailOptional() { + const emailOptional = this.getSettingValue(experimentSettings.IS_EMAIL_OPTIONAL); + return emailOptional === "true"; + }, + EmailValidationRules() { + return this.IsEmailOptional + ? "email-address-format" + : "email-address-required|email-address-format"; + }, + }, methods: { async navigateForwardWithSingleCarMatch() { const pageName = this.$options?.name; From 37a59cc74e4e221b5e3a1884836d359863f5ebea Mon Sep 17 00:00:00 2001 From: Sneha Date: Fri, 9 Feb 2024 21:27:13 +0530 Subject: [PATCH 4/4] merge to release --- src/layouts/payment-method/payment-method.vue | 1 + .../promo-modal-question.spec.js | 143 ++++++++++++++++++ .../promo-modal-question.vue | 76 ++++++---- src/layouts/quote/quote.spec.js | 33 +++- src/layouts/quote/quote.vue | 75 +++++++-- .../service-package-question.spec.js | 51 +++++++ .../service-package-question.vue | 3 + 7 files changed, 334 insertions(+), 48 deletions(-) diff --git a/src/layouts/payment-method/payment-method.vue b/src/layouts/payment-method/payment-method.vue index 3bde800bf..ee0eee749 100644 --- a/src/layouts/payment-method/payment-method.vue +++ b/src/layouts/payment-method/payment-method.vue @@ -33,6 +33,7 @@ class="small mt-4" v-model="lineItems" :availableVaps="availableVaps" + pageName="payment-method" modalWidgetName="PromoModalWidget" />
diff --git a/src/layouts/payment-method/promo-modal-question/promo-modal-question.spec.js b/src/layouts/payment-method/promo-modal-question/promo-modal-question.spec.js index 16d170e82..1dcc2ac70 100644 --- a/src/layouts/payment-method/promo-modal-question/promo-modal-question.spec.js +++ b/src/layouts/payment-method/promo-modal-question/promo-modal-question.spec.js @@ -1,5 +1,25 @@ import { mount, shallowMount } from "@vue/test-utils"; import promoModalQuestion from "./promo-modal-question"; +import { storeActions } from "@/constants/store-actions"; +import baseMixin from "@/mixins/base-mixin.js"; + +let mockReturnsForStoreActions = {}; + +jest.mock("@/mixins/base-mixin", () => ({ + ...jest.requireActual("@/mixins/base-mixin"), + methods: { + dispatchStoreActionWithLogging: jest.fn( + (action, { promoCode, addableVaps }, pageNameToLog, someBool) => { + return mockReturnsForStoreActions[action]; + } + ), + }, +})); + +afterEach(() => { + // reset store action returns + mockReturnsForStoreActions = {}; +}); jest.mock("@/digital-components/textbox-question/textbox-question", () => ({ getCmsContent: jest.fn((widgetName, cmsFieldName) => { @@ -79,4 +99,127 @@ describe("promo-modal-question.vue", () => { // Assert expect(wrapper.emitted("update:modelValue")).toEqual([[lineItems]]); }); + it("focuses on the input element when focusOnPromoInput is called", async () => { + // Arrange + const lineItems = { + glassParts: [], + supportingItems: [], + vaps: [], + promos: [], + }; + + const wrapper = mount(promoModalQuestion, { + mixins: [mockMixin], + props: { + modelValue: lineItems, + modalWidgetName: modalWidgetName, + }, + attachTo: document.body, + }); + const focusMock = jest.fn(); + const inputMock = { focus: focusMock }; + + // Mock document.getElementById to return the inputMock + jest.spyOn(document, "getElementById").mockReturnValue(inputMock); + + //Act + + wrapper.vm.focusOnPromoInput(); + await wrapper.vm.$nextTick(); + + //Assert + expect(focusMock).toHaveBeenCalled(); + }); + it("returns a validate response on applying promo", async () => { + // Arrange + const newPromo = "testPromo"; + const pageNameToLog = "testPage"; + const validateResponse = { orderPromos: [] }; + const addableVaps = []; + mockReturnsForStoreActions[storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA] = + validateResponse; + const lineItems = { + glassParts: [], + supportingItems: [], + vaps: [], + promos: [], + }; + + const wrapper = mount(promoModalQuestion, { + mixins: [mockMixin], + props: { + modelValue: lineItems, + modalWidgetName: modalWidgetName, + }, + attachTo: document.body, + }); + // Act + + wrapper.vm.getPromoCodeData(); + + const promoValidationResponse = await baseMixin.methods.dispatchStoreActionWithLogging( + storeActions.VALIDATE_ORDER_PROMO_AND_SAVE_SERVER_DATA, + { + newPromo, + lineItems, + addableVaps, + }, + pageNameToLog, + false + ); + + // Assert + expect(promoValidationResponse).toEqual(validateResponse); + }); + test("If promocode is valid return taxed lineItems", async () => { + //Arrange + const taxedlineItems = {}; + + mockReturnsForStoreActions[storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA] = + taxedlineItems; + + const lineItems = { + glassParts: [], + supportingItems: [], + vaps: [], + promos: [], + }; + + let promoCode = "1wiper0"; + const pricedLineItemsToTax = []; + + pricedLineItemsToTax.push(promoCode); + const wrapper = mount(promoModalQuestion, { + mixins: [mockMixin], + + props: { + modelValue: lineItems, + modalWidgetName: modalWidgetName, + }, + + attachTo: document.body, + }); + wrapper.vm.addPromoCode(); + + const taxedLineItems = await baseMixin.methods.dispatchStoreActionWithLogging( + storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA, + { + billToAccountNumber: "87291", + providerNumber: 2, + appointmentType: "IN_SHOP", + serviceLocationCity: "city", + serviceLocationState: "state", + serviceLocationZipCode: "12345", + pricedLineItems: pricedLineItemsToTax, + }, + + "payment-method", + + false + ); + + // Assert + + expect(taxedLineItems).toEqual(taxedlineItems); + }); }); diff --git a/src/layouts/payment-method/promo-modal-question/promo-modal-question.vue b/src/layouts/payment-method/promo-modal-question/promo-modal-question.vue index b4b02b3ed..da8d344e9 100644 --- a/src/layouts/payment-method/promo-modal-question/promo-modal-question.vue +++ b/src/layouts/payment-method/promo-modal-question/promo-modal-question.vue @@ -100,6 +100,12 @@ export default { modelValue: Object, modalWidgetName: String, availableVaps: Object, + pageName: String, + taxPromos: { + type: Boolean, + required: false, + default: true, + }, }, computed: { promoLinkText() { @@ -253,44 +259,52 @@ export default { this.promoCode, this.lineItems, this.availableVaps, - "payment-method" + this.pageName ); if (promoCodeData.isValid) { - const pricedLineItemsToTax = []; - pricedLineItemsToTax.push(...promoCodeData.promoCode); - const taxedLineItems = await baseMixin.methods.dispatchStoreActionWithLogging( - storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA, - { - billToAccountNumber: "87291", - providerNumber: - store.getters.order.serviceLocation.provider.providerNumber, - appointmentType: store.getters.order.serviceLocation.appointmentType, - serviceLocationCity: store.getters.order.serviceLocation.city, - serviceLocationState: store.getters.order.serviceLocation.state, - serviceLocationZipCode: store.getters.order.serviceLocation.zipCode, - pricedLineItems: pricedLineItemsToTax, - }, - "payment-method", - false - ); + if (this.taxPromos) { + const pricedLineItemsToTax = []; + pricedLineItemsToTax.push(...promoCodeData.promoCode); + const taxedLineItems = + await baseMixin.methods.dispatchStoreActionWithLogging( + storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA, + { + billToAccountNumber: "87291", + providerNumber: + store.getters.order.serviceLocation.provider.providerNumber, + appointmentType: + store.getters.order.serviceLocation.appointmentType, + serviceLocationCity: store.getters.order.serviceLocation.city, + serviceLocationState: store.getters.order.serviceLocation.state, + serviceLocationZipCode: + store.getters.order.serviceLocation.zipCode, + pricedLineItems: pricedLineItemsToTax, + }, + "payment-method", + false + ); - // Match all line items to the line items as they are in the store - // and rebuild the original structure. - this.lineItems = mapTaxedLineItemsToStoreFormat(taxedLineItems, this.lineItems); - const taxedVaps = mapTaxedLineItemsToStoreFormat( - taxedLineItems, - this.availableVaps - ); + // Match all line items to the line items as they are in the store + // and rebuild the original structure. + this.lineItems = mapTaxedLineItemsToStoreFormat( + taxedLineItems, + this.lineItems + ); + const taxedVaps = mapTaxedLineItemsToStoreFormat( + taxedLineItems, + this.availableVaps + ); - const getVaps = getVapsThatNeedToBeAddedToSatisfyPromos( - promoCodeData.promoCode, - taxedVaps, - this.lineItems - ); + const getVaps = getVapsThatNeedToBeAddedToSatisfyPromos( + promoCodeData.promoCode, + taxedVaps, + this.lineItems + ); + this.lineItems.vaps?.push(...getVaps); + } this.lineItems?.promos.push(...promoCodeData.promoCode); - this.lineItems.vaps?.push(...getVaps); this.closeModal(); } else { this.getErrorMessage(promoCodeData.errorCode, promoCodeData.additionalInfo); diff --git a/src/layouts/quote/quote.spec.js b/src/layouts/quote/quote.spec.js index dc30985a5..be68267ae 100644 --- a/src/layouts/quote/quote.spec.js +++ b/src/layouts/quote/quote.spec.js @@ -133,8 +133,6 @@ describe("quote.vue", () => { }; }); - wrapper.vm.pricedGlassParts = []; - //Act await wrapper.vm.forwardButtonAction(); @@ -172,8 +170,6 @@ describe("quote.vue", () => { }; }); - wrapper.vm.pricedGlassParts = []; - //Act await wrapper.vm.forwardButtonAction(); @@ -288,8 +284,8 @@ describe("quote.vue", () => { ); //Assert - expect(wrapper.vm.pricedGlassParts !== null).toBe(true); - expect(wrapper.vm.supportingItems !== null).toBe(true); + expect(wrapper.vm.lineItems !== null).toBe(true); + expect(wrapper.vm.availableVaps !== null).toBe(true); expect(wrapper.vm.availableLineItems !== null).toBe(true); // This should have its own test //expect(vm.isInsuranceSelected !== null).toBe(true); @@ -548,6 +544,31 @@ describe("quote.vue", () => { //Assert expect(wrapper.vm.isInsuranceSelected).toBe(true); }); + test("On forward button action save promos", async () => { + //Arrange + store.getters.payment = { + insuranceCoverage: {}, + isInsurance: false, + }; + store.getters.order = { + lineItems: [], + payment: { + parentAccountNumber: 167132, + }, + }; + const { wrapper } = setupMocks({ + customMountOptions: { + router: { + navigateWithSaving: jest.fn(), + }, + route: { quote }, + }, + }); + + wrapper.vm.forwardButtonAction(); + + expect(wrapper.vm.lineItems.promos !== null).toBe(true); + }); }); function setupMocks({ customMountOptions }) { diff --git a/src/layouts/quote/quote.vue b/src/layouts/quote/quote.vue index 5c6b2d744..8d3dbcee3 100644 --- a/src/layouts/quote/quote.vue +++ b/src/layouts/quote/quote.vue @@ -39,6 +39,15 @@ cmsWidgetName="AfterpayModalWidget" :lineItems="availableLineItems" /> + + { vm.setCmsContent(resultMap.cmsContent); - vm.pricedGlassParts = nullSafeGlassParts; - vm.supportingItems = resultMap.supportingItems; + vm.availableVaps = availableVaps; + vm.lineItems = lineItems; vm.availableLineItems = pricingResults; vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(vm.availableLineItems); @@ -208,15 +224,17 @@ export default { data() { return { isInsuranceSelected: null, - selectedVaps: null, availableLineItems: null, - supportingItems: null, - pricedGlassParts: null, + lineItems: [], + availableVaps: [], }; }, computed: { allActivePromos() { - return this.$store.getters.order.lineItems.promos ?? []; + return this.lineItems.promos ?? []; + }, + lineItemsCloneForWatcher() { + return Object.assign({}, this.lineItems); }, }, methods: { @@ -258,7 +276,7 @@ export default { } }, vapsItemsSelectedAction(vapsItemsSelected) { - this.selectedVaps = vapsItemsSelected; + this.lineItems.vaps = vapsItemsSelected; }, backButtonAction() { vehicleQuestionsMixin.methods.navigateBack(this); @@ -285,15 +303,23 @@ export default { this.supportingItems = this.filterOutFees(this.supportingItems); } - if (this.pricedGlassParts.length > 0) { + if (this.lineItems.glassParts?.length > 0) { this.dispatchStoreAction( this.storeActions.SAVE_GLASS_PARTS_SUPPRESSING_STATE_RESETTING, - this.pricedGlassParts, + this.lineItems.glassParts, false ); } - this.dispatchStoreAction(this.storeActions.SAVE_VAPS, this.selectedVaps, false); + this.dispatchStoreAction(this.storeActions.SAVE_VAPS, this.lineItems.vaps, false); + this.dispatchStoreAction( + storeActions.SAVE_ACTIVE_AND_OR_INACTIVE_PROMOS, + { + activePromos: this.lineItems.promos, + inactivePromos: this.$store.getters.order.payment.inactivePromos, + }, + false + ); const payment = this.$store.getters.payment; if (payment.isInsurance) { @@ -310,6 +336,32 @@ export default { } }, }, + watch: { + lineItemsCloneForWatcher: { + handler(newValue, oldValue) { + if ( + !oldValue || + oldValue.length == 0 || + !oldValue.vaps || + !newValue || + newValue.length == 0 + ) { + return; + } + if (oldValue.promos.length < newValue.promos.length) { + const oldPromoCodes = oldValue.promos.map( + (promoObject) => promoObject.promoCode + ); + const newlyActivatedPromoCodes = newValue.promos.filter( + (newPromo) => !oldPromoCodes.includes(newPromo.promoCode) + ); + const alert = createPromoSuccessAlert(newlyActivatedPromoCodes[0].promoCode); + this.$refs.funnelHeader.pushGlobalAlert(alert, alert.shouldAutoFade); + } + }, + deep: true, + }, + }, components: { funnelHeader, navbar, @@ -322,6 +374,7 @@ export default { contentGroupModal, loadingModal, afterpayModalBanner, + promoModalQuestion, }, }; diff --git a/src/layouts/quote/service-package-question/service-package-question.spec.js b/src/layouts/quote/service-package-question/service-package-question.spec.js index c156ef34c..8163c6324 100644 --- a/src/layouts/quote/service-package-question/service-package-question.spec.js +++ b/src/layouts/quote/service-package-question/service-package-question.spec.js @@ -256,6 +256,57 @@ describe("service-package-question.vue", () => { // Assert expect(wrapper.vm.selectedPackageName).toBe("TierThree"); }); + it("should select default package if promos are added", async () => { + //Arrange + mockProps.activePromos != null; + const wrapper = setupMocks({ + mountOptionsMockData: { + store: { + getters: { + order: { + damage: { + isRepair: false, + glassToReplace: [{ glassLocation: "Windshield" }], + }, + }, + lineItems: { + vaps: [], + }, + hasAnyNonWindshieldGlassParts: false, + payment: { + isInsurance: false, + }, + }, + }, + }, + }); + + //Act + wrapper.setProps({ + activePromos: [ + { + discountedLineItemIds: [ + { + 0: "428ec73c-38e4-4e14-8703-a987b0391898", + 1: "79db94d7-163c-4408-a1ec-f83e58c11992", + }, + ], + partType: "PROMO_DISCOUNT", + promoCode: "1WIPER0", + partNumber: "WIPER DISCOUNT", + laborAmount: 0, + sellingPrice: -10, + kitPrice: 0, + salesTax: null, + }, + ], + }); + const selectDefaultPackageMock = jest.spyOn(wrapper.vm, "selectDefaultPackage"); + await nextTick(); + + //Assert + expect(selectDefaultPackageMock).toHaveBeenCalled(); + }); }); describe("service-package-question.vue, matching business rules for package display", () => { // mock scenarios in figma: diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue index 28727f155..6b84f3557 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -63,6 +63,9 @@ export default { this.selectDefaultPackage(); } }, + activePromos() { + this.selectDefaultPackage(); + }, selectedPackageName(newValue) { const VapsProductsInSelectedPackage = this.getVapsLineItemsForSelectedPackage(newValue); this.$emit("vapsItemsSelected", VapsProductsInSelectedPackage);