diff --git a/src/constants/error-messages.js b/src/constants/error-messages.js
index 2f49e2f1b..380a5a98c 100644
--- a/src/constants/error-messages.js
+++ b/src/constants/error-messages.js
@@ -32,7 +32,7 @@ const errorMessages = {
DATE_REQUIRED: "Please select a date",
PHONE_REQUIRED: "Please enter your phone number",
PHONE_FORMAT: "Phone number must be 10 digits",
- SMS_CONSENT_REQUIRED: "Please select at least one consent option",
+ SMS_CONSENT_REQUIRED: "Please select checkbox to receive text messages",
YEAR_REQUIRED: "Please select your vehicle year",
MAKE_REQUIRED: "Please select your vehicle make",
MODEL_REQUIRED: "Please select your vehicle model",
diff --git a/src/constants/experiments.js b/src/constants/experiments.js
index b470cc425..1cd1260a7 100644
--- a/src/constants/experiments.js
+++ b/src/constants/experiments.js
@@ -52,6 +52,12 @@ const experimentSettings = {
// Claim and Coverage for 2.0
ENABLE_NON_CLAIM_AND_COVERAGE_FLOW: "EnableNonC&CFlow",
NON_CLAIM_AND_COVERAGE_TEST_PARENT_ACCOUNT: "NonC&CTestParentAccount",
+
+ // Service Package Name
+ SHOW_SERVICE_PACKAGE_NAME_TEST: "ShowServicePackageNameTest",
+ TIER_ONE: "TierOne",
+ TIER_TWO: "TierTwo",
+ TIER_THREE: "TierThree",
};
const experimentTriggers = {
diff --git a/src/digital-components/question-chain/question-chain.spec.js b/src/digital-components/question-chain/question-chain.spec.js
index d516afc03..7435feefd 100644
--- a/src/digital-components/question-chain/question-chain.spec.js
+++ b/src/digital-components/question-chain/question-chain.spec.js
@@ -244,10 +244,8 @@ describe("Question Chain component", () => {
test("should return answer object if returnedAnswer is a final matching answer", async () => {
//Arrange
- const { wrapper } = setupMocks({});
- const testReturnedAnswer = "1|answer|DB10840|No";
- await wrapper.setData({
- questions: [
+ const { wrapper } = setupMocks({
+ questionDataProp: [
{
questionSequence: 1,
questionText: "Question here?",
@@ -256,17 +254,21 @@ describe("Question Chain component", () => {
answerResult: "DB09410",
answerText: "Yes",
nextQuestionSequence: null,
+ problemQuestionId: 9529,
},
{
answerResult: "DB10840",
answerText: "No",
nextQuestionSequence: null,
+ problemQuestionId: 9531,
},
],
},
],
});
+ const testReturnedAnswer = "1|answer|DB10840|No";
await wrapper.setProps({ index: 0 });
+ await nextTick();
//Act
const result = wrapper.vm.getQuestionChainAnswerIfComplete(testReturnedAnswer);
@@ -274,16 +276,52 @@ describe("Question Chain component", () => {
//Assert
expect(result).toMatchObject({
answerResult: "DB10840",
- answeredQuestions: [
- { questionNum: 1, questionText: "Question here?", selectedAnswerText: "No" },
- {
- questionNum: 1,
- questionText: "Does only your center sliding piece need to be replaced?",
- selectedAnswerText: "No",
- },
- ],
+ problemQuestionId: 9531,
index: 0,
});
+ expect(result.answeredQuestions).toEqual([
+ {
+ questionText: "Question here?",
+ selectedAnswer: "1|answer|DB10840|No",
+ selectedAnswerText: "No",
+ questionNum: 1,
+ problemQuestionId: 9531,
+ },
+ ]);
+ });
+
+ test("should include problemQuestionId from parts-or-questions answer on created", async () => {
+ //Arrange
+ const { wrapper } = setupMocks({
+ questionDataProp: [
+ {
+ questionSequence: 1,
+ questionText:
+ "Does the rubber seal around your windshield have a chrome strip running through it?",
+ answers: [
+ {
+ answerResult: "WCR 848",
+ answerText: "Yes",
+ nextQuestionSequence: null,
+ problemQuestionId: 9531,
+ },
+ {
+ answerResult: "WCR 848",
+ answerText: "No",
+ nextQuestionSequence: null,
+ problemQuestionId: 9529,
+ },
+ ],
+ },
+ ],
+ });
+
+ //Act
+ await nextTick();
+
+ //Assert
+ expect(wrapper.vm.questions[0].answers[0].problemQuestionId).toBe(9531);
+ expect(wrapper.vm.questions[0].answers[1].problemQuestionId).toBe(9529);
});
});
});
diff --git a/src/digital-components/question-chain/question-chain.vue b/src/digital-components/question-chain/question-chain.vue
index 18b123aae..b1960eb51 100644
--- a/src/digital-components/question-chain/question-chain.vue
+++ b/src/digital-components/question-chain/question-chain.vue
@@ -60,6 +60,7 @@ export default {
: q.questionSequence + "|answer|" + a.answerResult + "|" + a.answerText,
nextQuestionSequence: a.nextQuestionSequence,
answerResult: a.answerResult,
+ problemQuestionId: a.problemQuestionId,
questionSequence: q.questionSequence,
questionType: a.nextQuestionSequence ? "nextQuestion" : "answer",
};
@@ -81,6 +82,17 @@ export default {
}
},
methods: {
+ getProblemQuestionIdFromSelectedAnswer(question, selectedAnswer) {
+ if (!question?.answers || !selectedAnswer) {
+ return null;
+ }
+
+ const matchedAnswer = question.answers.find(
+ (answer) => answer.value === selectedAnswer
+ );
+
+ return matchedAnswer?.problemQuestionId ?? null;
+ },
handleAnswer(question, returnedAnswer) {
/*
returnedAnswer example format:
@@ -126,6 +138,10 @@ export default {
selectedAnswer: q.answerSelected,
selectedAnswerText: q.answerSelected.split("|")[3],
questionNum: q.questionSequence,
+ problemQuestionId: this.getProblemQuestionIdFromSelectedAnswer(
+ q,
+ q.answerSelected
+ ),
});
}
});
@@ -144,10 +160,17 @@ export default {
} else {
// reset current question index (removes .current-question class)
this.currentQuestionNum = 0; // reset count
+ const answeredQuestion = this.questions.find(
+ (q) => q.questionSequence === questionNum
+ );
// return an object with the part answer, all the answered questions, and the part index
return {
answerResult: questionAnswer,
+ problemQuestionId: this.getProblemQuestionIdFromSelectedAnswer(
+ answeredQuestion,
+ returnedAnswer
+ ),
answeredQuestions: answeredQuestions,
index: this.index,
};
diff --git a/src/fmg-components/cart/cart.spec.js b/src/fmg-components/cart/cart.spec.js
index f06a49a90..a920e39f6 100644
--- a/src/fmg-components/cart/cart.spec.js
+++ b/src/fmg-components/cart/cart.spec.js
@@ -5,8 +5,24 @@ import cart from "@/fmg-components/cart/cart.vue";
import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
+import { packageNames } from "@/constants/package-names";
+
import store from "@/store";
+jest.mock("@/mixins/experiment-mixin.js", () => ({
+ methods: {
+ getSettingValue(settingName) {
+ return false;
+ },
+ hasSetting(settingName) {
+ return false;
+ },
+ hasSettingEqualTo(settingName, settingValue) {
+ return false;
+ },
+ },
+}));
+
global.$logger = {
logInformation: jest.fn(),
logWarning: jest.fn(),
@@ -354,9 +370,73 @@ describe("cart.vue", () => {
expect(found).toBe(true);
});
});
+
+ describe("servicePackageTitle", () => {
+ const servicePackageCmsContent = {
+ ServicePackageTitle: {
+ Answers: [
+ {
+ Name: packageNames.TIER_ONE,
+ SubWidgetName: "EconomyServiceTitle",
+ },
+ {
+ Name: packageNames.TIER_TWO,
+ SubWidgetName: "StandardServiceTitle",
+ },
+ {
+ Name: packageNames.TIER_THREE,
+ SubWidgetName: "PremiumServiceTitle",
+ },
+ ],
+ },
+ EconomyServiceTitle: {
+ Text: "Glass service only",
+ },
+ };
+
+ const defaultServicePackageProps = {
+ servicePackageOptionsCmsName: "ServicePackageTitle",
+ damage: {
+ glassToReplace: [{ glassLocation: "Windshield" }],
+ isRepair: false,
+ },
+ modelValue: {
+ glassParts: [],
+ supportingItems: [],
+ vaps: [],
+ promos: [],
+ },
+ availableVaps: [],
+ };
+
+ test("uses CMS widget name when experiment is not active", () => {
+ const { wrapper } = setupMocks({
+ props: defaultServicePackageProps,
+ cmsContent: servicePackageCmsContent,
+ });
+
+ expect(wrapper.vm.servicePackageTitleWidget).toBe("EconomyServiceTitle");
+ expect(wrapper.vm.servicePackageTitleText).toBeNull();
+ });
+
+ test("uses experiment display name when ServicePackageNameTest is active", () => {
+ const getExperimentPackageLabelSpy = jest
+ .spyOn(cart.methods, "getExperimentPackageLabel")
+ .mockReturnValue("Essential");
+
+ const { wrapper } = setupMocks({
+ props: defaultServicePackageProps,
+ cmsContent: servicePackageCmsContent,
+ });
+
+ expect(wrapper.vm.servicePackageTitleText).toBe("Essential");
+
+ getExperimentPackageLabelSpy.mockRestore();
+ });
+ });
});
-function setupMocks({ options, props }) {
+function setupMocks({ options, props, cmsContent }) {
const mountOptions = getMountOptions({
...options,
});
@@ -365,7 +445,9 @@ function setupMocks({ options, props }) {
methods: {
getTierOnePackagePrice: jest.fn(),
filterOutFees: jest.fn(),
- getCmsContent: jest.fn(),
+ getCmsContent: jest.fn((widgetName, cmsFieldName) => {
+ return cmsContent?.[widgetName]?.[cmsFieldName];
+ }),
},
};
diff --git a/src/fmg-components/cart/cart.vue b/src/fmg-components/cart/cart.vue
index 5d48dc5ae..1bbfb0b76 100644
--- a/src/fmg-components/cart/cart.vue
+++ b/src/fmg-components/cart/cart.vue
@@ -34,7 +34,9 @@
-
+
{{ getLineItemAmount(packagePrice) }}
@@ -193,6 +195,7 @@ import { formatToUSDollar } from "@/helpers/cms-content-helper";
// Constants
import { partTypeStrings } from "@/constants/part-type-strings";
+import { packageNames } from "@/constants/package-names";
import { cartItemCategories } from "@/constants/cart-item-categories";
import { cartItemTypes } from "@/constants/cart-item-types";
import { coverageStatus, cartItemTypesCoveredByInsurance } from "@/constants/insurance";
@@ -267,6 +270,24 @@ export default {
return subTotal;
},
+ getExperimentPackageLabel(tierName) {
+ if (
+ !experimentMixin.methods.hasSettingEqualTo(
+ experimentSettings.SHOW_SERVICE_PACKAGE_NAME_TEST,
+ "true"
+ )
+ ) {
+ return null;
+ }
+
+ const tierSettingKey = {
+ [packageNames.TIER_ONE]: experimentSettings.TIER_ONE,
+ [packageNames.TIER_TWO]: experimentSettings.TIER_TWO,
+ [packageNames.TIER_THREE]: experimentSettings.TIER_THREE,
+ }[tierName];
+
+ return tierSettingKey ? experimentMixin.methods.getSettingValue(tierSettingKey) : null;
+ },
getQuotePageDiscount() {
const quotePageDiscountLineItems = this.quotePageDiscountCartItem;
let subTotal = 0;
@@ -547,6 +568,9 @@ export default {
return currentPackage.SubWidgetName;
},
+ servicePackageTitleText() {
+ return this.getExperimentPackageLabel(this.packageLevel);
+ },
packageLevel() {
const tier = getHighestFullySatisfiedTier(
this.glassToReplace,
diff --git a/src/fmg-components/save-progress-popup-question/save-progress-popup-question.spec.js b/src/fmg-components/save-progress-popup-question/save-progress-popup-question.spec.js
index ad42fa617..3db98487a 100644
--- a/src/fmg-components/save-progress-popup-question/save-progress-popup-question.spec.js
+++ b/src/fmg-components/save-progress-popup-question/save-progress-popup-question.spec.js
@@ -45,14 +45,14 @@ describe("save-progress-popup-question ", () => {
});
describe("contact method tabs", () => {
- test("should default to the phone tab", () => {
+ test("should default to the email tab", () => {
const { wrapper } = setupMocks({
props: {
modalWidgetName: "SaveProgressPopupWidget",
},
});
- expect(wrapper.vm.isPhoneTabSelected).toBe(true);
+ expect(wrapper.vm.isPhoneTabSelected).toBe(false);
});
test("should use tab labels from phone and email specific CMS widgets", () => {
@@ -83,21 +83,24 @@ describe("save-progress-popup-question ", () => {
},
});
- expect(wrapper.vm.modalDisclaimerText).toBe("Phone disclaimer");
+ expect(wrapper.vm.modalDisclaimerText).toBe("Email disclaimer");
- wrapper.vm.selectContactMethod("EmailAnswer");
+ wrapper.vm.selectContactMethod("PhoneAnswer");
await wrapper.vm.$nextTick();
- expect(wrapper.vm.modalDisclaimerText).toBe("Email disclaimer");
+ expect(wrapper.vm.modalDisclaimerText).toBe("Phone disclaimer");
});
- test("should use phone question CMS widget on the phone tab", () => {
+ test("should use phone question CMS widget on the phone tab", async () => {
const { wrapper } = setupMocks({
props: {
modalWidgetName: "SaveProgressPopupWidget",
},
});
+ wrapper.vm.selectContactMethod("PhoneAnswer");
+ await wrapper.vm.$nextTick();
+
expect(wrapper.vm.phoneQuestionWidgetName).toBe("SaveProgressPopupPhoneQuestionWidget");
});
@@ -108,15 +111,26 @@ describe("save-progress-popup-question ", () => {
},
});
- wrapper.vm.selectContactMethod("EmailAnswer");
- await wrapper.vm.$nextTick();
-
expect(wrapper.vm.contactMethod).toBe("EmailAnswer");
expect(wrapper.vm.isPhoneTabSelected).toBe(false);
- expect(wrapper.vm.userInput).toBe("");
expect(wrapper.vm.emailQuestionWidgetName).toBe("SaveProgressPopupEmailQuestionWidget");
});
+ test("should show phone content when the phone tab is selected", async () => {
+ const { wrapper } = setupMocks({
+ props: {
+ modalWidgetName: "SaveProgressPopupWidget",
+ },
+ });
+
+ wrapper.vm.selectContactMethod("PhoneAnswer");
+ await wrapper.vm.$nextTick();
+
+ expect(wrapper.vm.contactMethod).toBe("PhoneAnswer");
+ expect(wrapper.vm.isPhoneTabSelected).toBe(true);
+ expect(wrapper.vm.userInput).toBe("");
+ });
+
test("should reset sms consent when switching tabs", async () => {
const { wrapper } = setupMocks({
props: {
@@ -126,7 +140,7 @@ describe("save-progress-popup-question ", () => {
wrapper.vm.smsConsent = { transactional: true, marketing: true };
wrapper.vm.showConsentErrors = true;
- wrapper.vm.selectContactMethod("EmailAnswer");
+ wrapper.vm.selectContactMethod("PhoneAnswer");
await wrapper.vm.$nextTick();
expect(wrapper.vm.smsConsent).toEqual({ transactional: false, marketing: false });
@@ -142,6 +156,7 @@ describe("save-progress-popup-question ", () => {
},
});
+ wrapper.vm.selectContactMethod("PhoneAnswer");
wrapper.vm.modal.validate = jest.fn().mockResolvedValue({ valid: true });
const resetSpy = jest.spyOn(wrapper.vm, "resetPhoneSendButtonStyle");
@@ -158,6 +173,7 @@ describe("save-progress-popup-question ", () => {
},
});
+ wrapper.vm.selectContactMethod("PhoneAnswer");
wrapper.vm.modal.validate = jest.fn().mockResolvedValue({ valid: false });
const resetSpy = jest.spyOn(wrapper.vm, "resetPhoneSendButtonStyle");
@@ -179,6 +195,7 @@ describe("save-progress-popup-question ", () => {
await flushPromises();
wrapper.vm.dispatchStoreAction = dispatchStoreAction;
+ wrapper.vm.selectContactMethod("PhoneAnswer");
wrapper.vm.userInput = "555-123-4567";
wrapper.vm.smsConsent = { transactional: true, marketing: false };
wrapper.vm.modal.validate = jest.fn().mockResolvedValue({ valid: true });
@@ -203,6 +220,7 @@ describe("save-progress-popup-question ", () => {
});
wrapper.vm.dispatchStoreAction = dispatchStoreAction;
+ wrapper.vm.selectContactMethod("PhoneAnswer");
wrapper.vm.userInput = "555-123-4567";
wrapper.vm.smsConsent = { transactional: true, marketing: false };
@@ -210,7 +228,7 @@ describe("save-progress-popup-question ", () => {
expect(dispatchStoreAction).toHaveBeenCalledWith(
storeActions.SAVE_PHONE_NUMBER,
- "555-123-4567",
+ "5551234567",
false
);
expect(dispatchStoreAction).not.toHaveBeenCalledWith(
@@ -238,7 +256,6 @@ describe("save-progress-popup-question ", () => {
});
wrapper.vm.dispatchStoreAction = dispatchStoreAction;
- wrapper.vm.selectContactMethod("EmailAnswer");
wrapper.vm.userInput = "test@example.com";
await wrapper.vm.saveProgress();
@@ -267,6 +284,7 @@ describe("save-progress-popup-question ", () => {
});
wrapper.vm.dispatchStoreAction = dispatchStoreAction;
+ wrapper.vm.selectContactMethod("PhoneAnswer");
wrapper.vm.userInput = "555-123-4567";
wrapper.vm.smsConsent = { transactional: true, marketing: false };
@@ -289,7 +307,6 @@ describe("save-progress-popup-question ", () => {
});
wrapper.vm.dispatchStoreAction = dispatchStoreAction;
- wrapper.vm.selectContactMethod("EmailAnswer");
wrapper.vm.userInput = "test@example.com";
await wrapper.vm.saveProgress();
diff --git a/src/fmg-components/save-progress-popup-question/save-progress-popup-question.vue b/src/fmg-components/save-progress-popup-question/save-progress-popup-question.vue
index 95fb40273..2d46b03b9 100644
--- a/src/fmg-components/save-progress-popup-question/save-progress-popup-question.vue
+++ b/src/fmg-components/save-progress-popup-question/save-progress-popup-question.vue
@@ -118,7 +118,7 @@ export default {
data() {
return {
userInput: "",
- contactMethod: saveProgressPopupContactMethods.PHONE,
+ contactMethod: saveProgressPopupContactMethods.EMAIL,
smsConsent: defaultSaveProgressSmsConsent(),
smsConsentCopy: getSaveProgressSmsConsentFallbackCopy(),
isProgressSaved: false,
diff --git a/src/fmg-components/save-progress-popup-question/save-progress-popup-sms-consent-question.spec.js b/src/fmg-components/save-progress-popup-question/save-progress-popup-sms-consent-question.spec.js
index 4a9fe13a1..fd6bea0ff 100644
--- a/src/fmg-components/save-progress-popup-question/save-progress-popup-sms-consent-question.spec.js
+++ b/src/fmg-components/save-progress-popup-question/save-progress-popup-sms-consent-question.spec.js
@@ -7,15 +7,14 @@ import {
import { errorMessages } from "@/constants/error-messages";
describe("save-progress-popup-sms-consent-question", () => {
- it("should render two consent checkboxes with default copy", () => {
+ it("should render the transactional consent checkbox with default copy", () => {
const wrapper = shallowMount(saveProgressPopupSmsConsentQuestion);
const fallbackCopy = getSaveProgressSmsConsentFallbackCopy();
const checkboxes = wrapper.findAllComponents({ name: "checkboxQuestion" });
- expect(checkboxes).toHaveLength(2);
+ expect(checkboxes).toHaveLength(1);
expect(checkboxes.at(0).props("labelText")).toBe(fallbackCopy.transactional);
- expect(checkboxes.at(1).props("labelText")).toBe(fallbackCopy.marketing);
});
it("should emit updated consent when a checkbox changes", async () => {
@@ -47,8 +46,8 @@ describe("save-progress-popup-sms-consent-question", () => {
const checkboxes = wrapper.findAllComponents({ name: "checkboxQuestion" });
+ expect(checkboxes).toHaveLength(1);
expect(checkboxes.at(0).props("labelText")).toBe("API transactional copy");
- expect(checkboxes.at(1).props("labelText")).toBe("API marketing copy");
});
it("should disable consent checkboxes when isDisabled is true", () => {
@@ -61,7 +60,6 @@ describe("save-progress-popup-sms-consent-question", () => {
const checkboxes = wrapper.findAllComponents({ name: "checkboxQuestion" });
expect(checkboxes.at(0).props("isDisabled")).toBe(true);
- expect(checkboxes.at(1).props("isDisabled")).toBe(true);
expect(wrapper.find("fieldset").attributes("disabled")).toBe("");
});
@@ -76,7 +74,6 @@ describe("save-progress-popup-sms-consent-question", () => {
const checkboxes = wrapper.findAllComponents({ name: "checkboxQuestion" });
expect(checkboxes.at(0).props("hasError")).toBe(false);
- expect(checkboxes.at(1).props("hasError")).toBe(false);
expect(wrapper.text()).not.toContain(errorMessages.SMS_CONSENT_REQUIRED);
});
@@ -94,7 +91,6 @@ describe("save-progress-popup-sms-consent-question", () => {
const checkboxes = wrapper.findAllComponents({ name: "checkboxQuestion" });
expect(checkboxes.at(0).props("hasError")).toBe(true);
- expect(checkboxes.at(1).props("hasError")).toBe(true);
});
it("should clear consent errors when a selection is made while showConsentErrors is true", async () => {
@@ -111,6 +107,5 @@ describe("save-progress-popup-sms-consent-question", () => {
const checkboxes = wrapper.findAllComponents({ name: "checkboxQuestion" });
expect(checkboxes.at(0).props("hasError")).toBe(false);
- expect(checkboxes.at(1).props("hasError")).toBe(false);
});
});
diff --git a/src/fmg-components/save-progress-popup-question/save-progress-popup-sms-consent-question.vue b/src/fmg-components/save-progress-popup-question/save-progress-popup-sms-consent-question.vue
index 55e18e623..683541895 100644
--- a/src/fmg-components/save-progress-popup-question/save-progress-popup-sms-consent-question.vue
+++ b/src/fmg-components/save-progress-popup-question/save-progress-popup-sms-consent-question.vue
@@ -9,12 +9,13 @@
:labelText="consentCopy.transactional"
:hasError="showConsentError"
:isDisabled="isDisabled" />
-
+ :isDisabled="isDisabled" /> -->