Merge branch 'release/2024.02.15' into feature/CSR-1952-ajc
This commit is contained in:
commit
d6433cd84b
19 changed files with 419 additions and 65 deletions
|
|
@ -8,6 +8,7 @@ const experimentSettings = {
|
||||||
DISPLAY_AVAILABILITY_INDICATORS: "DisplayAvailabilityIndicators",
|
DISPLAY_AVAILABILITY_INDICATORS: "DisplayAvailabilityIndicators",
|
||||||
PIA_EXPERIENCE: "PIA Experience",
|
PIA_EXPERIENCE: "PIA Experience",
|
||||||
SUBMIT_ORDER_ENABLE_PIA: "SubmitOrder_Enable_PIA",
|
SUBMIT_ORDER_ENABLE_PIA: "SubmitOrder_Enable_PIA",
|
||||||
|
IS_EMAIL_OPTIONAL: "isEmailOptional",
|
||||||
};
|
};
|
||||||
|
|
||||||
const experimentTriggers = {
|
const experimentTriggers = {
|
||||||
|
|
|
||||||
|
|
@ -121,6 +121,10 @@ export default {
|
||||||
hideInput: Boolean,
|
hideInput: Boolean,
|
||||||
centerErrorMessage: Boolean,
|
centerErrorMessage: Boolean,
|
||||||
keyDownHandler: Function,
|
keyDownHandler: Function,
|
||||||
|
isEmailOptional: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const uuid = uuidv4();
|
const uuid = uuidv4();
|
||||||
|
|
@ -193,7 +197,9 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
questionText() {
|
questionText() {
|
||||||
return this.getCmsContent(this.cmsWidgetName, "QuestionText");
|
return this.isEmailOptional
|
||||||
|
? this.getCmsContent(this.cmsWidgetName, "QuestionText") + " (optional)"
|
||||||
|
: this.getCmsContent(this.cmsWidgetName, "QuestionText");
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
get: function () {
|
get: function () {
|
||||||
|
|
|
||||||
|
|
@ -129,11 +129,7 @@ async function getLatestPageForRedirection() {
|
||||||
} else if (!estimateComponent.methods.arePagePrerequisitesValid()) {
|
} else if (!estimateComponent.methods.arePagePrerequisitesValid()) {
|
||||||
return fmgPageValues.VEHICLE_DAMAGE;
|
return fmgPageValues.VEHICLE_DAMAGE;
|
||||||
} else {
|
} else {
|
||||||
if (scheduleComponent.methods.arePagePrerequisitesValid()) {
|
if (quoteComponent.methods.arePagePrerequisitesValid()) {
|
||||||
return fmgPageValues.SCHEDULE;
|
|
||||||
} else if (serviceLocationComponent.methods.arePagePrerequisitesValid()) {
|
|
||||||
return fmgPageValues.SERVICE_LOCATION;
|
|
||||||
} else if (quoteComponent.methods.arePagePrerequisitesValid()) {
|
|
||||||
return fmgPageValues.QUOTE;
|
return fmgPageValues.QUOTE;
|
||||||
} else if (capabilityQuestionsComponent.methods.arePagePrerequisitesValid()) {
|
} else if (capabilityQuestionsComponent.methods.arePagePrerequisitesValid()) {
|
||||||
return fmgPageValues.CAPABILITY_QUESTIONS;
|
return fmgPageValues.CAPABILITY_QUESTIONS;
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { storeMutations } from "@/constants/store-mutations";
|
||||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||||
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
|
|
||||||
jest.mock("@/helpers/damage-helper", () => ({
|
jest.mock("@/helpers/damage-helper", () => ({
|
||||||
isGlassAvailableForCarId: jest.fn().mockImplementation(() => true),
|
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({
|
function setupMocks({
|
||||||
isZipValid = true,
|
isZipValid = true,
|
||||||
isZipServiceable = true,
|
isZipServiceable = true,
|
||||||
|
|
@ -709,6 +721,7 @@ function setupMocks({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
mixins: [mockMixin],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,11 @@
|
||||||
|
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" ref="funnelSubHeader" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" ref="funnelSubHeader" />
|
||||||
|
|
||||||
<customerQuestions ref="customerQuestions" v-model="customerQuestions" />
|
<customerQuestions
|
||||||
|
ref="customerQuestions"
|
||||||
|
v-model="customerQuestions"
|
||||||
|
:validationRules="EmailValidationRules"
|
||||||
|
:isEmailOptional="IsEmailOptional" />
|
||||||
|
|
||||||
<alert
|
<alert
|
||||||
ref="alertVinNotFound"
|
ref="alertVinNotFound"
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,9 @@
|
||||||
v-model="customerModel.emailAddress"
|
v-model="customerModel.emailAddress"
|
||||||
ref="emailAddress"
|
ref="emailAddress"
|
||||||
customInputId="emailAddress"
|
customInputId="emailAddress"
|
||||||
validationRules="email-address-required|email-address-format" />
|
:isRequired="!isEmailOptional"
|
||||||
|
:validationRules="validationRules"
|
||||||
|
:isEmailOptional="isEmailOptional" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mb-0">
|
<div class="row mb-0">
|
||||||
|
|
@ -79,6 +81,7 @@ export default {
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
validationRules: String,
|
validationRules: String,
|
||||||
|
isEmailOptional: Boolean,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
customerModel: {
|
customerModel: {
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { settleAllPromises } from "@/helpers/layout-helper.js";
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import baseMixin from "../../mixins/base-mixin";
|
import baseMixin from "../../mixins/base-mixin";
|
||||||
import { vinLookupMethodSelections } from "@/constants/vin-lookup-method-selections.js";
|
import { vinLookupMethodSelections } from "@/constants/vin-lookup-method-selections.js";
|
||||||
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
|
|
||||||
// Mock our module for promises.
|
// Mock our module for promises.
|
||||||
jest.mock("@/helpers/layout-helper.js", () => ({
|
jest.mock("@/helpers/layout-helper.js", () => ({
|
||||||
|
|
@ -273,12 +274,24 @@ function setupMocks({
|
||||||
Answers: cmsAnswers,
|
Answers: cmsAnswers,
|
||||||
FunnelFooterWidget: FunnelFooterWidget,
|
FunnelFooterWidget: FunnelFooterWidget,
|
||||||
};
|
};
|
||||||
|
const mockMixin = {
|
||||||
|
methods: {
|
||||||
|
getSettingValue: jest.fn((settingName) => {
|
||||||
|
if (settingName === experimentSettings.IS_EMAIL_OPTIONAL) {
|
||||||
|
return "true";
|
||||||
|
}
|
||||||
|
return "false";
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
};
|
||||||
const apiPromise = Promise.resolve({ cmsContent });
|
const apiPromise = Promise.resolve({ cmsContent });
|
||||||
settleAllPromises.mockImplementation(() => apiPromise);
|
settleAllPromises.mockImplementation(() => apiPromise);
|
||||||
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
|
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
|
||||||
|
|
||||||
const mountOptions = getMountOptions({ ...mountOptionsMockData, mixins: [baseMixin] });
|
const mountOptions = getMountOptions({
|
||||||
|
...mountOptionsMockData,
|
||||||
|
mixins: [baseMixin, mockMixin],
|
||||||
|
});
|
||||||
mountOptions["attachTo"] = document.body;
|
mountOptions["attachTo"] = document.body;
|
||||||
|
|
||||||
const wrapper = shallowMount(estimate, mountOptions);
|
const wrapper = shallowMount(estimate, mountOptions);
|
||||||
|
|
|
||||||
|
|
@ -52,9 +52,10 @@
|
||||||
cmsWidgetName="EmailAddressQuestionWidget"
|
cmsWidgetName="EmailAddressQuestionWidget"
|
||||||
v-model="emailAddress"
|
v-model="emailAddress"
|
||||||
inputId="emailAddress"
|
inputId="emailAddress"
|
||||||
isRequired
|
:isRequired="!IsEmailOptional"
|
||||||
disableAutoFill
|
disableAutoFill
|
||||||
validationRules="email-address-required|email-address-format" />
|
:validationRules="EmailValidationRules"
|
||||||
|
:isEmailOptional="IsEmailOptional" />
|
||||||
<textBlock cmsWidgetName="QuoteEmailTextBlockWidget" typeStyle="caption" />
|
<textBlock cmsWidgetName="QuoteEmailTextBlockWidget" typeStyle="caption" />
|
||||||
|
|
||||||
<alert
|
<alert
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { nextTick } from "vue";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import { storeMutations } from "@/constants/store-mutations";
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
|
|
||||||
jest.mock("@/assets/img/loader.gif", () => "loader.gif");
|
jest.mock("@/assets/img/loader.gif", () => "loader.gif");
|
||||||
jest.mock("@/assets/img/windshield.png", () => "windshield.png");
|
jest.mock("@/assets/img/windshield.png", () => "windshield.png");
|
||||||
|
|
@ -692,11 +693,20 @@ function setupMocks({
|
||||||
};
|
};
|
||||||
|
|
||||||
const apiPromise = Promise.resolve(apiResponses);
|
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);
|
settleAllPromises.mockImplementation(() => apiPromise);
|
||||||
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
|
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
|
mountOptions["attachTo"] = document.body; // append wrapper to document.body to test DOM methods
|
||||||
|
|
||||||
const wrapper = shallowMount(licensePlateLookup, mountOptions);
|
const wrapper = shallowMount(licensePlateLookup, mountOptions);
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,9 @@
|
||||||
cmsWidgetName="EmailAddressQuestionWidget"
|
cmsWidgetName="EmailAddressQuestionWidget"
|
||||||
v-model="email"
|
v-model="email"
|
||||||
customInputId="email"
|
customInputId="email"
|
||||||
validationRules="email-address-required|email-address-format" />
|
:isRequired="!IsEmailOptional"
|
||||||
|
:validationRules="EmailValidationRules"
|
||||||
|
:isEmailOptional="IsEmailOptional" />
|
||||||
|
|
||||||
<textBlock cmsWidgetName="QuoteEmailTextBlockWidget" typeStyle="caption" />
|
<textBlock cmsWidgetName="QuoteEmailTextBlockWidget" typeStyle="caption" />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,25 @@
|
||||||
import { mount, shallowMount } from "@vue/test-utils";
|
import { mount, shallowMount } from "@vue/test-utils";
|
||||||
import promoModalQuestion from "./promo-modal-question";
|
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", () => ({
|
jest.mock("@/digital-components/textbox-question/textbox-question", () => ({
|
||||||
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
|
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
|
||||||
|
|
@ -79,4 +99,127 @@ describe("promo-modal-question.vue", () => {
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.emitted("update:modelValue")).toEqual([[lineItems]]);
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,12 @@ export default {
|
||||||
modelValue: Object,
|
modelValue: Object,
|
||||||
modalWidgetName: String,
|
modalWidgetName: String,
|
||||||
availableVaps: Object,
|
availableVaps: Object,
|
||||||
|
pageName: String,
|
||||||
|
taxPromos: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
promoLinkText() {
|
promoLinkText() {
|
||||||
|
|
@ -253,44 +259,52 @@ export default {
|
||||||
this.promoCode,
|
this.promoCode,
|
||||||
this.lineItems,
|
this.lineItems,
|
||||||
this.availableVaps,
|
this.availableVaps,
|
||||||
"payment-method"
|
this.pageName
|
||||||
);
|
);
|
||||||
|
|
||||||
if (promoCodeData.isValid) {
|
if (promoCodeData.isValid) {
|
||||||
const pricedLineItemsToTax = [];
|
if (this.taxPromos) {
|
||||||
pricedLineItemsToTax.push(...promoCodeData.promoCode);
|
const pricedLineItemsToTax = [];
|
||||||
const taxedLineItems = await baseMixin.methods.dispatchStoreActionWithLogging(
|
pricedLineItemsToTax.push(...promoCodeData.promoCode);
|
||||||
storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
|
const taxedLineItems =
|
||||||
{
|
await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
billToAccountNumber: "87291",
|
storeActions.TAX_ORDER_ITEMS_AND_SAVE_SERVER_DATA,
|
||||||
providerNumber:
|
{
|
||||||
store.getters.order.serviceLocation.provider.providerNumber,
|
billToAccountNumber: "87291",
|
||||||
appointmentType: store.getters.order.serviceLocation.appointmentType,
|
providerNumber:
|
||||||
serviceLocationCity: store.getters.order.serviceLocation.city,
|
store.getters.order.serviceLocation.provider.providerNumber,
|
||||||
serviceLocationState: store.getters.order.serviceLocation.state,
|
appointmentType:
|
||||||
serviceLocationZipCode: store.getters.order.serviceLocation.zipCode,
|
store.getters.order.serviceLocation.appointmentType,
|
||||||
pricedLineItems: pricedLineItemsToTax,
|
serviceLocationCity: store.getters.order.serviceLocation.city,
|
||||||
},
|
serviceLocationState: store.getters.order.serviceLocation.state,
|
||||||
"payment-method",
|
serviceLocationZipCode:
|
||||||
false
|
store.getters.order.serviceLocation.zipCode,
|
||||||
);
|
pricedLineItems: pricedLineItemsToTax,
|
||||||
|
},
|
||||||
|
"payment-method",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
// Match all line items to the line items as they are in the store
|
// Match all line items to the line items as they are in the store
|
||||||
// and rebuild the original structure.
|
// and rebuild the original structure.
|
||||||
this.lineItems = mapTaxedLineItemsToStoreFormat(taxedLineItems, this.lineItems);
|
this.lineItems = mapTaxedLineItemsToStoreFormat(
|
||||||
const taxedVaps = mapTaxedLineItemsToStoreFormat(
|
taxedLineItems,
|
||||||
taxedLineItems,
|
this.lineItems
|
||||||
this.availableVaps
|
);
|
||||||
);
|
const taxedVaps = mapTaxedLineItemsToStoreFormat(
|
||||||
|
taxedLineItems,
|
||||||
|
this.availableVaps
|
||||||
|
);
|
||||||
|
|
||||||
const getVaps = getVapsThatNeedToBeAddedToSatisfyPromos(
|
const getVaps = getVapsThatNeedToBeAddedToSatisfyPromos(
|
||||||
promoCodeData.promoCode,
|
promoCodeData.promoCode,
|
||||||
taxedVaps,
|
taxedVaps,
|
||||||
this.lineItems
|
this.lineItems
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.lineItems.vaps?.push(...getVaps);
|
||||||
|
}
|
||||||
this.lineItems?.promos.push(...promoCodeData.promoCode);
|
this.lineItems?.promos.push(...promoCodeData.promoCode);
|
||||||
this.lineItems.vaps?.push(...getVaps);
|
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
} else {
|
} else {
|
||||||
this.getErrorMessage(promoCodeData.errorCode, promoCodeData.additionalInfo);
|
this.getErrorMessage(promoCodeData.errorCode, promoCodeData.additionalInfo);
|
||||||
|
|
|
||||||
|
|
@ -133,8 +133,6 @@ describe("quote.vue", () => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
wrapper.vm.pricedGlassParts = [];
|
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
await wrapper.vm.forwardButtonAction();
|
await wrapper.vm.forwardButtonAction();
|
||||||
|
|
||||||
|
|
@ -172,8 +170,6 @@ describe("quote.vue", () => {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
wrapper.vm.pricedGlassParts = [];
|
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
await wrapper.vm.forwardButtonAction();
|
await wrapper.vm.forwardButtonAction();
|
||||||
|
|
||||||
|
|
@ -288,8 +284,8 @@ describe("quote.vue", () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
expect(wrapper.vm.pricedGlassParts !== null).toBe(true);
|
expect(wrapper.vm.lineItems !== null).toBe(true);
|
||||||
expect(wrapper.vm.supportingItems !== null).toBe(true);
|
expect(wrapper.vm.availableVaps !== null).toBe(true);
|
||||||
expect(wrapper.vm.availableLineItems !== null).toBe(true);
|
expect(wrapper.vm.availableLineItems !== null).toBe(true);
|
||||||
// This should have its own test
|
// This should have its own test
|
||||||
//expect(vm.isInsuranceSelected !== null).toBe(true);
|
//expect(vm.isInsuranceSelected !== null).toBe(true);
|
||||||
|
|
@ -548,6 +544,31 @@ describe("quote.vue", () => {
|
||||||
//Assert
|
//Assert
|
||||||
expect(wrapper.vm.isInsuranceSelected).toBe(true);
|
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 }) {
|
function setupMocks({ customMountOptions }) {
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,15 @@
|
||||||
cmsWidgetName="AfterpayModalWidget"
|
cmsWidgetName="AfterpayModalWidget"
|
||||||
:lineItems="availableLineItems" />
|
:lineItems="availableLineItems" />
|
||||||
|
|
||||||
|
<promoModalQuestion
|
||||||
|
class="small mt-4"
|
||||||
|
v-model="lineItems"
|
||||||
|
:availableVaps="availableVaps"
|
||||||
|
:availableLineItems="availableLineItems"
|
||||||
|
pageName="quote"
|
||||||
|
:taxPromos="false"
|
||||||
|
modalWidgetName="PromoModalWidget" />
|
||||||
|
|
||||||
<textBlock
|
<textBlock
|
||||||
cmsWidgetName="quoteDisclaimer"
|
cmsWidgetName="quoteDisclaimer"
|
||||||
justifyText="left"
|
justifyText="left"
|
||||||
|
|
@ -89,9 +98,11 @@ import { payWithInsuranceStates } from "@/constants/pay-with-insurance-states";
|
||||||
import {
|
import {
|
||||||
revalidatePromosAndValidateQueryStringPromo,
|
revalidatePromosAndValidateQueryStringPromo,
|
||||||
buildToastMessagesFromRevalidateOrValidatePromoResponse,
|
buildToastMessagesFromRevalidateOrValidatePromoResponse,
|
||||||
|
createPromoSuccessAlert,
|
||||||
} from "@/helpers/promotions-helper";
|
} from "@/helpers/promotions-helper";
|
||||||
import { queryStrings } from "@/constants/query-strings";
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
import { getQuerystringParameter } from "@/helpers/querystring-helper";
|
||||||
|
import promoModalQuestion from "@/layouts/payment-method/promo-modal-question/promo-modal-question";
|
||||||
|
|
||||||
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
|
|
||||||
|
|
@ -139,7 +150,8 @@ export default {
|
||||||
];
|
];
|
||||||
|
|
||||||
const resultMap = await settleAllPromises(promiseResultMap);
|
const resultMap = await settleAllPromises(promiseResultMap);
|
||||||
const nullSafeGlassParts = store.getters.order.lineItems.glassParts ?? [];
|
const lineItems = store.getters.order.lineItems;
|
||||||
|
const nullSafeGlassParts = lineItems.glassParts ?? [];
|
||||||
const availableLineItems = [
|
const availableLineItems = [
|
||||||
resultMap.rainDefense,
|
resultMap.rainDefense,
|
||||||
...resultMap.supportingItems,
|
...resultMap.supportingItems,
|
||||||
|
|
@ -164,6 +176,7 @@ export default {
|
||||||
|
|
||||||
// Promo logic
|
// Promo logic
|
||||||
// Populate the previous state of promos for toast message usage in "next()"
|
// Populate the previous state of promos for toast message usage in "next()"
|
||||||
|
const availableVaps = [...resultMap.wipers, resultMap.rainDefense];
|
||||||
const oldActivePromos = store.getters.lineItems.promos?.slice(0);
|
const oldActivePromos = store.getters.lineItems.promos?.slice(0);
|
||||||
const oldInactivePromos = store.getters.order.payment.inactivePromos?.slice(0);
|
const oldInactivePromos = store.getters.order.payment.inactivePromos?.slice(0);
|
||||||
|
|
||||||
|
|
@ -175,13 +188,16 @@ export default {
|
||||||
pricingResults,
|
pricingResults,
|
||||||
"quote"
|
"quote"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
lineItems.promos = lineItems.promos ?? [];
|
||||||
|
lineItems.vaps = lineItems.vaps ?? [];
|
||||||
// End of promo logic
|
// End of promo logic
|
||||||
|
|
||||||
// Call the "next" function to complete the transition to this page.
|
// Call the "next" function to complete the transition to this page.
|
||||||
next((vm) => {
|
next((vm) => {
|
||||||
vm.setCmsContent(resultMap.cmsContent);
|
vm.setCmsContent(resultMap.cmsContent);
|
||||||
vm.pricedGlassParts = nullSafeGlassParts;
|
vm.availableVaps = availableVaps;
|
||||||
vm.supportingItems = resultMap.supportingItems;
|
vm.lineItems = lineItems;
|
||||||
vm.availableLineItems = pricingResults;
|
vm.availableLineItems = pricingResults;
|
||||||
vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(vm.availableLineItems);
|
vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(vm.availableLineItems);
|
||||||
|
|
||||||
|
|
@ -208,15 +224,17 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isInsuranceSelected: null,
|
isInsuranceSelected: null,
|
||||||
selectedVaps: null,
|
|
||||||
availableLineItems: null,
|
availableLineItems: null,
|
||||||
supportingItems: null,
|
lineItems: [],
|
||||||
pricedGlassParts: null,
|
availableVaps: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
allActivePromos() {
|
allActivePromos() {
|
||||||
return this.$store.getters.order.lineItems.promos ?? [];
|
return this.lineItems.promos ?? [];
|
||||||
|
},
|
||||||
|
lineItemsCloneForWatcher() {
|
||||||
|
return Object.assign({}, this.lineItems);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -258,7 +276,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
vapsItemsSelectedAction(vapsItemsSelected) {
|
vapsItemsSelectedAction(vapsItemsSelected) {
|
||||||
this.selectedVaps = vapsItemsSelected;
|
this.lineItems.vaps = vapsItemsSelected;
|
||||||
},
|
},
|
||||||
backButtonAction() {
|
backButtonAction() {
|
||||||
vehicleQuestionsMixin.methods.navigateBack(this);
|
vehicleQuestionsMixin.methods.navigateBack(this);
|
||||||
|
|
@ -285,15 +303,23 @@ export default {
|
||||||
this.supportingItems = this.filterOutFees(this.supportingItems);
|
this.supportingItems = this.filterOutFees(this.supportingItems);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.pricedGlassParts.length > 0) {
|
if (this.lineItems.glassParts?.length > 0) {
|
||||||
this.dispatchStoreAction(
|
this.dispatchStoreAction(
|
||||||
this.storeActions.SAVE_GLASS_PARTS_SUPPRESSING_STATE_RESETTING,
|
this.storeActions.SAVE_GLASS_PARTS_SUPPRESSING_STATE_RESETTING,
|
||||||
this.pricedGlassParts,
|
this.lineItems.glassParts,
|
||||||
false
|
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;
|
const payment = this.$store.getters.payment;
|
||||||
if (payment.isInsurance) {
|
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: {
|
components: {
|
||||||
funnelHeader,
|
funnelHeader,
|
||||||
navbar,
|
navbar,
|
||||||
|
|
@ -322,6 +374,7 @@ export default {
|
||||||
contentGroupModal,
|
contentGroupModal,
|
||||||
loadingModal,
|
loadingModal,
|
||||||
afterpayModalBanner,
|
afterpayModalBanner,
|
||||||
|
promoModalQuestion,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -256,6 +256,57 @@ describe("service-package-question.vue", () => {
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.vm.selectedPackageName).toBe("TierThree");
|
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", () => {
|
describe("service-package-question.vue, matching business rules for package display", () => {
|
||||||
// mock scenarios in figma:
|
// mock scenarios in figma:
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,9 @@ export default {
|
||||||
this.selectDefaultPackage();
|
this.selectDefaultPackage();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
activePromos() {
|
||||||
|
this.selectDefaultPackage();
|
||||||
|
},
|
||||||
selectedPackageName(newValue) {
|
selectedPackageName(newValue) {
|
||||||
const VapsProductsInSelectedPackage = this.getVapsLineItemsForSelectedPackage(newValue);
|
const VapsProductsInSelectedPackage = this.getVapsLineItemsForSelectedPackage(newValue);
|
||||||
this.$emit("vapsItemsSelected", VapsProductsInSelectedPackage);
|
this.$emit("vapsItemsSelected", VapsProductsInSelectedPackage);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import vinLookup from "./vin-lookup.vue";
|
||||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios.js";
|
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios.js";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper.js";
|
import { settleAllPromises } from "@/helpers/layout-helper.js";
|
||||||
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
|
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
|
||||||
|
|
@ -378,5 +379,11 @@ function mockOutStubFunctions(wrapper) {
|
||||||
const mockMixin = {
|
const mockMixin = {
|
||||||
methods: {
|
methods: {
|
||||||
getCmsContent: jest.fn(() => "placeholder CMS content"),
|
getCmsContent: jest.fn(() => "placeholder CMS content"),
|
||||||
|
getSettingValue: jest.fn((settingName) => {
|
||||||
|
if (settingName === experimentSettings.IS_EMAIL_OPTIONAL) {
|
||||||
|
return "true";
|
||||||
|
}
|
||||||
|
return "false";
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -53,8 +53,9 @@
|
||||||
cmsWidgetName="EmailAddressQuestionWidget"
|
cmsWidgetName="EmailAddressQuestionWidget"
|
||||||
v-model="emailAddress"
|
v-model="emailAddress"
|
||||||
customInputId="emailAddress"
|
customInputId="emailAddress"
|
||||||
isRequired
|
:isRequired="!IsEmailOptional"
|
||||||
validationRules="email-address-required|email-address-format" />
|
:validationRules="EmailValidationRules"
|
||||||
|
:isEmailOptional="IsEmailOptional" />
|
||||||
|
|
||||||
<textBlock cmsWidgetName="QuoteEmailTextBlockWidget" typeStyle="caption" />
|
<textBlock cmsWidgetName="QuoteEmailTextBlockWidget" typeStyle="caption" />
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,20 @@ import { storeActions } from "@/constants/store-actions.js";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
||||||
import { saveSession } from "@/helpers/heritage-integration/order-helper.js";
|
import { saveSession } from "@/helpers/heritage-integration/order-helper.js";
|
||||||
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
|
|
||||||
export default {
|
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: {
|
methods: {
|
||||||
async navigateForwardWithSingleCarMatch() {
|
async navigateForwardWithSingleCarMatch() {
|
||||||
const pageName = this.$options?.name;
|
const pageName = this.$options?.name;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue