Merge branch 'develop' into SSR-1160-add-desktop-functionality

This commit is contained in:
Bryan Mauger 2024-04-22 10:34:47 -04:00
commit 56ad7bcd3d
24 changed files with 1185 additions and 705 deletions

View file

@ -0,0 +1,8 @@
const coverageStatementPageVariations = Object.freeze({
DEDUCTIBLE: 0,
ITAC: 1,
NO_COMP: 2,
UNVERIFIED: 3
});
export default coverageStatementPageVariations;

View file

@ -76,10 +76,6 @@ const endpoints = Object.freeze({
url: `${PRICE_BASE_URL}/order-items-with-insurance-pricing`, url: `${PRICE_BASE_URL}/order-items-with-insurance-pricing`,
method: 'GET' method: 'GET'
}, },
GetPriceOrderItems: {
url: `${PRICE_BASE_URL}/order-items`,
method: 'GET'
},
GetProviders: { GetProviders: {
url: `${LOCATION_BASE_URL}/providers`, url: `${LOCATION_BASE_URL}/providers`,
method: 'GET' method: 'GET'

View file

@ -16,14 +16,18 @@ export async function getPricedMobileFeePart(serviceZipCode) {
if (!serviceZipCode) { if (!serviceZipCode) {
return Promise.resolve(null); return Promise.resolve(null);
} }
const zipCodeData = await getZipCodeData(serviceZipCode);
// Get the Mobile Fee Part // Get the Mobile Fee Part
const mobileFeePart = await useMainStore().getMobileFeePart(); const mobileFeePart = await useMainStore().getMobileFeePart();
if (mobileFeePart.data == null || mobileFeePart.data === '') {
return Promise.resolve(null);
}
// Get the Mobile Fee Part Price // Get the Mobile Fee Part Price
const pricingResults = await useMainStore() const pricingResults = await useMainStore()
.priceOrderItemsAndSaveServerData([mobileFeePart.data], serviceZipCode, zipCodeData.zipCodeCtu); .getPriceOrderItems([mobileFeePart.data]);
return Promise.resolve(pricingResults[0]); return Promise.resolve(pricingResults[0]);
} }

View file

@ -2,6 +2,7 @@
exports[`cart-dropdown component initial data rendered as expected 1`] = ` exports[`cart-dropdown component initial data rendered as expected 1`] = `
Object { Object {
"availableVaps": Array [],
"cartItemType": Object { "cartItemType": Object {
"MOBILE_FEE": "MOBILE FEE", "MOBILE_FEE": "MOBILE FEE",
"RECYCLE_FEE": "RECYCLE FEE", "RECYCLE_FEE": "RECYCLE FEE",
@ -10,6 +11,7 @@ Object {
"isExpanded": false, "isExpanded": false,
"widget": Object { "widget": Object {
"amountDue": "AmountDueTextWidget", "amountDue": "AmountDueTextWidget",
"amountPaid": "AmountPaidTextWidget",
"basePrice": "BasePriceWidget", "basePrice": "BasePriceWidget",
"deductible": "DeductibleWidget", "deductible": "DeductibleWidget",
"mobileFee": "MobileServiceWidget", "mobileFee": "MobileServiceWidget",

View file

@ -57,7 +57,11 @@ beforeEach(() => {
describe('cart-dropdown component', () => { describe('cart-dropdown component', () => {
test('initial data rendered as expected', () => { test('initial data rendered as expected', () => {
// Arrange // Arrange
const { wrapper } = getMountedComponent({}); const { wrapper } = getMountedComponent({
order: {
availableVaps: []
}
});
// Assert // Assert
expect(wrapper.vm.$data).toMatchSnapshot(); expect(wrapper.vm.$data).toMatchSnapshot();
@ -74,6 +78,30 @@ describe('cart-dropdown component', () => {
// Assert // Assert
expect(head.exists()).toBeTruthy(); expect(head.exists()).toBeTruthy();
}); });
test('cart dropdown head is visible', () => {
// Arrange
const reference = '#cart-dropdown-head';
const { wrapper } = getMountedComponent(cartDropdown, {}, { showDropdownHeader: true });
// Act
const head = wrapper.find(reference);
// Assert
expect(head.exists()).toBeTruthy();
expect(head.classes()).toContain('cart-toggle');
});
test('cart dropdown head is not visible', () => {
// Arrange
const reference = '#cart-dropdown-head';
const { wrapper } = getMountedComponent(cartDropdown, {}, { showDropdownHeader: false });
// Act
const head = wrapper.find(reference);
// Assert
expect(head.exists()).toBeTruthy();
expect(head.classes()).not.toContain('cart-toggle');
});
test('cart table', () => { test('cart table', () => {
// Arrange // Arrange
const reference = '#cart-table'; const reference = '#cart-table';
@ -225,6 +253,27 @@ describe('cart-dropdown component', () => {
// Assert // Assert
expect(salesTax.exists()).toBeTruthy(); expect(salesTax.exists()).toBeTruthy();
}); });
test('amount paid when Pay in Advance', () => {
// Arrange
const reference = '#cart-amount-paid';
const isExpanded = true;
const showAsPaid = true;
const initialPropsData = { isExpanded, showAsPaid };
const storeData = {
order: {
payment: {
isPayInAdvance: true
}
}
};
const { wrapper } = getMountedComponent(storeData, {}, initialPropsData);
// Act
const amountPaid = wrapper.find(reference);
// Assert
expect(amountPaid.exists()).toBeTruthy();
});
test('bottom amount due', () => { test('bottom amount due', () => {
// Arrange // Arrange
const reference = '#bottom-amount-due'; const reference = '#bottom-amount-due';
@ -313,115 +362,6 @@ describe('cart-dropdown component', () => {
}); });
}); });
describe('computed', () => { describe('computed', () => {
describe('isUnverified', () => {
test('returns false when no comp', () => {
// Arrange
const storeData = {
order: {
policy: {
isITAC: false,
noCoverage: true
},
currentDeductible: null
}
};
const { wrapper } = getMountedComponent(storeData);
// Act
const result = wrapper.vm.isUnverified;
// Assert
expect(result).toBeFalsy();
});
test('returns false when itac', () => {
// Arrange
const storeData = {
order: {
payment: {
insuranceCoverage: {
coverageStatus: coverageStatuses.PENDING
}
},
policy: {
isITAC: true
},
currentDeductible: null
}
};
const { wrapper } = getMountedComponent(storeData);
// Act
const result = wrapper.vm.isUnverified;
// Assert
expect(result).toBeFalsy();
});
test('returns false when deductible not null and verified coverage status', () => {
// Arrange
const storeData = {
order: {
payment: {
insuranceCoverage: {
coverageStatus: coverageStatuses.VERIFIED
}
},
policy: {
isITAC: false
},
currentDeductible: 23
}
};
const { wrapper } = getMountedComponent(storeData);
// Act
const result = wrapper.vm.isUnverified;
// Assert
expect(result).toBeFalsy();
});
test('returns true when not no comp, not itac, and deductible is null', () => {
// Arrange
const storeData = {
order: {
policy: {
isITAC: false,
noCoverage: false
},
currentDeductible: null
}
};
const { wrapper } = getMountedComponent(storeData);
// Act
const result = wrapper.vm.isUnverified;
// Assert
expect(result).toBeTruthy();
});
test('returns true when not no comp, not itac, and coverage status is not verified', () => {
// Arrange
const storeData = {
order: {
payment: {
insuranceCoverage: {
coverageStatus: coverageStatuses.PENDING
}
},
policy: {
isITAC: false
},
currentDeductible: 12
}
};
const { wrapper } = getMountedComponent(storeData);
// Act
const result = wrapper.vm.isUnverified;
// Assert
expect(result).toBeTruthy();
});
});
describe('amountDue', () => { describe('amountDue', () => {
test('returns 0 when showAsPaid is true', () => { test('returns 0 when showAsPaid is true', () => {
// Arrange // Arrange
@ -556,15 +496,24 @@ describe('cart-dropdown component', () => {
// Arrange // Arrange
const storeData = { const storeData = {
order: { order: {
policy: {
policyLookupSuccessful: true
},
currentDeductible: 250,
lineItems: { lineItems: {
glassParts: null, glassParts: null,
otherParts: null, otherParts: null,
supportingItems: null, supportingItems: null,
vaps: null vaps: null
},
payment: {
insuranceCoverage: { isVerified: true }
} }
},
issConfig: {
isClaimRegistrationRequired: true
} }
}; };
const { wrapper } = getMountedComponent(storeData); const { wrapper } = getMountedComponent(storeData);
// Act // Act
@ -578,6 +527,10 @@ describe('cart-dropdown component', () => {
// Arrange // Arrange
const storeData = { const storeData = {
order: { order: {
policy: {
policyLookupSuccessful: true
},
currentDeductible: 250,
lineItems: { lineItems: {
glassParts: [ glassParts: [
{ partType: 'mock', salesTax: null }, { partType: 'mock', salesTax: null },
@ -591,7 +544,13 @@ describe('cart-dropdown component', () => {
{ partType: 'mock', salesTax: null }, { partType: 'mock', salesTax: null },
{ partType: 'mock', salesTax: undefined } { partType: 'mock', salesTax: undefined }
] ]
},
payment: {
insuranceCoverage: { isVerified: true }
} }
},
issConfig: {
isClaimRegistrationRequired: true
} }
}; };
const { wrapper } = getMountedComponent(storeData); const { wrapper } = getMountedComponent(storeData);
@ -606,6 +565,10 @@ describe('cart-dropdown component', () => {
// Arrange // Arrange
const storeData = { const storeData = {
order: { order: {
policy: {
policyLookupSuccessful: true
},
currentDeductible: 250,
lineItems: { lineItems: {
glassParts: [{ partType: 'mock', salesTax: 10 }], glassParts: [{ partType: 'mock', salesTax: 10 }],
supportingItems: [ supportingItems: [
@ -619,10 +582,11 @@ describe('cart-dropdown component', () => {
vaps: [] vaps: []
}, },
payment: { payment: {
insuranceCoverage: { insuranceCoverage: { isVerified: false }
isVerified: false
}
} }
},
issConfig: {
isClaimRegistrationRequired: true
} }
}; };
const { wrapper } = getMountedComponent(storeData); const { wrapper } = getMountedComponent(storeData);
@ -636,15 +600,27 @@ describe('cart-dropdown component', () => {
test('Returns sum of vaps sales tax when coverage is unverified and order has vaps.', () => { test('Returns sum of vaps sales tax when coverage is unverified and order has vaps.', () => {
// Arrange // Arrange
const storeData = useMainStore().$state; const storeData = {
storeData.order.lineItems.glassParts = [ order: {
{ partType: 'mock', salesTax: 10 } policy: {
]; policyLookupSuccessful: true
storeData.order.lineItems.vaps = [ },
{ partType: 'mock', salesTax: 1 }, currentDeductible: 250,
{ partType: 'mock', salesTax: 2 } lineItems: {
]; glassParts: [{ partType: 'mock', salesTax: 10 }],
storeData.order.payment.insuranceCoverage.isVerified = false; vaps: [
{ partType: 'mock', salesTax: 1 },
{ partType: 'mock', salesTax: 2 }
]
},
payment: {
insuranceCoverage: { isVerified: false }
}
},
issConfig: {
isClaimRegistrationRequired: true
}
};
const { wrapper } = getMountedComponent(storeData); const { wrapper } = getMountedComponent(storeData);
@ -657,16 +633,38 @@ describe('cart-dropdown component', () => {
test('Returns Recycle Fee tax when coverage is Verified-Deductible, replace service, and no vaps.', () => { test('Returns Recycle Fee tax when coverage is Verified-Deductible, replace service, and no vaps.', () => {
// Arrange // Arrange
const storeData = useMainStore().$state; const storeData = {
storeData.order.currentDeductible = 250; order: {
storeData.order.lineItems.glassParts = [ policy: {
{ partType: 'mock', salesTax: 10, sellingPrice: 150 } policyLookupSuccessful: true
]; },
storeData.order.lineItems.supportingItems = [ currentDeductible: 250,
{ partNumber: partNumberStrings.RECYCLE_FEE, partType: 'mock', salesTax: 10, sellingPrice: 39.99 } lineItems: {
]; glassParts: [{ partType: 'mock', salesTax: 10, sellingPrice: 150 }],
storeData.order.lineItems.vaps = null; supportingItems: [
storeData.order.payment.insuranceCoverage.isVerified = true; {
partNumber: partNumberStrings.RECYCLE_FEE,
partType: 'mock',
salesTax: 10,
sellingPrice: 39.99
}
],
vaps: null
},
payment: {
insuranceCoverage: {
coverageStatus: coverageStatuses.VERIFIED
}
},
policy: {
noCoverage: false,
isITAC: false
}
},
issConfig: {
isClaimRegistrationRequired: true
}
};
const { wrapper } = getMountedComponent(storeData); const { wrapper } = getMountedComponent(storeData);
@ -679,24 +677,34 @@ describe('cart-dropdown component', () => {
test('Returns sum of vaps + recycle fee sales tax when Verified-Deductible, replace service, and has vaps.', () => { test('Returns sum of vaps + recycle fee sales tax when Verified-Deductible, replace service, and has vaps.', () => {
// Arrange // Arrange
const storeData = useMainStore().$state; const storeData = {
storeData.order.currentDeductible = 250; order: {
storeData.order.lineItems.glassParts = [ policy: {
{ partType: 'mock', salesTax: 10, sellingPrice: 100 } policyLookupSuccessful: true
]; },
storeData.order.lineItems.otherParts = [ currentDeductible: 250,
{ partType: 'mock', salesTax: 10, kitPrice: 100 } lineItems: {
]; glassParts: [{ partType: 'mock', salesTax: 10, sellingPrice: 100 }],
storeData.order.lineItems.supportingItems = [ otherParts: [{ partType: 'mock', salesTax: 10, kitPrice: 100 }],
{ partNumber: partNumberStrings.RECYCLE_FEE, partType: 'mock', salesTax: 10, sellingPrice: 39.99 }, supportingItems: [
{ partType: 'mock', salesTax: 10, kitPrice: 100 } { partNumber: partNumberStrings.RECYCLE_FEE, partType: 'mock', salesTax: 10, sellingPrice: 39.99 },
]; { partType: 'mock', salesTax: 10, kitPrice: 100 }
storeData.order.lineItems.vaps = [ ],
{ partType: 'mock', salesTax: 2 }, vaps: [
{ partType: 'mock', salesTax: 3 } { partType: 'mock', salesTax: 2 },
]; { partType: 'mock', salesTax: 3 }
storeData.order.payment.insuranceCoverage.isVerified = true; ]
},
payment: {
insuranceCoverage: {
coverageStatus: coverageStatuses.VERIFIED
}
}
},
issConfig: {
isClaimRegistrationRequired: true
}
};
const { wrapper } = getMountedComponent(storeData); const { wrapper } = getMountedComponent(storeData);
// Act // Act
@ -708,23 +716,27 @@ describe('cart-dropdown component', () => {
test('Returns sum of sales tax when Verified-ITAC.', () => { test('Returns sum of sales tax when Verified-ITAC.', () => {
// Arrange // Arrange
const storeData = useMainStore().$state; const storeData = {
storeData.order.currentDeductible = 0; order: {
storeData.order.lineItems.glassParts = [ currentDeductible: 0,
{ partType: 'mock', salesTax: 10, sellingPrice: 100 } lineItems: {
]; glassParts: [{ partType: 'mock', salesTax: 10, sellingPrice: 100 }],
storeData.order.lineItems.otherParts = [ otherParts: [{ partType: 'mock', salesTax: 10, kitPrice: 100 }],
{ partType: 'mock', salesTax: 10, kitPrice: 100 } supportingItems: [{ partType: 'mock', salesTax: 10, kitPrice: 100 }],
]; vaps: [{ partType: 'mock', salesTax: 5 }]
storeData.order.lineItems.supportingItems = [ },
{ partType: 'mock', salesTax: 10, kitPrice: 100 } payment: {
]; insuranceCoverage: { isVerified: true }
storeData.order.lineItems.vaps = [ },
{ partType: 'mock', salesTax: 5 } policy: {
]; isITAC: true,
storeData.order.payment.insuranceCoverage.isVerified = true; policyLookupSuccessful: true
storeData.order.policy.isITAC = true; }
},
issConfig: {
isClaimRegistrationRequired: true
}
};
const { wrapper } = getMountedComponent(storeData); const { wrapper } = getMountedComponent(storeData);
// Act // Act
@ -736,23 +748,28 @@ describe('cart-dropdown component', () => {
test('Returns sum of sales tax when Verified-NoComp.', () => { test('Returns sum of sales tax when Verified-NoComp.', () => {
// Arrange // Arrange
const storeData = useMainStore().$state; const storeData = {
storeData.order.currentDeductible = 0; order: {
storeData.order.lineItems.glassParts = [ currentDeductible: 0,
{ partType: 'mock', salesTax: 10, sellingPrice: 100 } lineItems: {
]; glassParts: [{ partType: 'mock', salesTax: 10, sellingPrice: 100 }],
storeData.order.lineItems.otherParts = [ otherParts: [{ partType: 'mock', salesTax: 10, kitPrice: 100 }],
{ partType: 'mock', salesTax: 10, kitPrice: 100 } supportingItems: [{ partType: 'mock', salesTax: 10, kitPrice: 100 }],
]; vaps: [{ partType: 'mock', salesTax: 5 }]
storeData.order.lineItems.supportingItems = [ },
{ partType: 'mock', salesTax: 10, kitPrice: 100 } payment: {
]; insuranceCoverage: { isVerified: true }
storeData.order.lineItems.vaps = [ },
{ partType: 'mock', salesTax: 5 } policy: {
]; noCoverage: true,
storeData.order.payment.insuranceCoverage.isVerified = true; policyLookupSuccessful: true
storeData.order.policy.noCoverage = true; }
},
issConfig: {
isClaimRegistrationRequired: true,
enableNoCompQuote: true
}
};
const { wrapper } = getMountedComponent(storeData); const { wrapper } = getMountedComponent(storeData);
// Act // Act
@ -849,8 +866,8 @@ describe('cart-dropdown component', () => {
test('returns expected when availableVaps null', async () => { test('returns expected when availableVaps null', async () => {
// Arrange // Arrange
const availableVaps = [{ partNumber: 'vaps1' }]; const availableVaps = [{ partNumber: 'vaps1' }];
const propsData = { availableVaps }; const initialData = { availableVaps };
const { wrapper } = getMountedComponent({}, {}, propsData); const { wrapper } = getMountedComponent({}, initialData, {});
const expected = availableVaps; const expected = availableVaps;
// Act // Act
@ -877,10 +894,10 @@ describe('cart-dropdown component', () => {
} }
} }
}; };
const propsData = { const initialData = {
availableVaps: [part4, part5] availableVaps: [part4, part5]
}; };
const { wrapper } = getMountedComponent(storeData, {}, propsData); const { wrapper } = getMountedComponent(storeData, initialData, {});
const sortByPartType = (a, b) => { const sortByPartType = (a, b) => {
const typeA = a.partType.toUpperCase(); const typeA = a.partType.toUpperCase();
const typeB = b.partType.toUpperCase(); const typeB = b.partType.toUpperCase();
@ -1791,21 +1808,25 @@ describe('cart-dropdown component', () => {
// Arrange // Arrange
const storeData = { const storeData = {
order: { order: {
policy: {
isITAC: false,
isNoComp: false,
policyLookupSuccessful: true
},
currentDeductible: 321,
policyLookupSuccessful: true,
payment: { payment: {
insuranceCoverage: { insuranceCoverage: {
coverageStatus: coverageStatuses.VERIFIED coverageStatus: coverageStatuses.VERIFIED
} }
}, }
policy: {
isITAC: false
},
currentDeductible: 321
} }
}; };
const { wrapper } = getMountedComponent(storeData); const { wrapper } = getMountedComponent(storeData);
const amount = 123; const amount = 123;
const dollarAmount = '$84.00'; const dollarAmount = '$84.00';
formatAmountInDollars.mockImplementationOnce(() => dollarAmount); formatAmountInDollars
.mockImplementationOnce((value) => (value === amount ? dollarAmount : 1));
// Act // Act
const result = wrapper.vm.getDisplayed(amount); const result = wrapper.vm.getDisplayed(amount);

View file

@ -1,8 +1,9 @@
<template> <template>
<div> <div>
<div <div
v-if="showDropdownHeader"
id="cart-dropdown-head" id="cart-dropdown-head"
class="row cart-toggle flex align-items-center pt-4" class="row flex align-items-center pt-4 cart-header cart-toggle"
:class="[isExpanded ? 'expanded' : '']" :class="[isExpanded ? 'expanded' : '']"
@click="toggleIsExpanded"> @click="toggleIsExpanded">
<a <a
@ -13,6 +14,11 @@
<span class="color-green">{{ getDisplayed(amountDue) }}</span> <span class="color-green">{{ getDisplayed(amountDue) }}</span>
</a> </a>
</div> </div>
<div
v-else
id="cart-dropdown-head"
class="expanded cart-header">
</div>
<div <div
id="cart-table" id="cart-table"
class="cart-table"> class="cart-table">
@ -59,7 +65,7 @@
<div class="py-1 d-flex justify-content-between align-items-center"> <div class="py-1 d-flex justify-content-between align-items-center">
<span>{{ item?.name ?? '' }}</span> <span>{{ item?.name ?? '' }}</span>
<textLink <textLink
v-if="!readOnly || !showAsPaid" v-if="!readOnly"
linkType="text" linkType="text"
text="Remove" text="Remove"
href="javascript:void(0)" href="javascript:void(0)"
@ -94,7 +100,7 @@
</div> </div>
<textLink <textLink
v-if="item.cartItemType === cartItemType.VAP v-if="item.cartItemType === cartItemType.VAP
&& (!readOnly || !showAsPaid)" && !readOnly"
linkType="text" linkType="text"
text="Remove" text="Remove"
href="javascript:void(0)" href="javascript:void(0)"
@ -123,6 +129,13 @@
<span id="sales-tax-label">{{ salesTaxLabel }}</span> <span id="sales-tax-label">{{ salesTaxLabel }}</span>
<span id="sales-tax-value">{{ formatAmountInDollars(salesTax) }}</span> <span id="sales-tax-value">{{ formatAmountInDollars(salesTax) }}</span>
</div> </div>
<div
v-if="showAsPaid"
id="cart-amount-paid"
class="pt-1 col d-flex justify-content-between">
<span id="amount-paid-label">{{ amountPaidLabel }}</span>
<span if="amount-paid-value">{{ getDisplayed(amountPaid) }}</span>
</div>
</div> </div>
<div <div
id="bottom-amount-due" id="bottom-amount-due"
@ -169,13 +182,17 @@ export default {
showAsPaid: Boolean, showAsPaid: Boolean,
readOnly: Boolean, readOnly: Boolean,
recyclingModalCmsWidgetName: String, recyclingModalCmsWidgetName: String,
availableVaps: Array showDropdownHeader: Boolean,
isInitiallyExpanded: Boolean,
submittedOrder: Object
}, },
data() { data() {
return { return {
isExpanded: false, isExpanded: this.isInitiallyExpanded,
availableVaps: this.submittedOrder ? this.submittedOrder.lineItems.vaps : useMainStore().order.availableVaps,
widget: { widget: {
amountDue: 'AmountDueTextWidget', amountDue: 'AmountDueTextWidget',
amountPaid: 'AmountPaidTextWidget',
deductible: 'DeductibleWidget', deductible: 'DeductibleWidget',
basePrice: 'BasePriceWidget', basePrice: 'BasePriceWidget',
subtotal: 'SubtotalWidget', subtotal: 'SubtotalWidget',
@ -191,17 +208,20 @@ export default {
computed: { computed: {
deductible() { deductible() {
// Note: added as computed so it can be used in the template. // Note: added as computed so it can be used in the template.
return useMainStore().order.currentDeductible; return this.submittedOrder ? this.submittedOrder.currentDeductible : useMainStore().order.currentDeductible;
}, },
showDeductibleCartItem() { showDeductibleCartItem() {
return !useMainStore().isNoComp && !useMainStore().isITAC; return !this.isNoComp && !this.isITAC;
},
lineItems() {
return this.submittedOrder ? this.submittedOrder.lineItems : useMainStore().lineItems;
}, },
recycleFeeLineItem() { recycleFeeLineItem() {
return useMainStore().lineItems.supportingItems return this.lineItems.supportingItems
?.find((lineItem) => lineItem.partNumber === partNumberStrings.RECYCLE_FEE); ?.find((lineItem) => lineItem.partNumber === partNumberStrings.RECYCLE_FEE);
}, },
baseServiceLineItems() { baseServiceLineItems() {
const { supportingItems, glassParts, otherParts } = useMainStore().lineItems; const { supportingItems, glassParts, otherParts } = this.lineItems;
const parts = [ const parts = [
...(supportingItems ?? []), ...(supportingItems ?? []),
...(glassParts ?? []), ...(glassParts ?? []),
@ -212,12 +232,21 @@ export default {
baseServicePrice() { baseServicePrice() {
return getPriceOfLineItems(this.baseServiceLineItems) ?? 0; return getPriceOfLineItems(this.baseServiceLineItems) ?? 0;
}, },
isVerifiedCoverageStatus() {
return this.submittedOrder ? this.submittedOrder.payment.insuranceCoverage.isVerified : useMainStore().isVerifiedCoverageStatus;
},
isUnverified() { isUnverified() {
return !useMainStore().isNoComp && !useMainStore().isITAC return !this.isNoComp && !this.isITAC
&& (this.deductible == null || !useMainStore().isVerifiedCoverageStatus); && (this.deductible == null || !this.isVerifiedCoverageStatus);
},
isITAC() {
return this.submittedOrder ? this.submittedOrder.policy.isITAC : useMainStore().isITAC;
},
isNoComp() {
return this.submittedOrder ? this.submittedOrder.policy.noCoverage : useMainStore().isNoComp;
}, },
subTotal() { subTotal() {
const { supportingItems, glassParts, otherParts, vaps, mobileFee } = useMainStore().lineItems; const { supportingItems, glassParts, otherParts, vaps, mobileFee } = this.lineItems;
const allLineItems = [ const allLineItems = [
...(supportingItems ?? []), ...(supportingItems ?? []),
...(glassParts ?? []), ...(glassParts ?? []),
@ -225,12 +254,12 @@ export default {
...(vaps ?? []), ...(vaps ?? []),
mobileFee mobileFee
]; ];
return !useMainStore().isNoComp && !useMainStore().isITAC return !this.isNoComp && !this.isITAC
? this.deductible + getPriceOfLineItems([...this.feeLineItems, ...(vaps ?? [])]) ? this.deductible + getPriceOfLineItems([...this.feeLineItems, ...(vaps ?? [])])
: getPriceOfLineItems(allLineItems); : getPriceOfLineItems(allLineItems);
}, },
feeLineItems() { feeLineItems() {
const { mobileFee } = useMainStore().lineItems; const { mobileFee } = this.lineItems;
const result = []; const result = [];
if (mobileFee) { if (mobileFee) {
result.push(mobileFee); result.push(mobileFee);
@ -247,8 +276,8 @@ export default {
let result = 0; let result = 0;
if (useMainStore().payment.insuranceCoverage.isVerified) { if (!this.isUnverified) {
if (useMainStore().isITAC || useMainStore().isNoComp) { if (this.isITAC || this.isNoComp) {
result += sumTax(this.baseServiceLineItems); result += sumTax(this.baseServiceLineItems);
} else { } else {
// deductible-case need to show tax for Recycle Fee // deductible-case need to show tax for Recycle Fee
@ -256,7 +285,7 @@ export default {
} }
} }
result += sumTax(useMainStore().lineItems.vaps ?? []); result += sumTax(this.lineItems.vaps ?? []);
return result; return result;
}, },
@ -265,8 +294,13 @@ export default {
? 0 ? 0
: this.subTotal + this.salesTax; : this.subTotal + this.salesTax;
}, },
amountPaid() {
return !this.showAsPaid
? 0
: this.subTotal + this.salesTax;
},
availableLineItems() { availableLineItems() {
const { supportingItems, glassParts, otherParts, mobileFee } = useMainStore().lineItems; const { supportingItems, glassParts, otherParts, mobileFee } = this.lineItems;
const result = [ const result = [
...(supportingItems ?? []), ...(supportingItems ?? []),
...(glassParts ?? []), ...(glassParts ?? []),
@ -278,9 +312,12 @@ export default {
} }
return result; return result;
}, },
vehicleDamage() {
return this.submittedOrder ? this.submittedOrder.damage : useMainStore().damage;
},
servicePackageTier() { servicePackageTier() {
const { glassToReplace, isRepair } = useMainStore().damage; const { glassToReplace, isRepair } = this.vehicleDamage;
const { vaps } = useMainStore().lineItems; const { vaps } = this.lineItems;
return getHighestFullySatisfiedTier( return getHighestFullySatisfiedTier(
glassToReplace ?? [], glassToReplace ?? [],
this.availableLineItems, this.availableLineItems,
@ -289,7 +326,7 @@ export default {
); );
}, },
partTypesInServicePackage() { partTypesInServicePackage() {
const { glassToReplace, isRepair } = useMainStore().damage; const { glassToReplace, isRepair } = this.vehicleDamage;
return getPackageContents( return getPackageContents(
glassToReplace ?? [], glassToReplace ?? [],
this.availableLineItems, this.availableLineItems,
@ -311,7 +348,7 @@ export default {
return items; return items;
}, },
nonServicePackageCartItems() { nonServicePackageCartItems() {
const vapPartTypesInOrder = Array.from(new Set(useMainStore().lineItems.vaps?.map((vap) => vap.partType) ?? [])); const vapPartTypesInOrder = Array.from(new Set(this.lineItems.vaps?.map((vap) => vap.partType) ?? []));
const vapPartTypesInOrderButNotPackage = vapPartTypesInOrder const vapPartTypesInOrderButNotPackage = vapPartTypesInOrder
.filter((partType) => !this.partTypesInServicePackage.includes(partType)) .filter((partType) => !this.partTypesInServicePackage.includes(partType))
?? []; ?? [];
@ -354,6 +391,9 @@ export default {
amountDueLabel() { amountDueLabel() {
return this.getCmsContent(this.widget.amountDue, widgetFields.TEXT_BLOCK_WIDGET.TEXT); return this.getCmsContent(this.widget.amountDue, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
}, },
amountPaidLabel() {
return this.getCmsContent(this.widget.amountPaid, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
},
deductibleLabel() { deductibleLabel() {
return this.getCmsContent(this.widget.deductible, widgetFields.TEXT_BLOCK_WIDGET.TEXT); return this.getCmsContent(this.widget.deductible, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
}, },
@ -392,7 +432,7 @@ export default {
: null; : null;
}, },
mobileFeeCartItem() { mobileFeeCartItem() {
const mobileFeeLineItem = useMainStore().lineItems.mobileFee; const mobileFeeLineItem = this.lineItems.mobileFee;
return mobileFeeLineItem return mobileFeeLineItem
? this.getCartItem( ? this.getCartItem(
this.getCmsContent(this.widget.mobileFee, widgetFields.TEXT_BLOCK_WIDGET.TEXT), this.getCmsContent(this.widget.mobileFee, widgetFields.TEXT_BLOCK_WIDGET.TEXT),
@ -412,8 +452,8 @@ export default {
}, },
getDisplayed(amount) { getDisplayed(amount) {
return this.isUnverified return this.isUnverified
&& !useMainStore().isNoComp && !this.isNoComp
&& !useMainStore().isITAC && !this.isITAC
? VERIFYING_COVERAGE ? VERIFYING_COVERAGE
: formatAmountInDollars(amount); : formatAmountInDollars(amount);
}, },
@ -437,7 +477,7 @@ export default {
}, },
getCartItemForVapsPart(partType) { getCartItemForVapsPart(partType) {
const label = this.getCmsContentForVapsType(partType); const label = this.getCmsContentForVapsType(partType);
const lineItems = useMainStore().lineItems.vaps const lineItems = this.lineItems.vaps
?.filter((vapsLineItem) => vapsLineItem.partType === partType) ?? []; ?.filter((vapsLineItem) => vapsLineItem.partType === partType) ?? [];
return this.getCartItem(label, lineItems, cartItemType.VAP, partType); return this.getCartItem(label, lineItems, cartItemType.VAP, partType);
}, },
@ -539,25 +579,30 @@ export default {
text-align: center; text-align: center;
} }
.cart-toggle { .cart-header {
font-weight: $font-weight-bold; &.cart-toggle {
font-weight: $font-weight-bold;
&:after { &:after {
content: ""; content: "";
transition: all 0.5s ease; transition: all 0.5s ease;
background-image: url($svg-payment-method-review-toggle); background-image: url($svg-payment-method-review-toggle);
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: right center; background-position: right center;
width: 1rem; width: 1rem;
height: 0.5625rem; height: 0.5625rem;
display: inline-flex; display: inline-flex;
position: relative; position: relative;
right: 0.75rem; right: 0.75rem;
margin: 0.5rem 0 0.5rem 1rem; margin: 0.5rem 0 0.5rem 1rem;
cursor: pointer; cursor: pointer;
} }
&.expanded:after { &.expanded:after {
transform: rotate(180deg); transform: rotate(180deg);
}
a {
text-decoration: none;
}
} }
&.expanded + .cart-table { &.expanded + .cart-table {
max-height: 50rem; max-height: 50rem;
@ -565,9 +610,6 @@ export default {
overflow: hidden; overflow: hidden;
visibility: visible; visibility: visible;
} }
a {
text-decoration: none;
}
} }
:deep(#recycle-fee-label a){ :deep(#recycle-fee-label a){

View file

@ -4,14 +4,11 @@ exports[`coverageStatement.vue-working returns the initial data 1`] = `
Object { Object {
"baseServiceLineItems": Array [], "baseServiceLineItems": Array [],
"deductibleText": "Your deductible is", "deductibleText": "Your deductible is",
"isNoComp": false,
"isRepair": true,
"loadingText": Array [ "loadingText": Array [
"Connecting to your insurance company", "Connecting to your insurance company",
"Nearly there", "Nearly there",
"Finishing up", "Finishing up",
], ],
"policyLookupSuccessful": true,
"rules": Object { "rules": Object {
"selectionRequired": "option-required", "selectionRequired": "option-required",
}, },

View file

@ -10,7 +10,7 @@ import navigationScenarios from '@/router/router-constants/navigation-scenarios.
import { getRandomString, getRandomInt } from '@/helpers/data-generation.js'; import { getRandomString, getRandomInt } from '@/helpers/data-generation.js';
import settleAllPromises from '@/helpers/layout-helper.js'; import settleAllPromises from '@/helpers/layout-helper.js';
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { useMainStore } from '@/store/index.js'; import { useMainStore, getDefaultState } from '@/store';
import getPriceOfLineItems from '@/helpers/price-calculator.js'; import getPriceOfLineItems from '@/helpers/price-calculator.js';
jest.mock('@/helpers/layout-helper.js', () => jest.fn()); jest.mock('@/helpers/layout-helper.js', () => jest.fn());
@ -90,6 +90,14 @@ function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRu
return { wrapper }; return { wrapper };
} }
beforeEach(() => {
const store = useMainStore();
const defaultState = getDefaultState();
Object.keys(defaultState).forEach((key) => {
store[key] = defaultState[key];
});
});
describe('coverageStatement.vue-working', () => { describe('coverageStatement.vue-working', () => {
test('returns the initial data', () => { test('returns the initial data', () => {
// Arrange // Arrange
@ -99,8 +107,8 @@ describe('coverageStatement.vue-working', () => {
isRepair: true isRepair: true
}, },
policy: { policy: {
policyLookupSuccessful: true, noCoverage: false,
noCoverage: false policyLookupSuccessful: true
} }
} }
}; };
@ -164,43 +172,67 @@ describe('coverageStatement.vue-working', () => {
}); });
describe('Computed', () => { describe('Computed', () => {
describe('verifiedNoComp', () => { describe('verifiedNoComp', () => {
test.each([true, false])('returns false when policyLookupSuccessful false', (isNoComp) => { describe('isNoCompQuoteVisible true', () => {
// Arrange const enableNoCompQuote = true;
const mainInitialState = { test.each([true, false])('returns false when policyLookupSuccessful false', (isNoComp) => {
order: { // Arrange
policy: { const mainInitialState = {
noCoverage: isNoComp, order: {
policyLookupSuccessful: false policy: {
} noCoverage: isNoComp,
} policyLookupSuccessful: false
}; }
const { wrapper } = getMountedComponent(mainInitialState); },
issConfig: { enableNoCompQuote }
};
const { wrapper } = getMountedComponent(mainInitialState);
// Act // Act
const result = wrapper.vm.verifiedNoComp; const result = wrapper.vm.isNoCompQuoteVisible;
// Assert // Assert
expect(result).toBeFalsy(); expect(result).toBeFalsy();
});
test.each([true, false])('returns false when isNoComp false', (policyLookupSuccessful) => {
// Arrange
const mainInitialState = {
order: {
policy: {
noCoverage: false,
policyLookupSuccessful
}
},
issConfig: { enableNoCompQuote }
};
const { wrapper } = getMountedComponent(mainInitialState);
// Act
const result = wrapper.vm.isNoCompQuoteVisible;
// Assert
expect(result).toBeFalsy();
});
test('returns true when policyLookupSuccessful true, noCoverage true, and enableNoCompQuote true', () => {
// Arrange
const mainInitialState = {
order: {
policy: {
noCoverage: true,
policyLookupSuccessful: true
}
},
issConfig: { enableNoCompQuote }
};
const { wrapper } = getMountedComponent(mainInitialState);
// Act
const result = wrapper.vm.isNoCompQuoteVisible;
// Assert
expect(result).toBeTruthy();
});
}); });
test.each([true, false])('returns false when isNoComp false', (policyLookupSuccessful) => { test('returns false when policyLookupSuccessful true, noCoverage true and enableNoCompQuote false', () => {
// Arrange
const mainInitialState = {
order: {
policy: {
noCoverage: false,
policyLookupSuccessful
}
}
};
const { wrapper } = getMountedComponent(mainInitialState);
// Act
const result = wrapper.vm.verifiedNoComp;
// Assert
expect(result).toBeFalsy();
});
test('returns true when policyLookupSuccessful true and policyLookupSuccessful true', () => {
// Arrange // Arrange
const mainInitialState = { const mainInitialState = {
order: { order: {
@ -208,18 +240,21 @@ describe('coverageStatement.vue-working', () => {
noCoverage: true, noCoverage: true,
policyLookupSuccessful: true policyLookupSuccessful: true
} }
},
issConfig: {
enableNoCompQuote: false
} }
}; };
const { wrapper } = getMountedComponent(mainInitialState); const { wrapper } = getMountedComponent(mainInitialState);
// Act // Act
const result = wrapper.vm.verifiedNoComp; const result = wrapper.vm.isNoCompQuoteVisible;
// Assert // Assert
expect(result).toBeTruthy(); expect(result).toBeFalsy();
}); });
}); });
describe('verifiedITAC', () => { describe('isITACQuoteVisible', () => {
const priceOfLineItems = 213; const priceOfLineItems = 213;
test.each([true, false])('returns false when policyLookupSuccessful false', (isNoComp) => { test.each([true, false])('returns false when policyLookupSuccessful false', (isNoComp) => {
// Arrange // Arrange
@ -236,7 +271,7 @@ describe('coverageStatement.vue-working', () => {
const { wrapper } = getMountedComponent(mainInitialState); const { wrapper } = getMountedComponent(mainInitialState);
// Act // Act
const result = wrapper.vm.verifiedITAC; const result = wrapper.vm.isITACQuoteVisible;
// Assert // Assert
expect(result).toBeFalsy(); expect(result).toBeFalsy();
@ -256,7 +291,7 @@ describe('coverageStatement.vue-working', () => {
const { wrapper } = getMountedComponent(mainInitialState); const { wrapper } = getMountedComponent(mainInitialState);
// Act // Act
const result = wrapper.vm.verifiedITAC; const result = wrapper.vm.isITACQuoteVisible;
// Assert // Assert
expect(result).toBeFalsy(); expect(result).toBeFalsy();
@ -281,7 +316,7 @@ describe('coverageStatement.vue-working', () => {
const { wrapper } = getMountedComponent(mainInitialState); const { wrapper } = getMountedComponent(mainInitialState);
// Act // Act
const result = wrapper.vm.verifiedITAC; const result = wrapper.vm.isITACQuoteVisible;
// Assert // Assert
expect(result).toBeFalsy(); expect(result).toBeFalsy();
@ -308,7 +343,7 @@ describe('coverageStatement.vue-working', () => {
const { wrapper } = getMountedComponent(mainInitialState); const { wrapper } = getMountedComponent(mainInitialState);
// Act // Act
const result = wrapper.vm.verifiedITAC; const result = wrapper.vm.isITACQuoteVisible;
// Assert // Assert
expect(result).toBeFalsy(); expect(result).toBeFalsy();
@ -329,13 +364,13 @@ describe('coverageStatement.vue-working', () => {
const { wrapper } = getMountedComponent(mainInitialState); const { wrapper } = getMountedComponent(mainInitialState);
// Act // Act
const result = wrapper.vm.verifiedITAC; const result = wrapper.vm.isITACQuoteVisible;
// Assert // Assert
expect(result).toBeTruthy(); expect(result).toBeTruthy();
}); });
}); });
describe('verifiedDeductible', () => { describe('isDeductibleVisible', () => {
const servicePrice = 123; const servicePrice = 123;
describe('claim registration required', () => { describe('claim registration required', () => {
const issConfig = { isClaimRegistrationRequired: true }; const issConfig = { isClaimRegistrationRequired: true };
@ -351,7 +386,7 @@ describe('coverageStatement.vue-working', () => {
}, },
policy: { policy: {
noCoverage: false, noCoverage: false,
policyLookupSuccessful: false policyLookupSuccessful: true
}, },
currentDeductible: servicePrice - 1 currentDeductible: servicePrice - 1
} }
@ -365,7 +400,7 @@ describe('coverageStatement.vue-working', () => {
const { wrapper } = getMountedComponent(mainInitialState); const { wrapper } = getMountedComponent(mainInitialState);
// Act // Act
const result = wrapper.vm.verifiedDeductible; const result = wrapper.vm.isDeductibleVisible;
// Assert // Assert
expect(result).toBeFalsy(); expect(result).toBeFalsy();
@ -377,7 +412,7 @@ describe('coverageStatement.vue-working', () => {
const { wrapper } = getMountedComponent(mainInitialState); const { wrapper } = getMountedComponent(mainInitialState);
// Act // Act
const result = wrapper.vm.verifiedDeductible; const result = wrapper.vm.isDeductibleVisible;
// Assert // Assert
expect(result).toBeFalsy(); expect(result).toBeFalsy();
@ -387,7 +422,7 @@ describe('coverageStatement.vue-working', () => {
const { wrapper } = getMountedComponent(verifiedDeductibleStoreState); const { wrapper } = getMountedComponent(verifiedDeductibleStoreState);
// Act // Act
const result = wrapper.vm.verifiedDeductible; const result = wrapper.vm.isDeductibleVisible;
// Assert // Assert
expect(result).toBeTruthy(); expect(result).toBeTruthy();
@ -420,7 +455,7 @@ describe('coverageStatement.vue-working', () => {
const { wrapper } = getMountedComponent(mainInitialState); const { wrapper } = getMountedComponent(mainInitialState);
// Act // Act
const result = wrapper.vm.verifiedDeductible; const result = wrapper.vm.isDeductibleVisible;
// Assert // Assert
expect(result).toBeFalsy(); expect(result).toBeFalsy();
@ -430,7 +465,7 @@ describe('coverageStatement.vue-working', () => {
const { wrapper } = getMountedComponent(verifiedDeductibleStoreState); const { wrapper } = getMountedComponent(verifiedDeductibleStoreState);
// Act // Act
const result = wrapper.vm.verifiedDeductible; const result = wrapper.vm.isDeductibleVisible;
// Assert // Assert
expect(result).toBeTruthy(); expect(result).toBeTruthy();
@ -459,7 +494,7 @@ describe('coverageStatement.vue-working', () => {
const { wrapper } = getMountedComponent(storeState); const { wrapper } = getMountedComponent(storeState);
// Act // Act
const result = wrapper.vm.verifiedDeductible; const result = wrapper.vm.isDeductibleVisible;
// Assert // Assert
expect(result).toBeFalsy(); expect(result).toBeFalsy();
@ -486,7 +521,7 @@ describe('coverageStatement.vue-working', () => {
const { wrapper } = getMountedComponent(storeState); const { wrapper } = getMountedComponent(storeState);
// Act // Act
const result = wrapper.vm.verifiedDeductible; const result = wrapper.vm.isDeductibleVisible;
// Assert // Assert
expect(result).toBeFalsy(); expect(result).toBeFalsy();
@ -683,7 +718,7 @@ describe('coverageStatement.vue-working', () => {
// Assert // Assert
expect(result).toBeFalsy(); expect(result).toBeFalsy();
}); });
test('returns true when isNoComp true', () => { test('returns false when isNoComp true and enableNoCompQuote false', () => {
// Arrange // Arrange
const mainInitialState = { const mainInitialState = {
order: { order: {
@ -692,6 +727,32 @@ describe('coverageStatement.vue-working', () => {
noCoverage: true noCoverage: true
}, },
currentDeductible: priceOfLineItems currentDeductible: priceOfLineItems
},
issConfig: {
enableNoCompQuote: false
}
};
getPriceOfLineItems.mockImplementationOnce(() => priceOfLineItems);
const { wrapper } = getMountedComponent(mainInitialState);
// Act
const result = wrapper.vm.isQuoteDisplayed;
// Assert
expect(result).toBeFalsy();
});
test('returns true when isNoComp true and enableNoCompQuote true', () => {
// Arrange
const mainInitialState = {
order: {
policy: {
policyLookupSuccessful: true,
noCoverage: true
},
currentDeductible: priceOfLineItems
},
issConfig: {
enableNoCompQuote: true
} }
}; };
getPriceOfLineItems.mockImplementationOnce(() => priceOfLineItems); getPriceOfLineItems.mockImplementationOnce(() => priceOfLineItems);
@ -712,7 +773,10 @@ describe('coverageStatement.vue-working', () => {
shouldRegisterClaimStoreStateItac = { shouldRegisterClaimStoreStateItac = {
order: { order: {
payment: { payment: {
insuranceCoverage: { claimNumber: null } insuranceCoverage: {
claimNumber: null,
isVerified: true // Added
}
}, },
policy: { policy: {
policyLookupSuccessful: true, policyLookupSuccessful: true,
@ -724,7 +788,8 @@ describe('coverageStatement.vue-working', () => {
currentDeductible: servicePrice - 1 currentDeductible: servicePrice - 1
}, },
issConfig: { issConfig: {
isClaimRegistrationRequired: true isClaimRegistrationRequired: true,
enableNoCompQuote: true
} }
}; };
getPriceOfLineItems.mockImplementation(() => servicePrice); getPriceOfLineItems.mockImplementation(() => servicePrice);
@ -801,6 +866,7 @@ describe('coverageStatement.vue-working', () => {
// Assert // Assert
expect(result).toBeFalsy(); expect(result).toBeFalsy();
}); });
test('returns false when insuranceCoverage not verified', () => {});
describe('returns true when policy lookup success, vehicleId set to %p, claim reg req, claim not yet reg', () => { describe('returns true when policy lookup success, vehicleId set to %p, claim reg req, claim not yet reg', () => {
test('and itac', () => { test('and itac', () => {
// Arrange // Arrange
@ -1010,6 +1076,9 @@ describe('coverageStatement.vue-working', () => {
noCoverage: true, noCoverage: true,
policyLookupSuccessful: true policyLookupSuccessful: true
} }
},
issConfig: {
enableNoCompQuote: true
} }
}; };
const { wrapper } = getMountedComponent(mainInitialState); const { wrapper } = getMountedComponent(mainInitialState);
@ -1038,6 +1107,9 @@ describe('coverageStatement.vue-working', () => {
noCoverage: true, noCoverage: true,
policyLookupSuccessful: true policyLookupSuccessful: true
} }
},
issConfig: {
enableNoCompQuote: true
} }
}; };
const { wrapper } = getMountedComponent(mainInitialState); const { wrapper } = getMountedComponent(mainInitialState);
@ -1166,11 +1238,15 @@ describe('coverageStatement.vue-working', () => {
const initialStore = { const initialStore = {
order: { order: {
payment: { payment: {
insuranceCoverage: { claimNumber: null } insuranceCoverage: {
claimNumber: null,
isVerified: true
}
}, },
policy: { policy: {
policyLookupSuccessful: true, noCoverage: false,
noCoverage: false isITAC: false,
policyLookupSuccessful: true
}, },
vehicle: { vehicle: {
policyVehicleId: 1 policyVehicleId: 1
@ -1191,6 +1267,8 @@ describe('coverageStatement.vue-working', () => {
const { wrapper } = getMountedComponent(initialStore, {}, mockStoreActions); const { wrapper } = getMountedComponent(initialStore, {}, mockStoreActions);
const next = (method) => { method(wrapper.vm); }; const next = (method) => { method(wrapper.vm); };
console.log(wrapper.vm.pageVariation);
// Act // Act
coverageStatement.beforeRouteEnter.call(wrapper.vm, undefined, undefined, next); coverageStatement.beforeRouteEnter.call(wrapper.vm, undefined, undefined, next);
for (let i = 0; i < 7; i++) { for (let i = 0; i < 7; i++) {

View file

@ -104,6 +104,7 @@ import contentGroupModal from '@/iss-components/content-group-modal/content-grou
import buttonQuestion from '@/digital-components/button-question/button-question.vue'; import buttonQuestion from '@/digital-components/button-question/button-question.vue';
import loadingModal from '@/iss-components/loading-modal/loading-modal.vue'; import loadingModal from '@/iss-components/loading-modal/loading-modal.vue';
import textBlock from '@/digital-components/text-block/text-block.vue'; import textBlock from '@/digital-components/text-block/text-block.vue';
import pageVariations from '@/constants/coverage-statement-page-variations';
// Import Supporting Files // Import Supporting Files
import { import {
@ -164,6 +165,7 @@ export default {
const clonedGlassParts = useMainStore().lineItems.glassParts const clonedGlassParts = useMainStore().lineItems.glassParts
? JSON.parse(JSON.stringify(useMainStore().lineItems.glassParts)) ? JSON.parse(JSON.stringify(useMainStore().lineItems.glassParts))
: []; : [];
// TODO SSR-1165: Recycle fee needs removed from quote calculation
const availableLineItems = [ const availableLineItems = [
...(resultMap.supportingItems ?? []), ...(resultMap.supportingItems ?? []),
...(clonedGlassParts ?? []), ...(clonedGlassParts ?? []),
@ -171,10 +173,8 @@ export default {
let hasBailedOut = false; let hasBailedOut = false;
let pricingResults = []; let pricingResults = [];
if ( const { policy, vehicle } = useMainStore();
useMainStore().policy.policyLookupSuccessful && if (policy.policyLookupSuccessful && vehicle.policyVehicleId >= 0) {
useMainStore().vehicle.policyVehicleId >= 0
) {
await useMainStore().getFinalDeductible(); await useMainStore().getFinalDeductible();
pricingResults = await useMainStore() pricingResults = await useMainStore()
.getPriceOrderItems(availableLineItems) .getPriceOrderItems(availableLineItems)
@ -210,12 +210,7 @@ export default {
} }
}, },
data() { data() {
const { isRepair } = useMainStore().damage;
const { policyLookupSuccessful, noCoverage } = useMainStore().policy;
return { return {
isRepair,
policyLookupSuccessful,
isNoComp: noCoverage ?? false,
baseServiceLineItems: [], baseServiceLineItems: [],
selectedProvider: '', selectedProvider: '',
deductibleText: 'Your deductible is', deductibleText: 'Your deductible is',
@ -239,6 +234,47 @@ export default {
}; };
}, },
computed: { computed: {
pageVariation() {
const {
isNoComp,
issConfig,
policy,
payment,
isClaimRegistrationRequired,
} = useMainStore();
const registerClaimSuccessful =
payment.insuranceCoverage.isVerified;
if (!policy.policyLookupSuccessful) {
return pageVariations.UNVERIFIED;
}
if (isNoComp) {
if (issConfig.enableNoCompQuote) {
return pageVariations.NO_COMP;
}
return pageVariations.UNVERIFIED;
}
if (this.deductibleValue == null) {
return pageVariations.UNVERIFIED;
}
// TODO this should be determined based on the result of the ITAC price call
// TODO it seems that ITAC now requires claim registration calls. If this call fails
// should the page become unverified?
if (this.deductibleValue > this.totalServicePrice) {
return pageVariations.ITAC;
}
if (isClaimRegistrationRequired) {
if (registerClaimSuccessful) {
return pageVariations.DEDUCTIBLE;
}
return pageVariations.UNVERIFIED;
}
return pageVariations.DEDUCTIBLE;
},
coverageStatementSubHeader() { coverageStatementSubHeader() {
return this.getTextFromCmsWithCustomIfStatements( return this.getTextFromCmsWithCustomIfStatements(
this.widget.subheader, this.widget.subheader,
@ -252,12 +288,15 @@ export default {
); );
}, },
verifiedItacAlertBody() { verifiedItacAlertBody() {
const itacCostSavings =
this.deductibleValue - this.totalServicePrice;
return this.getCmsContent( return this.getCmsContent(
this.widget.verifiedItacAlert, this.widget.verifiedItacAlert,
widgetFields.ALERT_WIDGET.BODY_TEXT widgetFields.ALERT_WIDGET.BODY_TEXT
)?.replaceAll( )?.replaceAll(
'{custom:costSavings}', '{custom:costSavings}',
this.itacCostSavingsForDisplay
formatAmountInDollars(itacCostSavings)
); );
}, },
secondaryText() { secondaryText() {
@ -291,62 +330,29 @@ export default {
deductibleValue() { deductibleValue() {
return useMainStore().order.currentDeductible; return useMainStore().order.currentDeductible;
}, },
deductibleForDisplay() { isNoCompQuoteVisible() {
return formatAmountInDollars(this.deductibleValue); return this.pageVariation === pageVariations.NO_COMP;
}, },
registerClaimSuccessful() { isITACQuoteVisible() {
return useMainStore().payment.insuranceCoverage.isVerified; return this.pageVariation === pageVariations.ITAC;
}, },
verifiedNoComp() { isDeductibleVisible() {
return this.policyLookupSuccessful && this.isNoComp; return this.pageVariation === pageVariations.DEDUCTIBLE;
}, },
verifiedITAC() {
return ( isUnverifiedVisible() {
this.policyLookupSuccessful && return this.pageVariation === pageVariations.UNVERIFIED;
!this.isNoComp &&
this.deductibleValue > this.totalServicePrice
);
},
coveredAndServicePriceAboveOrEqualDeductible() {
return (
!this.verifiedNoComp &&
this.totalServicePrice >= this.deductibleValue
);
},
verifiedDeductible() {
return useMainStore().isClaimRegistrationRequired
? this.registerClaimSuccessful &&
this.coveredAndServicePriceAboveOrEqualDeductible &&
this.deductibleValue !== null
: this.policyLookupSuccessful &&
this.coveredAndServicePriceAboveOrEqualDeductible;
},
unverified() {
return (
!this.verifiedDeductible &&
!this.verifiedITAC &&
!this.verifiedNoComp
);
}, },
isADAS() { isADAS() {
const parts = useMainStore().order.lineItems.glassParts; const { glassParts } = useMainStore().order.lineItems;
return ( return (
parts !== null && glassParts !== null &&
!!parts.find((part) => part.requiresRecalibration) !!glassParts.find((part) => part.requiresRecalibration)
); );
}, },
totalServicePrice() { totalServicePrice() {
return getPriceOfLineItems(this.baseServiceLineItems); return getPriceOfLineItems(this.baseServiceLineItems);
}, },
servicePriceForDisplay() {
return formatAmountInDollars(this.totalServicePrice);
},
itacCostSavings() {
return this.deductibleValue - this.totalServicePrice;
},
itacCostSavingsForDisplay() {
return formatAmountInDollars(this.itacCostSavings);
},
serviceProviderQuestionText() { serviceProviderQuestionText() {
return this.getCmsContent( return this.getCmsContent(
this.widget.serviceProviderQuestion, this.widget.serviceProviderQuestion,
@ -360,17 +366,23 @@ export default {
); );
}, },
isQuoteDisplayed() { isQuoteDisplayed() {
return this.verifiedITAC || this.verifiedNoComp; return this.isITACQuoteVisible || this.isNoCompQuoteVisible;
}, },
shouldRegisterClaim() { shouldRegisterClaim() {
const {
policy,
vehicle,
isClaimRegistrationRequired,
isClaimAlreadyRegistered,
} = useMainStore();
const { policyVehicleId } = vehicle;
return ( return (
this.policyLookupSuccessful && policy.policyLookupSuccessful &&
useMainStore().vehicle.policyVehicleId != null && policyVehicleId != null &&
useMainStore().vehicle.policyVehicleId >= 0 && policyVehicleId >= 0 &&
useMainStore().isClaimRegistrationRequired && isClaimRegistrationRequired &&
!useMainStore().isClaimAlreadyRegistered && !isClaimAlreadyRegistered &&
(this.coveredAndServicePriceAboveOrEqualDeductible || !this.isNoCompQuoteVisible
this.verifiedITAC)
); );
}, },
}, },
@ -397,9 +409,10 @@ export default {
return !!useMainStore().vehicle.carId; return !!useMainStore().vehicle.carId;
}, },
async initializeComponent() { async initializeComponent() {
useMainStore().updatePolicyITACFlag(this.verifiedITAC); useMainStore().updatePolicyITACFlag(this.isITACQuoteVisible);
// TODO how should coverage status be updated
const coverageStatus = const coverageStatus =
this.verifiedITAC || this.verifiedNoComp this.isITACQuoteVisible || this.isNoCompQuoteVisible
? coverageStatuses.VERIFIED ? coverageStatuses.VERIFIED
: coverageStatuses.PENDING; : coverageStatuses.PENDING;
useMainStore().updateCoverageStatus(coverageStatus); useMainStore().updateCoverageStatus(coverageStatus);
@ -411,10 +424,10 @@ export default {
this.$refs.loadingModal.hideModal(); this.$refs.loadingModal.hideModal();
}, },
async navigateForward() { async navigateForward() {
if (this.unverified || this.verifiedDeductible) { if (this.isUnverifiedVisible || this.isDeductibleVisible) {
useMainStore().updateSupportingItems(this.supportingItems); useMainStore().updateSupportingItems(this.supportingItems);
this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD); this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD);
} else if (this.verifiedITAC || this.verifiedNoComp) { } else if (this.isITACQuoteVisible || this.isNoCompQuoteVisible) {
useMainStore().updateIsSafeliteProvider( useMainStore().updateIsSafeliteProvider(
this.selectedProvider === SAFELITE_PROVIDER this.selectedProvider === SAFELITE_PROVIDER
); );
@ -450,28 +463,29 @@ export default {
); );
}, },
getCustomValueFromString(str) { getCustomValueFromString(str) {
const { isRepair } = useMainStore().damage;
switch (str) { switch (str) {
case 'coverageUnverified': case 'coverageUnverified':
return this.unverified; return this.isUnverifiedVisible;
case 'verifiedDeductible': case 'verifiedDeductible':
return this.verifiedDeductible; return this.isDeductibleVisible;
case 'verifiedITAC': case 'verifiedITAC':
return this.verifiedITAC; return this.isITACQuoteVisible;
case 'verifiedNoComp': case 'verifiedNoComp':
return this.verifiedNoComp; return this.isNoCompQuoteVisible;
case 'ADASReplace': case 'ADASReplace':
return !this.isRepair && this.isADAS; return !isRepair && this.isADAS;
case 'nonADASReplace': case 'nonADASReplace':
return !this.isRepair && !this.isADAS; return !isRepair && !this.isADAS;
case 'nonADASRepair': case 'nonADASRepair':
return this.isRepair; return isRepair;
case 'deductibleOverZero': case 'deductibleOverZero':
return ( return (
this.verifiedDeductible && this.deductibleValue !== 0 this.isDeductibleVisible && this.deductibleValue !== 0
); // TODO what if deductible is negative? ); // TODO what if deductible is negative?
case 'isDeductibleZero': case 'isDeductibleZero':
return ( return (
this.verifiedDeductible && this.deductibleValue === 0 this.isDeductibleVisible && this.deductibleValue === 0
); );
default: default:
return null; return null;
@ -483,6 +497,7 @@ export default {
setBaseServiceLineItems(lineItems) { setBaseServiceLineItems(lineItems) {
this.baseServiceLineItems = lineItems; this.baseServiceLineItems = lineItems;
}, },
formatAmountInDollars,
}, },
}; };
</script> </script>

View file

@ -123,6 +123,10 @@ export default {
if (clientFlags.ClaimRegistrationRequired) { if (clientFlags.ClaimRegistrationRequired) {
this.mainStore.issConfig.isClaimRegistrationRequired = true; this.mainStore.issConfig.isClaimRegistrationRequired = true;
} }
if (clientFlags.EnableNoCompQuote) {
this.mainStore.issConfig.enableNoCompQuote = true;
}
} }
} catch (e) { } catch (e) {
console.error(`Error parsing client flags: ${e}`); console.error(`Error parsing client flags: ${e}`);

View file

@ -14,7 +14,8 @@ jest.mock('@/helpers/layout-helper.js', () => jest.fn());
jest.mock('@/helpers/cms-content-helper', () => ({ jest.mock('@/helpers/cms-content-helper', () => ({
fetchCmsContentForPage: jest.fn(), fetchCmsContentForPage: jest.fn(),
processIfStatements: jest.fn() processIfStatements: jest.fn(),
splitCopyOnCMSPlaceHolder: jest.fn()
})); }));
const wordingText = 'wording text {custom:address}'; const wordingText = 'wording text {custom:address}';
@ -105,7 +106,17 @@ const sessionStorage = {
zipCode: '12345' zipCode: '12345'
} }
} }
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
const sessionStorageMock = (() => { const sessionStorageMock = (() => {
@ -302,7 +313,17 @@ describe('OrderConfirmation.vue', () => {
}, },
serviceLocation: { serviceLocation: {
appointmentType: 'Mobile' appointmentType: 'Mobile'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -331,7 +352,17 @@ describe('OrderConfirmation.vue', () => {
zipCode: '12345' zipCode: '12345'
} }
} }
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -368,7 +399,17 @@ describe('OrderConfirmation.vue', () => {
state: 'AZ', state: 'AZ',
zipCode: '12345', zipCode: '12345',
appointmentType: 'Mobile' appointmentType: 'Mobile'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -397,7 +438,17 @@ describe('OrderConfirmation.vue', () => {
} }
}, },
appointmentType: 'Dropoff' appointmentType: 'Dropoff'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -426,7 +477,17 @@ describe('OrderConfirmation.vue', () => {
} }
}, },
appointmentType: 'Inshop' appointmentType: 'Inshop'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -452,7 +513,17 @@ describe('OrderConfirmation.vue', () => {
state: 'AZ', state: 'AZ',
zipCode: '12345', zipCode: '12345',
appointmentType: 'Mobile' appointmentType: 'Mobile'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -481,7 +552,17 @@ describe('OrderConfirmation.vue', () => {
} }
}, },
appointmentType: 'Inshop' appointmentType: 'Inshop'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -507,7 +588,17 @@ describe('OrderConfirmation.vue', () => {
state: 'AZ', state: 'AZ',
zipCode: '12345', zipCode: '12345',
appointmentType: 'Mobile' appointmentType: 'Mobile'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -536,7 +627,17 @@ describe('OrderConfirmation.vue', () => {
} }
}, },
appointmentType: 'Dropoff' appointmentType: 'Dropoff'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();
@ -566,7 +667,17 @@ describe('OrderConfirmation.vue', () => {
} }
}, },
appointmentType: 'Inshop' appointmentType: 'Inshop'
} },
payment: {
isPayInAdvance: false
},
lineItems: {
vaps: []
},
policy: {
noCoverage: false
},
damage: {}
}; };
window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage)); window.sessionStorage.setItem('submittedOrder', JSON.stringify(testSessionStorage));
const { wrapper } = getMountedComponent(); const { wrapper } = getMountedComponent();

View file

@ -47,6 +47,16 @@
class="appointment-text text-center lh-base mt-2" class="appointment-text text-center lh-base mt-2"
v-html="appointmentWordingText2"></div> v-html="appointmentWordingText2"></div>
</div> </div>
<div>
<cartDropdown
:showAsPaid="isPayInAdvance"
:readOnly="true"
:isInitiallyExpanded="false"
:showDropdownHeader="true"
recyclingModalCmsWidgetName="RecycleModal"
servicePackageTitleWidgetName="ServicePackageTitle"
:submittedOrder="submittedOrder" />
</div>
<div <div
class="email-confirmation-text" class="email-confirmation-text"
v-html="confirmationEmailText" /> v-html="confirmationEmailText" />
@ -69,6 +79,8 @@ import siteHeader from '@/iss-components/site-header/site-header.vue';
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue'; import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue';
import siteFooter from '@/iss-components/site-footer/site-footer.vue'; import siteFooter from '@/iss-components/site-footer/site-footer.vue';
import addToCalendar from '@/layouts/order-confirmation/add-to-calendar/add-to-calendar.vue'; import addToCalendar from '@/layouts/order-confirmation/add-to-calendar/add-to-calendar.vue';
import cartDropdown from '@/iss-components/cart-dropdown/cart-dropdown.vue';
// Supporting files // Supporting files
import { import {
fetchCmsContentForPage, fetchCmsContentForPage,
@ -97,13 +109,14 @@ export default {
vehicleBanner, vehicleBanner,
siteFooter, siteFooter,
addToCalendar, addToCalendar,
cartDropdown,
}, },
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
console.log('beforeRouteEnter just called');
useMainStore().createSubmittedOrder(); useMainStore().createSubmittedOrder();
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
{ {
@ -113,6 +126,7 @@ export default {
]; ];
// use resultMap to populate layout content. // use resultMap to populate layout content.
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => { next((vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
}); });
@ -307,6 +321,12 @@ export default {
); );
return inshopDurationTime; return inshopDurationTime;
}, },
isPayInAdvance() {
return this.submittedOrder.payment.isPayInAdvance;
},
selectedVaps() {
return this.submittedOrder.lineItems.vaps;
},
}, },
mounted() { mounted() {
if (this.carrierUrl) { if (this.carrierUrl) {

View file

@ -3,20 +3,28 @@ import paymentMethod from '@/layouts/payment-method/payment-method.vue';
// Supporting Files // Supporting Files
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { createTestingPinia } from '@pinia/testing';
import { getMountOptions } from '@/helpers/unit-test-helper.js'; import { getMountOptions } from '@/helpers/unit-test-helper.js';
import { useMainStore } from '@/store'; import { useMainStore, getDefaultState } from '@/store';
import issPageValues from '@/router/router-constants/issPage-values'; import issPageValues from '@/router/router-constants/issPage-values';
import { paymentMethods } from '@/constants/payment-method-constants'; import { paymentMethods } from '@/constants/payment-method-constants';
import queryStrings from '@/constants/query-strings'; import queryStrings from '@/constants/query-strings';
import { experimentSettings } from '@/constants/experiments'; import { experimentSettings } from '@/constants/experiments';
function setupMocks({ customMountOptions = {}, queryString }) { function setupMocks({ customMountOptions = {}, queryString }, mainInitialState = {}, customMixin = null) {
const mountOptions = getMountOptions({ const mountOptions = getMountOptions({
...customMountOptions, ...customMountOptions,
route: { query: { issPage: issPageValues.PAYMENT_METHOD, ...queryString }, params: {} } route: { query: { issPage: issPageValues.PAYMENT_METHOD, ...queryString }, params: {} }
}); });
const mockMixin = { const testingPinia = createTestingPinia({
initialState: {
main: mainInitialState
}
});
useMainStore(testingPinia);
const mockMixin = customMixin ?? {
methods: { methods: {
getSettingValue: jest.fn((settingName) => { getSettingValue: jest.fn((settingName) => {
if (settingName === experimentSettings.ISS_DISPLAY_PAY_IN_ADVANCE) { if (settingName === experimentSettings.ISS_DISPLAY_PAY_IN_ADVANCE) {
@ -28,6 +36,7 @@ function setupMocks({ customMountOptions = {}, queryString }) {
} }
}; };
mountOptions.global.plugins = [testingPinia];
mountOptions.global.mixins = [mockMixin]; mountOptions.global.mixins = [mockMixin];
const wrapper = shallowMount(paymentMethod, mountOptions); const wrapper = shallowMount(paymentMethod, mountOptions);
@ -35,31 +44,53 @@ function setupMocks({ customMountOptions = {}, queryString }) {
return wrapper; return wrapper;
} }
beforeEach(() => {
const store = useMainStore();
const defaultState = getDefaultState();
Object.keys(defaultState).forEach((key) => {
store[key] = defaultState[key];
});
});
describe('payment-method.vue', () => { describe('payment-method.vue', () => {
describe('Payment Method Type', () => { describe('Payment Method Type', () => {
test('getting payment method when method is pay later', async () => { test('getting payment method when method is pay later', async () => {
// Arrange // Arrange
const wrapper = setupMocks({}); const payLaterMethod = paymentMethods.PAY_AT_TIME_OF_SERVICE;
const payLaterPaymentMethod = paymentMethods.PAY_AT_TIME_OF_SERVICE; const store = {
useMainStore().savePaymentMethodChoice(payLaterPaymentMethod); order: {
payment: {
isPayInAdvance: false,
payInAdvanceType: null
}
}
};
const wrapper = setupMocks({}, store);
// Act // Act
const paymethod = wrapper.vm.getPaymentMethodFromStore(); const paymethod = wrapper.vm.getPaymentMethodFromStore();
// Assert // Assert
expect(paymethod).toBe(payLaterPaymentMethod); expect(paymethod).toBe(payLaterMethod);
}); });
test('getting payment method when method is pay in advance', async () => { test('getting payment method when method is pay in advance', async () => {
// Arrange // Arrange
const wrapper = setupMocks({}); const payInAdvanceMethod = paymentMethods.CREDIT_CARD;
const payInAdvancePaymentMethod = paymentMethods.CREDIT_CARD; const store = {
useMainStore().savePaymentMethodChoice(payInAdvancePaymentMethod); order: {
payment: {
isPayInAdvance: true,
payInAdvanceType: payInAdvanceMethod
}
}
};
const wrapper = setupMocks({}, store);
// Act // Act
const paymethod = wrapper.vm.getPaymentMethodFromStore(); const paymethod = wrapper.vm.getPaymentMethodFromStore();
// Assert // Assert
expect(paymethod).not.toBe(payInAdvancePaymentMethod); expect(paymethod).toBe(payInAdvanceMethod);
}); });
}); });
@ -86,4 +117,138 @@ describe('payment-method.vue', () => {
expect(payInAdvanceErrorAlert.exists()).toBeFalsy(); expect(payInAdvanceErrorAlert.exists()).toBeFalsy();
}); });
}); });
describe('isPayInAdvanceDisabled', () => {
let store = {};
let mixin = {
methods: {
getSettingValue: jest.fn((settingName) => {
if (settingName === experimentSettings.ISS_DISPLAY_PAY_IN_ADVANCE) {
return 'true';
}
return 'false';
})
}
};
beforeEach(() => {
store = {
order: {
policy: {
isITAC: false,
noCoverage: false,
policyLookupSuccessful: true
},
payment: {
insuranceCoverage: {
isVerified: true
}
},
currentDeductible: 123
},
issConfig: {
isClaimRegistrationRequired: true,
enableNoCompQuote: true
},
applicationUser: {
experiments: [
{
settings: {
ISSDisplayPIAInsurance_ISS: 'true'
}
}
]
}
};
});
test('returns true when policyLookupSuccessful false', () => {
// Arrange
store.order.policy.policyLookupSuccessful = false;
const wrapper = setupMocks({}, store, mixin);
// Act
const result = wrapper.vm.isPayInAdvanceDisabled;
// Assert
expect(result).toBeTruthy();
});
test('returns true when no comp and enableNoCompQuote false', () => {
// Arrange
store.order.policy.noCoverage = true;
store.issConfig.enableNoCompQuote = false;
const wrapper = setupMocks({}, store, mixin);
// Act
const result = wrapper.vm.isPayInAdvanceDisabled;
// Assert
expect(result).toBeTruthy();
});
test('returns true when not no comp and currentDeductible null', () => {
// Arrange
store.order.policy.noCoverage = false;
store.order.currentDeductible = null;
const wrapper = setupMocks({}, store, mixin);
// Act
const result = wrapper.vm.isPayInAdvanceDisabled;
// Assert
expect(result).toBeTruthy();
});
test('returns true when isClaimRegistrationRequired true and insurance coverage not verified', () => {
// Arrange
store.order.payment.insuranceCoverage.isVerified = false;
store.issConfig.isClaimRegistrationRequired = true;
const wrapper = setupMocks({}, store, mixin);
// Act
const result = wrapper.vm.isPayInAdvanceDisabled;
// Assert
expect(result).toBeTruthy();
});
test('returns false when no comp, enableNoCompQuote true, and currentDeductible null and pia enabled', () => {
// Arrange
store.order.policy.noCoverage = true;
store.issConfig.enableNoCompQuote = true;
store.order.currentDeductible = null;
const wrapper = setupMocks({}, store, mixin);
// Act
const result = wrapper.vm.isPayInAdvanceDisabled;
// Assert
expect(result).toBeFalsy();
});
test('returns false when not no comp, currentDeductible not null and pia enabled', () => {
// Arrange
const wrapper = setupMocks({}, store, mixin);
// Act
const result = wrapper.vm.isPayInAdvanceDisabled;
// Assert
expect(result).toBeFalsy();
});
test.skip('returns true when pia not enabled', () => {
// Arrange
mixin = {
methods: {
getSettingValue: jest.fn((settingName) => {
if (settingName === experimentSettings.ISS_DISPLAY_PAY_IN_ADVANCE) {
return 'false';
}
return 'true';
})
}
};
const wrapper = setupMocks({}, store, mixin);
// Act
const result = wrapper.vm.isPayInAdvanceDisabled;
// Assert
expect(result).toBeTruthy();
});
});
}); });

View file

@ -4,61 +4,52 @@
v-slot="{ meta }" v-slot="{ meta }"
@submit="onSubmit" @submit="onSubmit"
@invalidSubmit="onInvalidSubmit"> @invalidSubmit="onInvalidSubmit">
<div class="container-fluid fade-on-route-transition"> <div class="page-container-grouped-styles">
<div class="row justify-content-center"> <siteHeader ref="siteHeader" cmsWidgetName="SiteHeaderWidget" />
<div class="col-md-6 px-0 px-md-2"> <vehicleBanner
<siteHeader cmsWidgetName="VehicleBannerWidget"
ref="siteHeader" :displayGenericVehicleImage="false" />
cmsWidgetName="SiteHeaderWidget" /> <siteSubHeader
</div> class="mb-5 mt-4 px-3"
</div> cmsWidgetName="SiteSubHeaderWidget"
<div class="row justify-content-center"> subHeaderClasses="mt-2" />
<div class="col-md-6 col-xl-4"> <div class="main-content-container">
<vehicleBanner <hr class="my-0" />
cmsWidgetName="VehicleBannerWidget" <reviewDropdown ref="reviewDropdown" />
:displayGenericVehicleImage="false" /> <hr class="my-0" />
<siteSubHeader <cartDropdown
class="mb-5 mt-4 px-3" :showAsPaid="false"
cmsWidgetName="SiteSubHeaderWidget" :readOnly="false"
subHeaderClasses="mt-2" /> :showDropdownHeader="true"
<div class="main-content-container"> :isInitiallyExpanded="false"
<hr class="my-0" /> recyclingModalCmsWidgetName="RecycleModal"
<reviewDropdown ref="reviewDropdown" /> servicePackageTitleWidgetName="ServicePackageTitle" />
<hr class="my-0" /> <hr class="mt-0 mb-5" />
<cartDropdown <alert
:showAsPaid="false" v-if="displayPayInAdvanceAlert"
:readOnly="false" name="payInAdvanceErrorAlert"
recyclingModalCmsWidgetName="RecycleModal" class="my-4"
servicePackageTitleWidgetName="ServicePackageTitle" cmsWidgetName="PayInAdvanceErrorAlertWidget"
:availableVaps="availableVaps" /> alertClass="alert-danger"
<hr class="mt-0 mb-5" /> :isDismissible="false" />
<alert <paymentMethodQuestion
v-if="displayPayInAdvanceAlert" v-if="!isPayInAdvanceDisabled"
name="payInAdvanceErrorAlert" v-model="paymentMethodInternalModel"
class="my-4" cmsWidgetName="PaymentMethodWidget"
cmsWidgetName="PayInAdvanceErrorAlertWidget" :validationRules="rules.optionRequired" />
alertClass="alert-danger" <alert
:isDismissible="false" /> v-if="isPayInAdvanceDisabled"
<paymentMethodQuestion :isDismissible="false"
v-if="!isPayInAdvanceDisabled" alertClass="alert-info"
v-model="paymentMethodInternalModel" cmsWidgetName="NoPayInAdvanceDisclaimerWidget"
cmsWidgetName="PaymentMethodWidget" :shouldScrollToOnMount="false" />
:validationRules="rules.optionRequired" /> <siteFooter
<alert ref="siteFooter"
v-if="isPayInAdvanceDisabled" cmsWidgetName="SiteFooterWidget"
:isDismissible="false" :isForwardActionDisabled="!meta.valid"
alertClass="alert-info" :isStackedVertically="true"
cmsWidgetName="NoPayInAdvanceDisclaimerWidget" @backClicked="navigateBack"
:shouldScrollToOnMount="false" /> @ForwardClicked="forwardButtonAction" />
<siteFooter
ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
:isStackedVertically="true"
@backClicked="navigateBack"
@ForwardClicked="forwardButtonAction" />
</div>
</div>
</div> </div>
</div> </div>
</Form> </Form>
@ -87,6 +78,7 @@ import { Form } from 'vee-validate';
import issPageValues from '@/router/router-constants/issPage-values'; import issPageValues from '@/router/router-constants/issPage-values';
import bailoutMessage from '@/constants/bailoutMessage'; import bailoutMessage from '@/constants/bailoutMessage';
import { AppointmentTypeStrings } from '@/constants/schedule-constants'; import { AppointmentTypeStrings } from '@/constants/schedule-constants';
import VehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue';
export default { export default {
name: 'payment-method', name: 'payment-method',
@ -100,6 +92,7 @@ export default {
cartDropdown, cartDropdown,
paymentMethodQuestion, paymentMethodQuestion,
alert, alert,
VehicleBanner,
}, },
mixins: [baseFormMixin], mixins: [baseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
@ -155,13 +148,12 @@ export default {
vm.$refs.reviewDropdown.initializeComponent( vm.$refs.reviewDropdown.initializeComponent(
resultMap.reviewDropdownData resultMap.reviewDropdownData
); );
vm.setAvailableVaps(pricedVaps ?? []); vm.storeAvailableVaps(pricedVaps ?? []);
}); });
} }
}, },
data() { data() {
return { return {
availableVaps: [],
paymentMethodInternalModel: this.getPaymentMethodFromStore(), paymentMethodInternalModel: this.getPaymentMethodFromStore(),
rules: { rules: {
optionRequired: globalRules.OPTION_REQUIRED, optionRequired: globalRules.OPTION_REQUIRED,
@ -185,20 +177,7 @@ export default {
return this.$route.query[queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT]; return this.$route.query[queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT];
}, },
isPayInAdvanceDisabled() { isPayInAdvanceDisabled() {
const piaExperience = this.getSettingValue( return useMainStore().isUnverified;
experimentSettings.ISS_DISPLAY_PAY_IN_ADVANCE
);
const isEnabled = piaExperience === 'true';
return !isEnabled || this.isUnverified;
},
isUnverified() {
return (
!useMainStore().isNoComp &&
!useMainStore().isITAC &&
(useMainStore().order.currentDeductible == null ||
!useMainStore().isVerifiedCoverageStatus)
);
}, },
paymentMethod() { paymentMethod() {
return this.paymentMethodInternalModel; return this.paymentMethodInternalModel;
@ -323,8 +302,8 @@ export default {
); );
} }
}, },
setAvailableVaps(vaps) { storeAvailableVaps(vaps) {
this.availableVaps = vaps; useMainStore().order.availableVaps = vaps;
}, },
}, },
}; };

View file

@ -7,6 +7,7 @@ import { getMountOptions } from '@/helpers/unit-test-helper';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import { paymentMethods, hopPaymentMethods } from '@/constants/payment-method-constants.js'; import { paymentMethods, hopPaymentMethods } from '@/constants/payment-method-constants.js';
import queryStrings from '@/constants/query-strings'; import queryStrings from '@/constants/query-strings';
import CartDropdown from '@/iss-components/cart-dropdown/cart-dropdown.vue';
// Constants // Constants
const parts = { const parts = {
@ -316,6 +317,44 @@ describe('payment-page.vue', () => {
}); });
}); });
describe('cart-dropdown', () => {
test('Show cart dropdown if credit card', async () => {
const wrapper = setupMocks({});
wrapper.vm.paymentType = hopPaymentMethods.CREDIT_CARD;
// Act
await wrapper.vm.$nextTick();
const cartTable = wrapper.findComponent(CartDropdown);
// Assert
expect(cartTable.exists()).toBeTruthy();
});
test('Hide cart dropdown if afterpay', async () => {
const wrapper = setupMocks({});
wrapper.vm.paymentType = hopPaymentMethods.AFTERPAY;
// Act
await wrapper.vm.$nextTick();
const cartTable = wrapper.findComponent(CartDropdown);
// Assert
expect(cartTable.exists()).toBeFalsy();
});
test('Hide cart dropdown if paypal', async () => {
const wrapper = setupMocks({});
wrapper.vm.paymentType = hopPaymentMethods.PAYPAL;
// Act
await wrapper.vm.$nextTick();
const cartTable = wrapper.findComponent(CartDropdown);
// Assert
expect(cartTable.exists()).toBeFalsy();
});
});
describe('isPaypal', () => { describe('isPaypal', () => {
test('Is responsive if initial data changes', async () => { test('Is responsive if initial data changes', async () => {
// Arrange // Arrange

View file

@ -44,6 +44,15 @@
:isForwardButtonHidden="true" :isForwardButtonHidden="true"
@ForwardClicked="forwardButtonAction" @ForwardClicked="forwardButtonAction"
@backClicked="backButtonAction" /> @backClicked="backButtonAction" />
<div v-if="isCreditCard" class="px-2">
<cartDropdown
:readOnly="true"
:showDropdownHeader="false"
:isInitiallyExpanded="true"
recyclingModalCmsWidgetName="RecycleModal"
servicePackageTitleWidgetName="ServicePackageTitle" />
</div>
</div> </div>
</div> </div>
<div <div
@ -231,10 +240,12 @@ import {
import { AppointmentTypeStrings } from '@/constants/schedule-constants.js'; import { AppointmentTypeStrings } from '@/constants/schedule-constants.js';
import queryStrings from '@/constants/query-strings'; import queryStrings from '@/constants/query-strings';
import alert from '@/ux-components/alert/alert.vue'; import alert from '@/ux-components/alert/alert.vue';
import cartDropdown from '@/iss-components/cart-dropdown/cart-dropdown.vue';
export default { export default {
name: 'payment-page', name: 'payment-page',
components: { components: {
cartDropdown,
siteHeader, siteHeader,
siteFooter, siteFooter,
// eslint-disable-next-line vue/no-reserved-component-names // eslint-disable-next-line vue/no-reserved-component-names
@ -261,9 +272,6 @@ export default {
const paymentSignaturePromise = const paymentSignaturePromise =
await useMainStore().getPaymentSignature(); await useMainStore().getPaymentSignature();
const wipersPromise = useMainStore().getWipers();
const rainDefensePromise = useMainStore().getRainDefense();
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
{ {
@ -274,52 +282,13 @@ export default {
resultKey: 'paymentSignature', resultKey: 'paymentSignature',
promise: paymentSignaturePromise, promise: paymentSignaturePromise,
}, },
{
resultKey: 'wipers',
promise: wipersPromise,
},
{
resultKey: 'rainDefense',
promise: rainDefensePromise,
},
]; ];
// use resultMap to populate layout content. // use resultMap to populate layout content.
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
const lineItemsFromStore = useMainStore().lineItems;
const glassParts = lineItemsFromStore.glassParts ?? [];
const supportingItems = lineItemsFromStore.supportingItems ?? [];
const lineItemsToTax = [
resultMap.rainDefense,
...supportingItems,
...resultMap.wipers,
...glassParts,
];
const availableVaps = [resultMap.rainDefense, ...resultMap.wipers];
const pricedLineItemsToTax =
await useMainStore().priceOrderItemsAndSaveServerData(
lineItemsToTax
);
const taxedLineItems =
await useMainStore().taxOrderItemsAndSaveServerData(
pricedLineItemsToTax
);
// Match all line items to the line items as they are in the store
// and rebuild the original structure.
const taxLineItems = useMainStore().mapTaxedLineItemsToStoreFormat(
taxedLineItems,
lineItemsFromStore
);
const taxedVaps = useMainStore().mapTaxedLineItemsToStoreFormat(
taxedLineItems,
availableVaps
);
next((vm) => { next((vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
vm.setData(taxedVaps, taxLineItems);
vm.$nextTick(() => { vm.$nextTick(() => {
if (vm.$refs.cart) { if (vm.$refs.cart) {
@ -403,6 +372,9 @@ export default {
isPaypal() { isPaypal() {
return this.paymentType === hopPaymentMethods.PAYPAL; return this.paymentType === hopPaymentMethods.PAYPAL;
}, },
isCreditCard() {
return this.paymentType === hopPaymentMethods.CREDIT_CARD;
},
displayPayInAdvanceCreditCardAlert() { displayPayInAdvanceCreditCardAlert() {
return this.displayPayInAdvanceAlert(paymentMethods.CREDIT_CARD); return this.displayPayInAdvanceAlert(paymentMethods.CREDIT_CARD);
}, },
@ -479,10 +451,6 @@ export default {
paymentMethodReqs paymentMethodReqs
); );
}, },
setData(taxedVaps, taxLineItems) {
this.availableVaps = taxedVaps;
this.lineItems = taxLineItems;
},
getWorkOrderNumber() { getWorkOrderNumber() {
const { workOrderNumber } = useMainStore().order; const { workOrderNumber } = useMainStore().order;
if (workOrderNumber) { if (workOrderNumber) {

View file

@ -18,18 +18,6 @@ const mockMixin = {
getCmsContent: jest.fn().mockImplementation(() => ''), getCmsContent: jest.fn().mockImplementation(() => ''),
setCmsContent: jest.fn(), setCmsContent: jest.fn(),
dispatchStoreAction: jest.fn().mockImplementation((storeAction) => { dispatchStoreAction: jest.fn().mockImplementation((storeAction) => {
if (storeAction === 'priceOrderItemsAndSaveServerData') {
return Promise.resolve([
{
partNumber: 'EARLY BIRD',
description: null,
partType: 'EARLY BIRD',
laborAmount: 0,
sellingPrice: 14.99,
kitPrice: 0
}
]);
}
if (storeAction === 'saveSupportingItemsSuppressingStateResetting') { if (storeAction === 'saveSupportingItemsSuppressingStateResetting') {
return Promise.resolve([ return Promise.resolve([
{ {

View file

@ -264,9 +264,7 @@ export default {
const premiumFeeWithPricePromise = premiumFeePromise.then((result) => { const premiumFeeWithPricePromise = premiumFeePromise.then((result) => {
if (result.data) { if (result.data) {
return useMainStore().priceOrderItemsAndSaveServerData( return useMainStore().getPriceOrderItems(result.data);
result.data
);
} }
return result.data; return result.data;
}); });

View file

@ -4,116 +4,92 @@
v-slot="{ meta }" v-slot="{ meta }"
@submit="onSubmit" @submit="onSubmit"
@invalidSubmit="onInvalidSubmit"> @invalidSubmit="onInvalidSubmit">
<div class="container-fluid fade-on-route-transition"> <div class="page-container-grouped-styles">
<div class="row justify-content-center"> <siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="col-md-6 px-0 px-md-2"> <siteSubHeader cmsWidgetName="SiteSubHeaderWidget" class="mt-5" />
<siteHeader <div class="main-content-container">
class="mb-2 header" <serviceZipModalQuestion
cmsWidgetName="SiteHeaderWidget" /> ref="serviceZipCodeQuestion"
</div> v-model="serviceZipCodeQuestion"
</div> modalWidgetName="ServiceZipModalWidget"
<div class="row justify-content-center"> :onZipUpdateCallback="reloadShopData"
<div class="col-md-6 col-xl-4"> @updatedServiceability="setServiceabilityDetails"
<siteSubHeader @updatedContainsMilitaryBase="setContainsMilitaryBase" />
id="sub-header" <alert
cmsWidgetName="SiteSubHeader" v-if="displayMilitaryZipAlert"
class="mb-5 mt-4" /> ref="alertMilitaryBaseZip"
</div> class="my-5"
</div> cmsWidgetName="AlertMilitaryBaseZipWidget"
<div class="row justify-content-center"> alertClass="alert-warning" />
<div class="col-md-6"> <alert
<serviceZipModalQuestion v-if="displayServiceableMobileOnly"
ref="serviceZipCodeQuestion" ref="alertMobileOnly"
v-model="serviceZipCodeQuestion" class="my-5"
modalWidgetName="ServiceZipModalWidget" cmsWidgetName="AlertMobileOnlyWidget"
:onZipUpdateCallback="reloadShopData" alertClass="alert-warning" />
@updatedServiceability="setServiceabilityDetails" <alert
@updatedContainsMilitaryBase=" v-if="displayRecalibrationWarning"
setContainsMilitaryBase ref="alertRecalNoMobile"
" /> class="my-5"
<alert cmsWidgetName="AlertRecalNoMobileWidget"
v-if="displayMilitaryZipAlert" alertClass="alert-warning"
ref="alertMilitaryBaseZip" @text-link-clicked="openModalAction" />
class="my-5" <alert
cmsWidgetName="AlertMilitaryBaseZipWidget" v-if="displayServiceableInshopOnly"
alertClass="alert-warning" /> ref="alertInshopOnly"
<alert class="my-5"
v-if="displayServiceableMobileOnly" cmsWidgetName="AlertInshopOnlyWidget"
ref="alertMobileOnly" alertClass="alert-warning" />
class="my-5" <alert
cmsWidgetName="AlertMobileOnlyWidget" v-if="displayNoShopsAlert"
alertClass="alert-warning" /> ref="alertNoShops"
<alert class="my-5"
v-if="displayRecalibrationWarning" cmsWidgetName="AlertNoShopsWidget"
ref="alertRecalNoMobile" alertClass="alert-warning" />
class="my-5" <appointmentTypeQuestion
cmsWidgetName="AlertRecalNoMobileWidget" v-show="isAppointmentTypeDisplayed"
alertClass="alert-warning" ref="appointmentTypeQuestion"
@text-link-clicked="openModalAction" /> v-model="selectedAppointmentType"
<alert :isServiceableMobile="isServiceableMobile"
v-if="displayServiceableInshopOnly" :isServiceableInshop="isServiceableInshop"
ref="alertInshopOnly" :isDisplayed="isAppointmentTypeDisplayed"
class="my-5" groupName="appointmentTypeQuestion"
cmsWidgetName="AlertInshopOnlyWidget" cmsWidgetName="AppointmentTypeQuestionWidget"
alertClass="alert-warning" /> validationRules="option-required" />
<alert <mobileLocationModalQuestions
v-if="displayNoShopsAlert" v-if="isMobileLocationDisplayed"
ref="alertNoShops" ref="mobileLocationQuestions"
class="my-5" v-model="mobileLocationQuestions"
cmsWidgetName="AlertNoShopsWidget" customComponentId="mobileLocationQuestions"
alertClass="alert-warning" /> :mobileFeePart="mobileFeePart"
<appointmentTypeQuestion validationRules="mobile-location-required"
v-show="isAppointmentTypeDisplayed" linkWidgetName="MobileLocationLinkWidget"
ref="appointmentTypeQuestion" modalWidgetName="MobileLocationModalWidget"
v-model="selectedAppointmentType" :onZipUpdateCallback="reloadShopData"
:isServiceableMobile="isServiceableMobile" @updated-mobile-fee-part="setMobileFeePart"
:isServiceableInshop="isServiceableInshop" @updated-serviceability="setServiceabilityDetails"
:isDisplayed="isAppointmentTypeDisplayed" @updated-contains-military-base="setContainsMilitaryBase" />
groupName="appointmentTypeQuestion" <shopQuestion
cmsWidgetName="AppointmentTypeQuestionWidget" v-show="isShopQuestionDisplayed"
validationRules="option-required" /> ref="shopQuestion"
<mobileLocationModalQuestions v-model="selectedProvider"
v-if="isMobileLocationDisplayed" :selectedAppointmentType="selectedAppointmentType"
ref="mobileLocationQuestions" :isDisplayed="isShopQuestionDisplayed"
v-model="mobileLocationQuestions" cmsWidgetName="ShopQuestionWidget"
customComponentId="mobileLocationQuestions" @updatedMobileProviderNumber="setMobileProviderNumber" />
:mobileFeePart="mobileFeePart" <contentGroupModal
validationRules="mobile-location-required" ref="RecalModal"
linkWidgetName="MobileLocationLinkWidget" cmsWidgetName="RecalModal" />
modalWidgetName="MobileLocationModalWidget" <siteFooter
:onZipUpdateCallback="reloadShopData" ref="siteFooter"
@updated-mobile-fee-part="setMobileFeePart" class="mt-5"
@updated-serviceability="setServiceabilityDetails" cmsWidgetName="SiteFooterWidget"
@updated-contains-military-base=" :isForwardActionDisabled="
setContainsMilitaryBase !meta.valid || displayNoShopsAlert
" /> "
<shopQuestion @backClicked="navigateBack(this, navigateBackScenario)"
v-show="isShopQuestionDisplayed" @forwardClicked="forwardButtonAction" />
ref="shopQuestion" >>>>>>> develop
v-model="selectedProvider"
:selectedAppointmentType="selectedAppointmentType"
:isDisplayed="isShopQuestionDisplayed"
cmsWidgetName="ShopQuestionWidget"
@updatedMobileProviderNumber="
setMobileProviderNumber
" />
<contentGroupModal
ref="RecalModal"
cmsWidgetName="RecalModal" />
</div>
</div>
<div class="row justify-content-center">
<div class="col-md-6 col-xl-4">
<siteFooter
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="
!meta.valid || displayNoShopsAlert
"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction" />
</div>
</div> </div>
</div> </div>
</Form> </Form>
@ -382,6 +358,12 @@ export default {
displayServiceableMobileOnly() { displayServiceableMobileOnly() {
return this.isServiceableMobile && !this.isServiceableInshop; return this.isServiceableMobile && !this.isServiceableInshop;
}, },
navigateBackScenario() {
const { isNoComp, isITAC } = useMainStore();
return isNoComp || isITAC
? this.navigationScenarios.CLICKED_BACK_CANNOT_REACH_TPA_FLOW
: this.navigationScenarios.CLICKED_BACK_CAN_REACH_TPA_FLOW;
},
}, },
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {

View file

@ -28,10 +28,10 @@ export default {
const footerInfoBox = document.querySelector('.footer#infoBox'); const footerInfoBox = document.querySelector('.footer#infoBox');
return footerInfoBox ? footerInfoBox.offsetHeight : 0; return footerInfoBox ? footerInfoBox.offsetHeight : 0;
}, },
navigateBack(vm) { navigateBack(vm, scenario = this.navigationScenarios.CLICKED_BACK) {
const self = vm ?? this; const self = vm ?? this;
self.$router.navigateWithSpinner(this.navigationScenarios.CLICKED_BACK, self.$route); self.$router.navigateWithSpinner(scenario, self.$route);
}, },
savePageDataToStore(page, data) { savePageDataToStore(page, data) {
useMainStore().updatePageData({ page, data }); useMainStore().updatePageData({ page, data });

View file

@ -80,6 +80,10 @@ const navigationScenarios = Object.freeze({
EDIT_PREFERRED_SHOP: 'EDIT_PREFERRED_SHOP', EDIT_PREFERRED_SHOP: 'EDIT_PREFERRED_SHOP',
EDIT_CONTACT_DETAILS: 'EDIT_CONTACT_DETAILS', EDIT_CONTACT_DETAILS: 'EDIT_CONTACT_DETAILS',
// Service location
CLICKED_BACK_CANNOT_REACH_TPA_FLOW: 'CLICKED_BACK_CANNOT_REACH_TPA_FLOW',
CLICKED_BACK_CAN_REACH_TPA_FLOW: 'CLICKED_BACK_CAN_REACH_TPA_FLOW',
// Provider Preference // Provider Preference
CLICKED_FORWARD_WITH_SAFELITE: 'CLICKED_FORWARD_WITH_SAFELITE', CLICKED_FORWARD_WITH_SAFELITE: 'CLICKED_FORWARD_WITH_SAFELITE',
CLICKED_FORWARD_WITH_TPA_ENABLED: 'CLICKED_FORWARD_WITH_TPA_ENABLED', CLICKED_FORWARD_WITH_TPA_ENABLED: 'CLICKED_FORWARD_WITH_TPA_ENABLED',

View file

@ -559,7 +559,11 @@ const routingTable = () => [
issPageValue: issPageValues.SERVICE_LOCATION, issPageValue: issPageValues.SERVICE_LOCATION,
maps: [ maps: [
{ {
scenario: navigationScenarios.CLICKED_BACK, scenario: navigationScenarios.CLICKED_BACK_CANNOT_REACH_TPA_FLOW,
destinationIssPageValue: issPageValues.COVERAGE_STATEMENT
},
{
scenario: navigationScenarios.CLICKED_BACK_CAN_REACH_TPA_FLOW,
destinationIssPageValue: issPageValues.PROVIDER_PREFERENCE destinationIssPageValue: issPageValues.PROVIDER_PREFERENCE
}, },
{ {

View file

@ -206,7 +206,8 @@ export const getDefaultState = () => ({
currentDeductible: null, currentDeductible: null,
carrierPhoneNumber: null, carrierPhoneNumber: null,
loadedFromDupeCheck: null, loadedFromDupeCheck: null,
loadedSessionClearedPreviousData: null loadedSessionClearedPreviousData: null,
availableVaps: null
}, },
applicationUser: { applicationUser: {
experiments: [], experiments: [],
@ -242,7 +243,8 @@ export const getDefaultState = () => ({
policyZipCode: null, policyZipCode: null,
dateOfLoss: null dateOfLoss: null
}, },
siteType: null siteType: null,
enableNoCompQuote: false
} }
}); });
@ -280,8 +282,27 @@ export const useMainStore = defineStore({
isClaimAlreadyRegistered: (state) => state.order.payment.insuranceCoverage.claimNumber !== null, isClaimAlreadyRegistered: (state) => state.order.payment.insuranceCoverage.claimNumber !== null,
isBailout: (state) => state.applicationUser.pageData[issPageValues.BAILOUT_PAGE] != null, isBailout: (state) => state.applicationUser.pageData[issPageValues.BAILOUT_PAGE] != null,
bailoutCode: (state) => state.applicationUser.pageData[issPageValues.BAILOUT_PAGE]?.bailoutCode, bailoutCode: (state) => state.applicationUser.pageData[issPageValues.BAILOUT_PAGE]?.bailoutCode,
isNoComp: (state) => !!state.order.policy.noCoverage, isNoComp: (s) => !!s.order.policy.noCoverage,
isITAC: (state) => !!state.order.policy.isITAC, isITAC: (state) => !!state.order.policy.isITAC,
isUnverified: (s) => {
const { payment, currentDeductible, policy } = s.order;
const { policyLookupSuccessful } = policy;
const registerClaimSuccessful = !!payment.insuranceCoverage.isVerified;
if (!policyLookupSuccessful) {
return true;
}
if (s.isNoComp && !s.issConfig.enableNoCompQuote) {
return true;
}
if (!s.isNoComp && currentDeductible == null) {
return true;
}
if (s.isClaimRegistrationRequired && !registerClaimSuccessful) {
return true;
}
return false;
},
// TODO condense verification logic
isVerifiedCoverageStatus: (state) => state.order.payment.insuranceCoverage.coverageStatus === coverageStatuses.VERIFIED, isVerifiedCoverageStatus: (state) => state.order.payment.insuranceCoverage.coverageStatus === coverageStatuses.VERIFIED,
eventBusItem: (state) => (eventCategory, eventSubCategory) => { eventBusItem: (state) => (eventCategory, eventSubCategory) => {
const matchedEvent = state.applicationUser.eventBus.find(({ category, subCategory }) => category === eventCategory && subCategory === eventSubCategory); const matchedEvent = state.applicationUser.eventBus.find(({ category, subCategory }) => category === eventCategory && subCategory === eventSubCategory);
@ -1691,6 +1712,7 @@ export const useMainStore = defineStore({
this.issConfig.disabledFields.policyZipCode = false; this.issConfig.disabledFields.policyZipCode = false;
this.issConfig.disabledFields.dateOfLoss = false; this.issConfig.disabledFields.dateOfLoss = false;
this.issConfig.siteType = null; this.issConfig.siteType = null;
this.issConfig.enableNoCompQuote = false;
this.issConfig.billToAccountNumber = null; this.issConfig.billToAccountNumber = null;
this.issConfig.itacCashBillToNumber = null; this.issConfig.itacCashBillToNumber = null;
this.issConfig.itacFnrBillToNumber = null; this.issConfig.itacFnrBillToNumber = null;
@ -1880,46 +1902,6 @@ export const useMainStore = defineStore({
this.updateCapabilityQuestionAnswers(capabilityQuestionAnswersArray); this.updateCapabilityQuestionAnswers(capabilityQuestionAnswersArray);
}, },
// Price order actions
async priceOrderItemsAndSaveServerData(availableLineItems, serviceZipCode, serviceZipCodeCtu) {
const zipCodeToUse = serviceZipCode || this.order.serviceLocation.zipCode;
const ctuToUse = serviceZipCodeCtu || this.order.serviceLocation.zipCodeCtu;
const flattenedLineItemsWithChildParts = getFlattenedArrayOfLineItemsWithChildParts(availableLineItems);
const lineItemsWithOnlyPartNumbers = flattenedLineItemsWithChildParts.map((lineItem) => ({
partNumber: lineItem.partNumber
}));
const availableLineItemsFormattedForRequest =
buildQueryStringParameterFromArrayOfComplexObjects(
lineItemsWithOnlyPartNumbers,
'lineItems'
);
const { vehicle } = this.order;
let queryString =
`ParentAccountNumber=${this.order.accountNumber}`
+ `&CTU=${ctuToUse}`
+ `&CarId=${vehicle.carId}`
+ `&Make=${vehicle.make}`
+ `&Model=${vehicle.model}`
+ `&Year=${vehicle.year}`
+ `&EON=${this.order.eon}`
+ `&ZipCode=${zipCodeToUse}`
+ `&${availableLineItemsFormattedForRequest}`;
const lineItemServerData = this.order.lineItems.serverData;
if (lineItemServerData) {
queryString += `&ServerData=${encodeURIComponent(lineItemServerData)}`;
}
const response = await globalMethods.callHttpClient({
method: endpoints.GetPriceOrderItems.method,
endpoint: `${endpoints.GetPriceOrderItems.url}?${queryString}`
});
// context.commit(storeMutations.UPDATE_LINE_ITEMS_SERVER_DATA, response.data.serverData);
return addPricesToLineItems(availableLineItems, response.data.lineItems);
},
// Tax order actions // Tax order actions
async taxOrderItemsAndSaveServerData(pricedLineItems) { async taxOrderItemsAndSaveServerData(pricedLineItems) {
const { order } = this; const { order } = this;
@ -2291,13 +2273,13 @@ export const useMainStore = defineStore({
setBailout(bailoutData) { setBailout(bailoutData) {
const params = new URL(document.location.toString()).searchParams; const params = new URL(document.location.toString()).searchParams;
const currentPage = params.get('issPage'); const currentPage = params.get('issPage');
this.updatePageData({ this.updatePageData({
page: issPageValues.BAILOUT_PAGE, page: issPageValues.BAILOUT_PAGE,
data: { data: {
url: window.location.href, url: window.location.href,
page: currentPage || 'Unknown Page', page: currentPage || 'Unknown Page',
bailoutCode: bailoutData.code, bailoutCode: bailoutData.code,
errorMessage: bailoutData.message, errorMessage: bailoutData.message,
submit: false submit: false

View file

@ -1465,6 +1465,79 @@ describe('Store', () => {
}); });
}); });
describe('isUnverified', () => {
beforeEach(() => {
store.order.policy.isITAC = false;
store.order.policy.noCoverage = false;
store.order.payment.insuranceCoverage.isVerified = true;
store.order.currentDeductible = 123;
store.order.policy.policyLookupSuccessful = true;
store.issConfig.isClaimRegistrationRequired = true;
store.issConfig.enableNoCompQuote = true;
});
it('returns true when policyLookupSuccessful false', () => {
// Arrange
store.order.policy.policyLookupSuccessful = false;
// Act
const result = store.isUnverified;
// Assert
expect(result).toBeTruthy();
});
it('returns true when no comp and enableNoCompQuote false', () => {
// Arrange
store.order.policy.noCoverage = true;
store.issConfig.enableNoCompQuote = false;
// Act
const result = store.isUnverified;
// Assert
expect(result).toBeTruthy();
});
it('returns true when not no comp and currentDeductible null', () => {
// Arrange
store.order.policy.noCoverage = false;
store.order.currentDeductible = null;
// Act
const result = store.isUnverified;
// Assert
expect(result).toBeTruthy();
});
it('returns true when isClaimRegistrationRequired true and insurance coverage not verified', () => {
// Arrange
store.order.payment.insuranceCoverage.isVerified = false;
// Act
const result = store.isUnverified;
// Assert
expect(result).toBeTruthy();
});
it('returns false when no comp, enableNoCompQuote true, and currentDeductible null', () => {
// Arrange
store.order.policy.noCoverage = true;
store.order.currentDeductible = null;
store.issConfig.enableNoCompQuote = true;
// Act
const result = store.isUnverified;
// Assert
expect(result).toBeFalsy();
});
it('returns false when not no comp, currentDeductible not null', () => {
// Act
const result = store.isUnverified;
// Assert
expect(result).toBeFalsy();
});
});
describe('isMobileAppointment', () => { describe('isMobileAppointment', () => {
it('Should return true for mobile appointments', () => { it('Should return true for mobile appointments', () => {
// Arrange // Arrange