Merge branch 'release/2025.02.13' into feature/CASH-240
This commit is contained in:
commit
2b037c469b
7 changed files with 141 additions and 21 deletions
|
|
@ -16,6 +16,13 @@
|
||||||
v-model="parentAccountNumber"
|
v-model="parentAccountNumber"
|
||||||
:originalList="originalList" />
|
:originalList="originalList" />
|
||||||
</div>
|
</div>
|
||||||
|
<textLink
|
||||||
|
id="payOnMyOwnBackButton"
|
||||||
|
useLoadingModal
|
||||||
|
linkType="text"
|
||||||
|
:text="payWithCashButtonCopy"
|
||||||
|
href="javascript:void(0)"
|
||||||
|
@click-event="backButtonAction(foreceCashSelectionOnQuote = true)" />
|
||||||
|
|
||||||
<navbar
|
<navbar
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
|
|
@ -36,6 +43,7 @@ import navbar from "@/fmg-components/nav-bar/nav-bar";
|
||||||
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
|
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
|
||||||
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
|
||||||
import insuranceCompanyQuestion from "@/layouts/insurance-company/insurance-company-question";
|
import insuranceCompanyQuestion from "@/layouts/insurance-company/insurance-company-question";
|
||||||
|
import textLink from "../../ux-components/text-link/text-link.vue";
|
||||||
|
|
||||||
// Supporting files
|
// Supporting files
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
|
|
@ -92,6 +100,9 @@ export default {
|
||||||
isParentAccountNumber() {
|
isParentAccountNumber() {
|
||||||
return !!this.parentAccountNumber;
|
return !!this.parentAccountNumber;
|
||||||
},
|
},
|
||||||
|
payWithCashButtonCopy() {
|
||||||
|
return this.getCmsContent("PayWithCashWidget", "Text");
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
isCashParentAccountNumber() {
|
isCashParentAccountNumber() {
|
||||||
|
|
@ -109,8 +120,13 @@ export default {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
return store.getters.order.payment.isInsurance === true;
|
return store.getters.order.payment.isInsurance === true;
|
||||||
},
|
},
|
||||||
backButtonAction() {
|
backButtonAction(forceCashSelectionOnQuote) {
|
||||||
// Go back to Quote page
|
// Go back to Quote page
|
||||||
|
if (forceCashSelectionOnQuote) {
|
||||||
|
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route, {"isCashSelected": true});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
|
this.$router.navigateWithoutSaving(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||||
},
|
},
|
||||||
async forwardButtonAction() {
|
async forwardButtonAction() {
|
||||||
|
|
@ -157,6 +173,7 @@ export default {
|
||||||
Form,
|
Form,
|
||||||
loadingModal,
|
loadingModal,
|
||||||
insuranceCompanyQuestion,
|
insuranceCompanyQuestion,
|
||||||
|
textLink,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -577,6 +577,56 @@ describe("quote.vue", () => {
|
||||||
//Assert
|
//Assert
|
||||||
expect(wrapper.vm.isInsuranceSelected).toBe(false);
|
expect(wrapper.vm.isInsuranceSelected).toBe(false);
|
||||||
});
|
});
|
||||||
|
test("should default to cash if isCashSelected is true, despite all other checks pointing to insurance selection", async () => {
|
||||||
|
// Also needs no query parameter or previous selection in store to be present
|
||||||
|
//Arrange
|
||||||
|
|
||||||
|
store.getters = {
|
||||||
|
lineItems: {
|
||||||
|
glassParts: ["item", "item2"],
|
||||||
|
},
|
||||||
|
applicationUser: {
|
||||||
|
experiments: [],
|
||||||
|
},
|
||||||
|
order: {
|
||||||
|
lineItems: {
|
||||||
|
glassParts: ["item", "item2"],
|
||||||
|
},
|
||||||
|
payment: {
|
||||||
|
isInsurance: true,
|
||||||
|
},
|
||||||
|
customer: {
|
||||||
|
emailAddress: "test@test.com",
|
||||||
|
},
|
||||||
|
serviceLocation: {
|
||||||
|
state: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
vehicle: {
|
||||||
|
cardId: "123",
|
||||||
|
},
|
||||||
|
experimentSettings: {
|
||||||
|
settingName: "SERVICE_PACKAGE_DISCOUNT",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
mockTierOnePrice = 505;
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
|
||||||
|
//Act
|
||||||
|
await quote.beforeRouteEnter.call(
|
||||||
|
wrapper.vm,
|
||||||
|
{ query: { fmgPage: "quote",
|
||||||
|
isInsuranceSelected: true,
|
||||||
|
isCashSelected: true,
|
||||||
|
} },
|
||||||
|
undefined,
|
||||||
|
(c) => c(wrapper.vm)
|
||||||
|
);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(wrapper.vm.isInsuranceSelected).toBe(false);
|
||||||
|
});
|
||||||
test("should default to insurance if total economy package price is over $500", async () => {
|
test("should default to insurance if total economy package price is over $500", async () => {
|
||||||
// Also needs no query parameter or previous selection in store to be present
|
// Also needs no query parameter or previous selection in store to be present
|
||||||
//Arrange
|
//Arrange
|
||||||
|
|
|
||||||
|
|
@ -328,6 +328,10 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
const getIsInsuranceSelectedValue = (availableLineItems, insuranceThreshold) => {
|
const getIsInsuranceSelectedValue = (availableLineItems, insuranceThreshold) => {
|
||||||
|
// query param checked here is set on insurance-selection "Pay on my own" link click
|
||||||
|
if (to.query?.isCashSelected || to.query?.iscashselected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
const serviceLocationState = store.getters.order.serviceLocation.state;
|
const serviceLocationState = store.getters.order.serviceLocation.state;
|
||||||
|
|
||||||
// usually true when returning from heritage but can be false when returning from heritage on a save quote
|
// usually true when returning from heritage but can be false when returning from heritage on a save quote
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ describe("mobile-location-modal-questions.vue", () => {
|
||||||
expect(wrapper.vm.$refs.MobileLocationModalWidget.openModal).toHaveBeenCalled();
|
expect(wrapper.vm.$refs.MobileLocationModalWidget.openModal).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should emit update:modelValue on setMobileLocation for a valid address and vehicle protection answer", async () => {
|
it("Should emit updated-mobile-location-questions on setMobileLocation for a valid address and vehicle protection answer", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const mobileLocationQuestions = {
|
const mobileLocationQuestions = {
|
||||||
addressQuestions: {
|
addressQuestions: {
|
||||||
|
|
@ -240,17 +240,26 @@ describe("mobile-location-modal-questions.vue", () => {
|
||||||
});
|
});
|
||||||
wrapper.vm.$refs.MobileLocationModalWidget.closeModal = jest.fn();
|
wrapper.vm.$refs.MobileLocationModalWidget.closeModal = jest.fn();
|
||||||
|
|
||||||
|
// Mock the emitEvent function
|
||||||
|
wrapper.vm.emitEvent = jest.fn(async (eventName, payload) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
wrapper.vm.$emit(eventName, newMobileLocationQuestions, resolve);
|
||||||
|
resolve(newMobileLocationQuestions);
|
||||||
|
});
|
||||||
|
});
|
||||||
// Act
|
// Act
|
||||||
wrapper.vm.internalModel = newMobileLocationQuestions;
|
wrapper.vm.internalModel = newMobileLocationQuestions;
|
||||||
|
|
||||||
await wrapper.vm.setMobileLocation();
|
await wrapper.vm.setMobileLocation();
|
||||||
|
|
||||||
let expectedEmit = [[newMobileLocationQuestions]];
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.emitted("update:modelValue")).toEqual(expectedEmit);
|
expect(wrapper.emitted()["updated-mobile-location-questions"]).toBeTruthy();
|
||||||
|
expect(wrapper.emitted()["updated-mobile-location-questions"][0][0]).toBe(
|
||||||
|
newMobileLocationQuestions
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should not emit update:modelValue on setMobileLocation for an invalid address zip code", async () => {
|
it("Should not emit updated-mobile-location-questions on setMobileLocation for an invalid address zip code", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const mobileLocationQuestions = {
|
const mobileLocationQuestions = {
|
||||||
addressQuestions: {
|
addressQuestions: {
|
||||||
|
|
@ -293,7 +302,7 @@ describe("mobile-location-modal-questions.vue", () => {
|
||||||
await wrapper.vm.setMobileLocation();
|
await wrapper.vm.setMobileLocation();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.emitted("update:modelValue")).not.toBeTruthy();
|
expect(wrapper.emitted("updated-mobile-location-questions")).not.toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should display invalid zip alert for invalid address zip inputs", async () => {
|
it("Should display invalid zip alert for invalid address zip inputs", async () => {
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,7 @@ export default {
|
||||||
"updated-contains-military-base",
|
"updated-contains-military-base",
|
||||||
"updated-bill-to-account-number",
|
"updated-bill-to-account-number",
|
||||||
"mobileLocationSelected",
|
"mobileLocationSelected",
|
||||||
|
"updated-mobile-location-questions",
|
||||||
],
|
],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -251,10 +252,15 @@ export default {
|
||||||
if (!zipCodeData.isValid) {
|
if (!zipCodeData.isValid) {
|
||||||
this.displayInvalidZipAlert = true;
|
this.displayInvalidZipAlert = true;
|
||||||
this.resetModalButtonStyle();
|
this.resetModalButtonStyle();
|
||||||
|
return;
|
||||||
} else if (zipCodeData.state != this.internalModel.addressQuestions.state) {
|
} else if (zipCodeData.state != this.internalModel.addressQuestions.state) {
|
||||||
this.displayMismatchStateAndZipAlert = true;
|
this.displayMismatchStateAndZipAlert = true;
|
||||||
this.resetModalButtonStyle();
|
this.resetModalButtonStyle();
|
||||||
} else {
|
return;
|
||||||
|
} else if (
|
||||||
|
this.internalModel.addressQuestions.zipCode !==
|
||||||
|
this.modelValue.addressQuestions.zipCode
|
||||||
|
) {
|
||||||
// retrieve mobile fee part
|
// retrieve mobile fee part
|
||||||
const serviceZipCode = this.internalModel.addressQuestions.zipCode;
|
const serviceZipCode = this.internalModel.addressQuestions.zipCode;
|
||||||
const mobileFeePart = await getPricedMobileFeePart(
|
const mobileFeePart = await getPricedMobileFeePart(
|
||||||
|
|
@ -279,13 +285,17 @@ export default {
|
||||||
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
|
this.$emit("updated-contains-military-base", zipCodeData.containsMilitaryBase);
|
||||||
this.$emit("updated-mobile-ctu", zipCodeData.zipCodeCtu);
|
this.$emit("updated-mobile-ctu", zipCodeData.zipCodeCtu);
|
||||||
this.$emit("updated-bill-to-account-number", billToAccountNumber);
|
this.$emit("updated-bill-to-account-number", billToAccountNumber);
|
||||||
|
|
||||||
// update the page level model
|
|
||||||
this.$emit("update:modelValue", this.internalModel);
|
|
||||||
|
|
||||||
//Page advance to Schedule page
|
|
||||||
this.$emit("mobileLocationSelected");
|
|
||||||
}
|
}
|
||||||
|
// update the page level model
|
||||||
|
await this.emitEvent("updated-mobile-location-questions", this.internalModel);
|
||||||
|
|
||||||
|
this.$emit("mobileLocationSelected");
|
||||||
|
},
|
||||||
|
//creating emitEvent function to raise events with resolve callback to handle events sequencing
|
||||||
|
async emitEvent(eventName, payload) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
this.$emit(eventName, payload, resolve);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
|
||||||
|
|
@ -463,11 +463,23 @@ describe("service-location.vue", () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
mobileLocationQuestionsComponent.vm.$emit(
|
|
||||||
"update:modelValue",
|
// Mock the emitEvent function
|
||||||
|
mobileLocationQuestionsComponent.vm.emitEvent = jest.fn(async (eventName, payload) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
mobileLocationQuestionsComponent.vm.$emit(eventName, payload, resolve);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trigger the event
|
||||||
|
await mobileLocationQuestionsComponent.vm.emitEvent(
|
||||||
|
"updated-mobile-location-questions",
|
||||||
newMobileLocationQuestions
|
newMobileLocationQuestions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await wrapper.vm.$nextTick(); // Wait for DOM updates
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0)); // Wait for promise to resolve
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.vm.mobileLocationQuestions).toStrictEqual(newMobileLocationQuestions);
|
expect(wrapper.vm.mobileLocationQuestions).toStrictEqual(newMobileLocationQuestions);
|
||||||
});
|
});
|
||||||
|
|
@ -532,11 +544,23 @@ describe("service-location.vue", () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
mobileLocationQuestionsComponent.vm.$emit(
|
|
||||||
"update:modelValue",
|
// Mock the emitEvent function
|
||||||
|
mobileLocationQuestionsComponent.vm.emitEvent = jest.fn(async (eventName, payload) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
mobileLocationQuestionsComponent.vm.$emit(eventName, payload, resolve);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Trigger the event
|
||||||
|
await mobileLocationQuestionsComponent.vm.emitEvent(
|
||||||
|
"updated-mobile-location-questions",
|
||||||
newMobileLocationQuestions
|
newMobileLocationQuestions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await wrapper.vm.$nextTick(); // Wait for DOM updates
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 0)); // Wait for promise to resolve
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.vm.serviceZipCodeQuestion).toStrictEqual(newServiceZipCodeInfo);
|
expect(wrapper.vm.serviceZipCodeQuestion).toStrictEqual(newServiceZipCodeInfo);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,7 @@
|
||||||
@updated-serviceability="setServiceabilityDetails"
|
@updated-serviceability="setServiceabilityDetails"
|
||||||
@updated-contains-military-base="setContainsMilitaryBase"
|
@updated-contains-military-base="setContainsMilitaryBase"
|
||||||
@updated-mobile-ctu="setCtuForMobile"
|
@updated-mobile-ctu="setCtuForMobile"
|
||||||
|
@updated-mobile-location-questions="setMobileLocationQuestions"
|
||||||
validationRules="mobile-location-required"
|
validationRules="mobile-location-required"
|
||||||
ref="mobileLocationQuestions"
|
ref="mobileLocationQuestions"
|
||||||
linkWidgetName="MobileLocationLinkWidget"
|
linkWidgetName="MobileLocationLinkWidget"
|
||||||
|
|
@ -296,17 +297,18 @@ export default {
|
||||||
isVehicleProtected: this.isVehicleProtected,
|
isVehicleProtected: this.isVehicleProtected,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
set: function (newValue) {
|
async set(newValue) {
|
||||||
if (newValue.addressQuestions.zipCode !== this.zipCode) {
|
if (newValue.addressQuestions.zipCode !== this.zipCode) {
|
||||||
getShopProviderData(newValue.addressQuestions.zipCode).then((result) => {
|
await getShopProviderData(newValue.addressQuestions.zipCode).then((result) => {
|
||||||
this.shopProviderData = result.data;
|
this.shopProviderData = result.data;
|
||||||
this.selectedProvider = new Provider(
|
this.selectedProvider = new Provider(
|
||||||
this.shopProviderData.mobileProviderNumber
|
this.shopProviderData.mobileProviderNumber
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setMobileLocation(newValue);
|
this.setMobileLocation(newValue);
|
||||||
|
//handle promise
|
||||||
|
newValue.resolve?.();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
isServiceableMobile() {
|
isServiceableMobile() {
|
||||||
|
|
@ -485,6 +487,10 @@ export default {
|
||||||
setMobileFeePart(mobileFeePart) {
|
setMobileFeePart(mobileFeePart) {
|
||||||
this.mobileFeePart = mobileFeePart;
|
this.mobileFeePart = mobileFeePart;
|
||||||
},
|
},
|
||||||
|
//creating async function as the computed property can not directly handle asynchronous operations or promises.
|
||||||
|
async setMobileLocationQuestions(newValue, resolve) {
|
||||||
|
this.mobileLocationQuestions = { ...newValue, resolve };
|
||||||
|
},
|
||||||
getServiceAddressFromStore() {
|
getServiceAddressFromStore() {
|
||||||
return store.getters.order.serviceLocation.address;
|
return store.getters.order.serviceLocation.address;
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue