CSR-1764
This commit is contained in:
parent
ca9c821b31
commit
e88da1bd77
6 changed files with 199 additions and 34 deletions
|
|
@ -1 +1,66 @@
|
||||||
test.todo("some test to be written in the future");
|
import { mount, shallowMount } from "@vue/test-utils";
|
||||||
|
import menuModal from "@/fmg-components/funnel-header/menu-modal/menu-modal.vue";
|
||||||
|
|
||||||
|
describe("menu-modal.vue", () => {
|
||||||
|
it("Should return text Footer Navigation", async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(menuModal);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const footerModalLabel = wrapper.find("h5");
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
expect(footerModalLabel.text()).toContain("Footer Navigation");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should return footer text as Safelite Group", async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(menuModal);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
const modalFooter = wrapper.find("div.modal-footer");
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
expect(modalFooter.text()).toContain("Safelite Group");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should return Terms of use text link text", async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(menuModal);
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
expect(wrapper.html()).toContain("Terms of use");
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should return "Your privacy choices" text link text', async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = mount(menuModal);
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
expect(wrapper.html()).toContain("Your privacy choices");
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Icon for link to "Privacy Policies" page is displayed with the correct alternate text', () => {
|
||||||
|
// NOTE: We need a way to target a specific <textLink> component so we can be sure that
|
||||||
|
// an icon is coming from a specific <textLink> component.
|
||||||
|
// That requires an additional prop in the <textLink> component. Will hold off on adding the prop
|
||||||
|
// until there is more clarity on how we handle shared components.
|
||||||
|
// For now the test is below is the best we can do.
|
||||||
|
|
||||||
|
// Arrange
|
||||||
|
const wrapper = mount(menuModal);
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
const icon = wrapper.find('[data-id="ccpa-icon"]');
|
||||||
|
expect(icon.isVisible()).toBe(true);
|
||||||
|
expect(icon.attributes("alt")).toBe("Your privacy choices");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should return Warranty text link text", async () => {
|
||||||
|
// Act
|
||||||
|
const wrapper = shallowMount(menuModal);
|
||||||
|
|
||||||
|
// Expect
|
||||||
|
expect(wrapper.html()).toContain("Warranty");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@
|
||||||
<template v-slot:after-text>
|
<template v-slot:after-text>
|
||||||
<img
|
<img
|
||||||
class="ccpa-icon"
|
class="ccpa-icon"
|
||||||
|
data-id="ccpa-icon"
|
||||||
src="~@/assets/img/icons/ccpa-icon.svg"
|
src="~@/assets/img/icons/ccpa-icon.svg"
|
||||||
alt="Your privacy choices" />
|
alt="Your privacy choices" />
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
class="small mt-4"
|
class="small mt-4"
|
||||||
v-model="lineItems"
|
v-model="lineItems"
|
||||||
:availableVaps="availableVaps"
|
:availableVaps="availableVaps"
|
||||||
|
pageName="payment-method"
|
||||||
modalWidgetName="PromoModalWidget" />
|
modalWidgetName="PromoModalWidget" />
|
||||||
|
|
||||||
<hr class="my-5" />
|
<hr class="my-5" />
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ export default {
|
||||||
modelValue: Object,
|
modelValue: Object,
|
||||||
modalWidgetName: String,
|
modalWidgetName: String,
|
||||||
availableVaps: Object,
|
availableVaps: Object,
|
||||||
|
pageName: String,
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
promoLinkText() {
|
promoLinkText() {
|
||||||
|
|
@ -253,44 +254,51 @@ 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.pageName == "payment-method") {
|
||||||
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,
|
||||||
const getVaps = getVapsThatNeedToBeAddedToSatisfyPromos(
|
this.availableVaps
|
||||||
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?.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);
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,14 @@
|
||||||
cmsWidgetName="AfterpayModalWidget"
|
cmsWidgetName="AfterpayModalWidget"
|
||||||
:lineItems="availableLineItems" />
|
:lineItems="availableLineItems" />
|
||||||
|
|
||||||
|
<promoModalQuestion
|
||||||
|
class="small mt-4"
|
||||||
|
v-model="lineItems"
|
||||||
|
:availableVaps="availableVaps"
|
||||||
|
:availableLineItems="availableLineItems"
|
||||||
|
pageName="quote"
|
||||||
|
modalWidgetName="PromoModalWidget" />
|
||||||
|
|
||||||
<textBlock
|
<textBlock
|
||||||
cmsWidgetName="quoteDisclaimer"
|
cmsWidgetName="quoteDisclaimer"
|
||||||
justifyText="left"
|
justifyText="left"
|
||||||
|
|
@ -89,9 +97,13 @@ import { payWithInsuranceStates } from "@/constants/pay-with-insurance-states";
|
||||||
import {
|
import {
|
||||||
revalidatePromosAndValidateQueryStringPromo,
|
revalidatePromosAndValidateQueryStringPromo,
|
||||||
buildToastMessagesFromRevalidateOrValidatePromoResponse,
|
buildToastMessagesFromRevalidateOrValidatePromoResponse,
|
||||||
|
createPromoSuccessAlert,
|
||||||
|
createPromoErrorAlert,
|
||||||
|
getNewlyInactivatedPromos,
|
||||||
} 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));
|
||||||
|
|
||||||
|
|
@ -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,6 +188,10 @@ export default {
|
||||||
pricingResults,
|
pricingResults,
|
||||||
"quote"
|
"quote"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const lineItems = store.getters.order.lineItems;
|
||||||
|
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.
|
||||||
|
|
@ -183,6 +200,8 @@ export default {
|
||||||
vm.pricedGlassParts = nullSafeGlassParts;
|
vm.pricedGlassParts = nullSafeGlassParts;
|
||||||
vm.supportingItems = resultMap.supportingItems;
|
vm.supportingItems = resultMap.supportingItems;
|
||||||
vm.availableLineItems = pricingResults;
|
vm.availableLineItems = pricingResults;
|
||||||
|
vm.availableVaps = availableVaps;
|
||||||
|
vm.lineItems = lineItems;
|
||||||
vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(vm.availableLineItems);
|
vm.isInsuranceSelected = vm.getDefaultIsInsuranceSelectedValue(vm.availableLineItems);
|
||||||
|
|
||||||
if (revalidatePromoResponse) {
|
if (revalidatePromoResponse) {
|
||||||
|
|
@ -212,11 +231,16 @@ export default {
|
||||||
availableLineItems: null,
|
availableLineItems: null,
|
||||||
supportingItems: null,
|
supportingItems: null,
|
||||||
pricedGlassParts: null,
|
pricedGlassParts: null,
|
||||||
|
lineItems: [],
|
||||||
|
availableVaps: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
allActivePromos() {
|
allActivePromos() {
|
||||||
return this.$store.getters.order.lineItems.promos ?? [];
|
return this.lineItems.promos ?? [];
|
||||||
|
},
|
||||||
|
lineItemsCloneForWatcher() {
|
||||||
|
return Object.assign({}, this.lineItems);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -260,6 +284,29 @@ export default {
|
||||||
vapsItemsSelectedAction(vapsItemsSelected) {
|
vapsItemsSelectedAction(vapsItemsSelected) {
|
||||||
this.selectedVaps = vapsItemsSelected;
|
this.selectedVaps = vapsItemsSelected;
|
||||||
},
|
},
|
||||||
|
async revalidatePromos() {
|
||||||
|
this.$refs.loadingModal.showModal();
|
||||||
|
const revalidatePromoResponse = await baseMixin.methods.dispatchStoreActionWithLogging(
|
||||||
|
storeActions.REVALIDATE_ORDER_PROMOS_AND_SAVE_SERVER_DATA,
|
||||||
|
{
|
||||||
|
activePromosToUse: this.lineItems.promos,
|
||||||
|
inactivePromosToUse: this.$store.getters.order.payment.inactivePromos,
|
||||||
|
lineItemsToUse: this.lineItems,
|
||||||
|
},
|
||||||
|
"quote",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
// Handle error alerts here, success alerts are handled in the watcher
|
||||||
|
if (revalidatePromoResponse.errors.length) {
|
||||||
|
getNewlyInactivatedPromos(
|
||||||
|
this.inactivePromos,
|
||||||
|
revalidatePromoResponse.errors
|
||||||
|
).forEach((promoCode) => {
|
||||||
|
const errorAlert = createPromoErrorAlert(promoCode);
|
||||||
|
this.$refs.funnelHeader.pushGlobalAlert(errorAlert, errorAlert.shouldAutoFade);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
backButtonAction() {
|
backButtonAction() {
|
||||||
vehicleQuestionsMixin.methods.navigateBack(this);
|
vehicleQuestionsMixin.methods.navigateBack(this);
|
||||||
},
|
},
|
||||||
|
|
@ -294,6 +341,14 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dispatchStoreAction(this.storeActions.SAVE_VAPS, this.selectedVaps, false);
|
this.dispatchStoreAction(this.storeActions.SAVE_VAPS, this.selectedVaps, 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) {
|
||||||
|
|
@ -309,6 +364,37 @@ 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);
|
||||||
|
} else if (
|
||||||
|
oldValue.promos.length == newValue.promos.length &&
|
||||||
|
oldValue.vaps.length != newValue.vaps.length
|
||||||
|
) {
|
||||||
|
this.revalidatePromos();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
components: {
|
components: {
|
||||||
funnelHeader,
|
funnelHeader,
|
||||||
navbar,
|
navbar,
|
||||||
|
|
@ -321,6 +407,7 @@ export default {
|
||||||
contentGroupModal,
|
contentGroupModal,
|
||||||
loadingModal,
|
loadingModal,
|
||||||
afterpayModalBanner,
|
afterpayModalBanner,
|
||||||
|
promoModalQuestion,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue