Merge pull request #1101 from Safelite/feature/humphries/INSR-7759
INSR-7759: payment-method Parity Updates
This commit is contained in:
commit
1f80b31f11
33 changed files with 838 additions and 2222 deletions
BIN
src/assets/img/icons/chevron-circle_review_dropdown.png
Normal file
BIN
src/assets/img/icons/chevron-circle_review_dropdown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -60,10 +60,11 @@ export const pageProgressMapper = {
|
||||||
percent: 85
|
percent: 85
|
||||||
},
|
},
|
||||||
'payment-method': {
|
'payment-method': {
|
||||||
percent: 90
|
percent: 95
|
||||||
},
|
},
|
||||||
'payment-page': {
|
'payment-page': {
|
||||||
percent: 95
|
// No progress bar on this page
|
||||||
|
percent: 0
|
||||||
},
|
},
|
||||||
'tpa-search': {
|
'tpa-search': {
|
||||||
percent: 80
|
percent: 80
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,12 @@ export function getLineItems(order) {
|
||||||
* @returns {[]} array of service line items
|
* @returns {[]} array of service line items
|
||||||
*/
|
*/
|
||||||
export function getServiceLineItems(order) {
|
export function getServiceLineItems(order) {
|
||||||
const { supportingItems, glassParts, otherParts } = getLineItems(order);
|
const { supportingItems, glassParts, otherParts, feeItems } = getLineItems(order);
|
||||||
return [
|
return [
|
||||||
...(supportingItems ?? []),
|
...(supportingItems ?? []),
|
||||||
...(glassParts ?? []),
|
...(glassParts ?? []),
|
||||||
...(otherParts ?? [])
|
...(otherParts ?? []),
|
||||||
|
...(feeItems ?? [])
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,10 +34,9 @@ export function getServiceLineItems(order) {
|
||||||
* @returns {[]} array of non-service line items
|
* @returns {[]} array of non-service line items
|
||||||
*/
|
*/
|
||||||
export function getNonServiceLineItems(order) {
|
export function getNonServiceLineItems(order) {
|
||||||
const { vaps, feeItems } = getLineItems(order);
|
const { vaps } = getLineItems(order);
|
||||||
return [
|
return [
|
||||||
...(vaps ?? []),
|
...(vaps ?? [])
|
||||||
...(feeItems ?? [])
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -142,3 +142,18 @@ export function getSalesTax(order) {
|
||||||
export function getCartTotal(order) {
|
export function getCartTotal(order) {
|
||||||
return Math.round((getSubtotal(order) + getSalesTax(order)) * 100) / 100;
|
return Math.round((getSubtotal(order) + getSalesTax(order)) * 100) / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the total price of all recal child parts on the order
|
||||||
|
* @param {object} order order object
|
||||||
|
* @returns {number} total price of all recal child parts on the order
|
||||||
|
*/
|
||||||
|
export function getRecalibrationTotal(order) {
|
||||||
|
const itemsWithRecalibration = getServiceLineItems(order)
|
||||||
|
.filter((item) => item.requiresRecalibration);
|
||||||
|
const recalibrationLineItems = itemsWithRecalibration.map((item) => {
|
||||||
|
return item.childParts?.find((child) => child.partType === partTypeStrings.RECALIBRATION || child.partType === partTypeStrings.ADAS_RECALIBRATION) ?? {};
|
||||||
|
});
|
||||||
|
|
||||||
|
return getPriceOfLineItems(recalibrationLineItems);
|
||||||
|
}
|
||||||
|
|
@ -93,10 +93,12 @@ describe('cart-helper', () => {
|
||||||
const result = getServiceLineItems(order);
|
const result = getServiceLineItems(order);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).toHaveLength(3);
|
expect(result).toHaveLength(5);
|
||||||
expect(result).toContain(supportingLineItem);
|
expect(result).toContain(supportingLineItem);
|
||||||
expect(result).toContain(glassLineItem);
|
expect(result).toContain(glassLineItem);
|
||||||
expect(result).toContain(otherLineItem);
|
expect(result).toContain(otherLineItem);
|
||||||
|
expect(result).toContain(mobileFeeLineItem);
|
||||||
|
expect(result).toContain(recycleFeeLineItem);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Returns empty array when line items are null', () => {
|
test('Returns empty array when line items are null', () => {
|
||||||
|
|
@ -124,10 +126,8 @@ describe('cart-helper', () => {
|
||||||
const result = getNonServiceLineItems(order);
|
const result = getNonServiceLineItems(order);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).toHaveLength(3);
|
expect(result).toHaveLength(1);
|
||||||
expect(result).toContain(vapsLineItem);
|
expect(result).toContain(vapsLineItem);
|
||||||
expect(result).toContain(mobileFeeLineItem);
|
|
||||||
expect(result).toContain(recycleFeeLineItem);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Returns empty array when line items are null', () => {
|
test('Returns empty array when line items are null', () => {
|
||||||
|
|
@ -403,7 +403,7 @@ describe('cart-helper', () => {
|
||||||
describe.each([
|
describe.each([
|
||||||
[330, coverageType.ITAC],
|
[330, coverageType.ITAC],
|
||||||
[330, coverageType.NO_COMP],
|
[330, coverageType.NO_COMP],
|
||||||
[310, coverageType.Deductible]])('getSubtotal with line items', (expected, type) => {
|
[160, coverageType.Deductible]])('getSubtotal with line items', (expected, type) => {
|
||||||
test(`Returns ${expected} cart subtotal when verified ${getEnumName(coverageType, type)}`, () => {
|
test(`Returns ${expected} cart subtotal when verified ${getEnumName(coverageType, type)}`, () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const order = {
|
const order = {
|
||||||
|
|
@ -480,7 +480,7 @@ describe('cart-helper', () => {
|
||||||
describe.each([
|
describe.each([
|
||||||
[21, coverageType.ITAC],
|
[21, coverageType.ITAC],
|
||||||
[21, coverageType.NO_COMP],
|
[21, coverageType.NO_COMP],
|
||||||
[15, coverageType.Deductible]])('getSalesTax with line items', (expected, type) => {
|
[4, coverageType.Deductible]])('getSalesTax with line items', (expected, type) => {
|
||||||
test(`Returns ${expected} when coverageType is ${getEnumName(coverageType, type)}`, () => {
|
test(`Returns ${expected} when coverageType is ${getEnumName(coverageType, type)}`, () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const order = {
|
const order = {
|
||||||
|
|
@ -565,7 +565,7 @@ describe('cart-helper', () => {
|
||||||
describe.each([
|
describe.each([
|
||||||
[351, coverageType.ITAC],
|
[351, coverageType.ITAC],
|
||||||
[351, coverageType.NO_COMP],
|
[351, coverageType.NO_COMP],
|
||||||
[325, coverageType.Deductible]
|
[164, coverageType.Deductible]
|
||||||
])('getCartTotal items', (expected, type) => {
|
])('getCartTotal items', (expected, type) => {
|
||||||
test(`Returns ${expected} cart total when verified ${getEnumName(coverageType, type)}`, () => {
|
test(`Returns ${expected} cart total when verified ${getEnumName(coverageType, type)}`, () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
exports[`cart-dropdown component initial data rendered as expected 1`] = `
|
exports[`cart-dropdown component initial data rendered as expected 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"RECYCLING_MODAL_REF_NAME": "RecycleModal",
|
|
||||||
"cartItemType": Object {
|
"cartItemType": Object {
|
||||||
"MOBILE_FEE": "MOBILE FEE",
|
"MOBILE_FEE": "MOBILE FEE",
|
||||||
"RECYCLE_FEE": "RECYCLE FEE",
|
"RECYCLE_FEE": "RECYCLE FEE",
|
||||||
|
|
@ -16,6 +15,7 @@ Object {
|
||||||
"deductible": "DeductibleWidget",
|
"deductible": "DeductibleWidget",
|
||||||
"guaranteeText": "GuaranteeCartItemTextWidget",
|
"guaranteeText": "GuaranteeCartItemTextWidget",
|
||||||
"mobileFee": "MobileServiceWidget",
|
"mobileFee": "MobileServiceWidget",
|
||||||
|
"recalibration": "RecalibrationWidget",
|
||||||
"recycleFee": "RecycleFeeWidget",
|
"recycleFee": "RecycleFeeWidget",
|
||||||
"salesTax": "SalesTaxWidget",
|
"salesTax": "SalesTaxWidget",
|
||||||
"servicePackage": "ServicePackageTitle",
|
"servicePackage": "ServicePackageTitle",
|
||||||
|
|
|
||||||
|
|
@ -73,10 +73,22 @@ describe('cart-dropdown component', () => {
|
||||||
expect(wrapper.vm.$data).toMatchSnapshot();
|
expect(wrapper.vm.$data).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
describe('displays', () => {
|
describe('displays', () => {
|
||||||
test('cart table', () => {
|
test('cart table when not deductible only', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const reference = '.cart-table';
|
const reference = '.cart-table';
|
||||||
const { wrapper } = getMountedComponent({}, {}, {});
|
const part1 = { partType: partTypeStrings.FRONT_WIPER };
|
||||||
|
const storeData = {
|
||||||
|
order: {
|
||||||
|
insuranceCoverage: {
|
||||||
|
coverageStatus: coverageStatuses.VERIFIED,
|
||||||
|
coverageType: coverageType.Deductible
|
||||||
|
},
|
||||||
|
lineItems: {
|
||||||
|
vaps: [part1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const cartTable = wrapper.find(reference);
|
const cartTable = wrapper.find(reference);
|
||||||
|
|
@ -84,9 +96,9 @@ describe('cart-dropdown component', () => {
|
||||||
// Assert
|
// Assert
|
||||||
expect(cartTable.exists()).toBeTruthy();
|
expect(cartTable.exists()).toBeTruthy();
|
||||||
});
|
});
|
||||||
test('cart deductible when showDeductibleCartItem is true', () => {
|
test('deductible-box when deductible only', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const reference = '#deductible-label';
|
const reference = '.deductible-box';
|
||||||
const storeData = {
|
const storeData = {
|
||||||
order: {
|
order: {
|
||||||
insuranceCoverage: {
|
insuranceCoverage: {
|
||||||
|
|
@ -127,9 +139,9 @@ describe('cart-dropdown component', () => {
|
||||||
// Assert
|
// Assert
|
||||||
expect(cartBasePrice.exists()).toBeTruthy();
|
expect(cartBasePrice.exists()).toBeTruthy();
|
||||||
});
|
});
|
||||||
test('non service package cart items', () => {
|
test('additional cart items', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const reference = '.non-packaged.cart-item';
|
const reference = '.cart-item';
|
||||||
const part1 = { partType: 'apple' };
|
const part1 = { partType: 'apple' };
|
||||||
const part2 = { partType: 'banana' };
|
const part2 = { partType: 'banana' };
|
||||||
const part3 = { partType: 'orange' };
|
const part3 = { partType: 'orange' };
|
||||||
|
|
@ -155,38 +167,27 @@ describe('cart-dropdown component', () => {
|
||||||
const { wrapper } = getMountedComponent(storeData, {}, {});
|
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const nonPackagedCartItems = wrapper.find(reference);
|
const cartItems = wrapper.findAll(reference);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(nonPackagedCartItems.exists()).toBeTruthy();
|
expect(cartItems.length).toBeGreaterThan(1);
|
||||||
});
|
});
|
||||||
test('recycle cart item label when isRecycle true for some cart item', () => {
|
test('cart footer when not deductible only', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const reference = '#recycle-fee-label';
|
const reference = '.cart-footer';
|
||||||
const isExpanded = true;
|
const part1 = { partType: partTypeStrings.FRONT_WIPER };
|
||||||
const initialPropsData = { isExpanded };
|
|
||||||
const storeData = {
|
const storeData = {
|
||||||
order: {
|
order: {
|
||||||
|
insuranceCoverage: {
|
||||||
|
coverageStatus: coverageStatuses.VERIFIED,
|
||||||
|
coverageType: coverageType.Deductible
|
||||||
|
},
|
||||||
lineItems: {
|
lineItems: {
|
||||||
feeItems: [
|
vaps: [part1],
|
||||||
{ partNumber: partNumberStrings.RECYCLE_FEE }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const { wrapper } = getMountedComponent(storeData, {}, initialPropsData);
|
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||||
|
|
||||||
// Act
|
|
||||||
const recycleFeeLabel = wrapper.find(reference);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.recycleFeeCartItem).not.toBeNull();
|
|
||||||
expect(recycleFeeLabel.exists()).toBeTruthy();
|
|
||||||
});
|
|
||||||
test('cart footer', () => {
|
|
||||||
// Arrange
|
|
||||||
const reference = '#cart-footer';
|
|
||||||
const { wrapper } = getMountedComponent({}, {}, {});
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const cartFooter = wrapper.find(reference);
|
const cartFooter = wrapper.find(reference);
|
||||||
|
|
@ -197,7 +198,19 @@ describe('cart-dropdown component', () => {
|
||||||
test('subtotal', () => {
|
test('subtotal', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const reference = '#cart-subtotal';
|
const reference = '#cart-subtotal';
|
||||||
const { wrapper } = getMountedComponent({}, {}, {});
|
const part1 = { partType: partTypeStrings.FRONT_WIPER };
|
||||||
|
const storeData = {
|
||||||
|
order: {
|
||||||
|
insuranceCoverage: {
|
||||||
|
coverageStatus: coverageStatuses.VERIFIED,
|
||||||
|
coverageType: coverageType.Deductible
|
||||||
|
},
|
||||||
|
lineItems: {
|
||||||
|
vaps: [part1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const subtotal = wrapper.find(reference);
|
const subtotal = wrapper.find(reference);
|
||||||
|
|
@ -208,7 +221,19 @@ describe('cart-dropdown component', () => {
|
||||||
test('tax', () => {
|
test('tax', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const reference = '#cart-sales-tax';
|
const reference = '#cart-sales-tax';
|
||||||
const { wrapper } = getMountedComponent({}, {}, {});
|
const part1 = { partType: partTypeStrings.FRONT_WIPER };
|
||||||
|
const storeData = {
|
||||||
|
order: {
|
||||||
|
insuranceCoverage: {
|
||||||
|
coverageStatus: coverageStatuses.VERIFIED,
|
||||||
|
coverageType: coverageType.Deductible
|
||||||
|
},
|
||||||
|
lineItems: {
|
||||||
|
vaps: [part1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const salesTax = wrapper.find(reference);
|
const salesTax = wrapper.find(reference);
|
||||||
|
|
@ -222,8 +247,16 @@ describe('cart-dropdown component', () => {
|
||||||
const isExpanded = true;
|
const isExpanded = true;
|
||||||
const showAsPaid = true;
|
const showAsPaid = true;
|
||||||
const initialPropsData = { isExpanded, showAsPaid };
|
const initialPropsData = { isExpanded, showAsPaid };
|
||||||
|
const part1 = { partType: partTypeStrings.FRONT_WIPER };
|
||||||
const storeData = {
|
const storeData = {
|
||||||
order: {
|
order: {
|
||||||
|
insuranceCoverage: {
|
||||||
|
coverageStatus: coverageStatuses.VERIFIED,
|
||||||
|
coverageType: coverageType.Deductible
|
||||||
|
},
|
||||||
|
lineItems: {
|
||||||
|
vaps: [part1],
|
||||||
|
},
|
||||||
payment: {
|
payment: {
|
||||||
isPayInAdvance: true
|
isPayInAdvance: true
|
||||||
}
|
}
|
||||||
|
|
@ -240,7 +273,19 @@ describe('cart-dropdown component', () => {
|
||||||
test('bottom amount due', () => {
|
test('bottom amount due', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const reference = '#bottom-amount-due';
|
const reference = '#bottom-amount-due';
|
||||||
const { wrapper } = getMountedComponent({}, {}, {});
|
const part1 = { partType: partTypeStrings.FRONT_WIPER };
|
||||||
|
const storeData = {
|
||||||
|
order: {
|
||||||
|
insuranceCoverage: {
|
||||||
|
coverageStatus: coverageStatuses.VERIFIED,
|
||||||
|
coverageType: coverageType.Deductible
|
||||||
|
},
|
||||||
|
lineItems: {
|
||||||
|
vaps: [part1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const { wrapper } = getMountedComponent(storeData, {}, {});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const bottomAmountDue = wrapper.find(reference);
|
const bottomAmountDue = wrapper.find(reference);
|
||||||
|
|
@ -293,27 +338,6 @@ describe('cart-dropdown component', () => {
|
||||||
// Assert
|
// Assert
|
||||||
expect(cartBasePrice.exists()).toBeFalsy();
|
expect(cartBasePrice.exists()).toBeFalsy();
|
||||||
});
|
});
|
||||||
test('recycle cart item label when isRecycle false for all cart items', () => {
|
|
||||||
// Arrange
|
|
||||||
const reference = '#recycle-fee-label';
|
|
||||||
const isExpanded = true;
|
|
||||||
const initialPropsData = { isExpanded };
|
|
||||||
const storeData = {
|
|
||||||
order: {
|
|
||||||
lineItems: {
|
|
||||||
supportingItems: []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const { wrapper } = getMountedComponent(storeData, {}, initialPropsData);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const recycleFeeLabel = wrapper.find(reference);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.recycleFeeCartItem).toBeNull();
|
|
||||||
expect(recycleFeeLabel.exists()).toBeFalsy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
describe('computed', () => {
|
describe('computed', () => {
|
||||||
describe('amountDue', () => {
|
describe('amountDue', () => {
|
||||||
|
|
@ -475,7 +499,7 @@ describe('cart-dropdown component', () => {
|
||||||
expect(result.sort(sortByPartType)).toStrictEqual(expected.sort(sortByPartType));
|
expect(result.sort(sortByPartType)).toStrictEqual(expected.sort(sortByPartType));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('servicePackageCartItems', () => {
|
describe('cartItems', () => {
|
||||||
test.each([
|
test.each([
|
||||||
[undefined], [null], [[]]
|
[undefined], [null], [[]]
|
||||||
])('returns empty list when package contents %p', (packageContents) => {
|
])('returns empty list when package contents %p', (packageContents) => {
|
||||||
|
|
@ -485,7 +509,7 @@ describe('cart-dropdown component', () => {
|
||||||
const expected = [];
|
const expected = [];
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = wrapper.vm.servicePackageCartItems;
|
const result = wrapper.vm.cartItems;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).toStrictEqual(expected);
|
expect(result).toStrictEqual(expected);
|
||||||
|
|
@ -531,7 +555,7 @@ describe('cart-dropdown component', () => {
|
||||||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
const wrapper = shallowMount(cartDropdown, mountOptions);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = wrapper.vm.servicePackageCartItems;
|
const result = wrapper.vm.cartItems;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result.length).toBe(1);
|
expect(result.length).toBe(1);
|
||||||
|
|
@ -577,7 +601,7 @@ describe('cart-dropdown component', () => {
|
||||||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
const wrapper = shallowMount(cartDropdown, mountOptions);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = wrapper.vm.servicePackageCartItems;
|
const result = wrapper.vm.cartItems;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result.length).toBe(1);
|
expect(result.length).toBe(1);
|
||||||
|
|
@ -623,7 +647,7 @@ describe('cart-dropdown component', () => {
|
||||||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
const wrapper = shallowMount(cartDropdown, mountOptions);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = wrapper.vm.servicePackageCartItems;
|
const result = wrapper.vm.cartItems;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result.length).toBe(1);
|
expect(result.length).toBe(1);
|
||||||
|
|
@ -685,7 +709,7 @@ describe('cart-dropdown component', () => {
|
||||||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
const wrapper = shallowMount(cartDropdown, mountOptions);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = wrapper.vm.servicePackageCartItems;
|
const result = wrapper.vm.cartItems;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result.length).toBe(3);
|
expect(result.length).toBe(3);
|
||||||
|
|
@ -699,8 +723,6 @@ describe('cart-dropdown component', () => {
|
||||||
name: expectedRainDefenseName
|
name: expectedRainDefenseName
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
});
|
|
||||||
describe('nonServicePackageCartItems', () => {
|
|
||||||
test('when recycleFee, includes recycle fee', () => {
|
test('when recycleFee, includes recycle fee', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const storeData = {
|
const storeData = {
|
||||||
|
|
@ -717,7 +739,9 @@ describe('cart-dropdown component', () => {
|
||||||
const { wrapper } = getMountedComponent(storeData);
|
const { wrapper } = getMountedComponent(storeData);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = wrapper.vm.nonServicePackageCartItems;
|
const result = wrapper.vm.cartItems;
|
||||||
|
|
||||||
|
console.log(result);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).toContainEqual(expect.objectContaining({
|
expect(result).toContainEqual(expect.objectContaining({
|
||||||
|
|
@ -736,163 +760,13 @@ describe('cart-dropdown component', () => {
|
||||||
const { wrapper } = getMountedComponent(storeData);
|
const { wrapper } = getMountedComponent(storeData);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = wrapper.vm.nonServicePackageCartItems;
|
const result = wrapper.vm.cartItems;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).toContainEqual(expect.objectContaining({
|
expect(result).toContainEqual(expect.objectContaining({
|
||||||
partType: partTypeStrings.MOBILE_FEE
|
partType: partTypeStrings.MOBILE_FEE
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
test('front wiper not included when front wiper in service package', () => {
|
|
||||||
// Arrange
|
|
||||||
getPriceOfLineItems.mockImplementation(() => 123);
|
|
||||||
getPackageContents.mockImplementationOnce(() => [partTypeStrings.FRONT_WIPER]);
|
|
||||||
const storeData = {
|
|
||||||
order: {
|
|
||||||
lineItems: {
|
|
||||||
vaps: [
|
|
||||||
{
|
|
||||||
partType: partTypeStrings.FRONT_WIPER
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const { wrapper } = getMountedComponent(storeData);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const result = wrapper.vm.nonServicePackageCartItems;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).not.toContainEqual(expect.objectContaining({
|
|
||||||
partType: partTypeStrings.FRONT_WIPER
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
test('rear wiper not included when rear wiper in service package', () => {
|
|
||||||
// Arrange
|
|
||||||
getPriceOfLineItems.mockImplementation(() => 123);
|
|
||||||
getPackageContents.mockImplementationOnce(() => [partTypeStrings.REAR_WIPER]);
|
|
||||||
const storeData = {
|
|
||||||
order: {
|
|
||||||
lineItems: {
|
|
||||||
vaps: [
|
|
||||||
{
|
|
||||||
partType: partTypeStrings.REAR_WIPER
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const { wrapper } = getMountedComponent(storeData);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const result = wrapper.vm.nonServicePackageCartItems;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).not.toContainEqual(expect.objectContaining({
|
|
||||||
partType: partTypeStrings.REAR_WIPER
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
test('rain defense not included when rain defense in service package', () => {
|
|
||||||
// Arrange
|
|
||||||
getPriceOfLineItems.mockImplementation(() => 123);
|
|
||||||
getPackageContents.mockImplementationOnce(() => [partTypeStrings.RAIN_DEFENSE]);
|
|
||||||
const storeData = {
|
|
||||||
order: {
|
|
||||||
lineItems: {
|
|
||||||
vaps: [
|
|
||||||
{
|
|
||||||
partType: partTypeStrings.RAIN_DEFENSE
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const { wrapper } = getMountedComponent(storeData);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const result = wrapper.vm.nonServicePackageCartItems;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).not.toContainEqual(expect.objectContaining({
|
|
||||||
partType: partTypeStrings.RAIN_DEFENSE
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
test('front wiper included when not in service package and in vaps', () => {
|
|
||||||
// Arrange
|
|
||||||
getPriceOfLineItems.mockImplementation(() => 123);
|
|
||||||
getPackageContents.mockImplementationOnce(() => []);
|
|
||||||
const storeData = {
|
|
||||||
order: {
|
|
||||||
lineItems: {
|
|
||||||
vaps: [
|
|
||||||
{
|
|
||||||
partType: partTypeStrings.FRONT_WIPER
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const { wrapper } = getMountedComponent(storeData);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const result = wrapper.vm.nonServicePackageCartItems;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).toContainEqual(expect.objectContaining({
|
|
||||||
partType: partTypeStrings.FRONT_WIPER
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
test('rear wiper included when not in service package and in vaps', () => {
|
|
||||||
// Arrange
|
|
||||||
getPriceOfLineItems.mockImplementation(() => 123);
|
|
||||||
getPackageContents.mockImplementationOnce(() => []);
|
|
||||||
const storeData = {
|
|
||||||
order: {
|
|
||||||
lineItems: {
|
|
||||||
vaps: [
|
|
||||||
{
|
|
||||||
partType: partTypeStrings.REAR_WIPER
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const { wrapper } = getMountedComponent(storeData);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const result = wrapper.vm.nonServicePackageCartItems;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).toContainEqual(expect.objectContaining({
|
|
||||||
partType: partTypeStrings.REAR_WIPER
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
test('rain defense included when not in service package and in vaps', () => {
|
|
||||||
// Arrange
|
|
||||||
getPriceOfLineItems.mockImplementation(() => 123);
|
|
||||||
getPackageContents.mockImplementationOnce(() => []);
|
|
||||||
const storeData = {
|
|
||||||
order: {
|
|
||||||
lineItems: {
|
|
||||||
vaps: [
|
|
||||||
{
|
|
||||||
partType: partTypeStrings.RAIN_DEFENSE
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const { wrapper } = getMountedComponent(storeData);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const result = wrapper.vm.nonServicePackageCartItems;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).toContainEqual(expect.objectContaining({
|
|
||||||
partType: partTypeStrings.RAIN_DEFENSE
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
test('item in vaps outside front wiper, rear wiper, and rain defense never included', () => {
|
test('item in vaps outside front wiper, rear wiper, and rain defense never included', () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
getPriceOfLineItems.mockImplementation(() => 123);
|
getPriceOfLineItems.mockImplementation(() => 123);
|
||||||
|
|
@ -911,143 +785,13 @@ describe('cart-dropdown component', () => {
|
||||||
const { wrapper } = getMountedComponent(storeData);
|
const { wrapper } = getMountedComponent(storeData);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = wrapper.vm.nonServicePackageCartItems;
|
const result = wrapper.vm.cartItems;
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).not.toContainEqual(expect.objectContaining({
|
expect(result).not.toContainEqual(expect.objectContaining({
|
||||||
partType: partTypeStrings.RECALIBRATION
|
partType: partTypeStrings.RECALIBRATION
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
test('returns expected when multiple variables apply', () => {
|
|
||||||
// Arrange
|
|
||||||
getPriceOfLineItems.mockImplementation(() => 123);
|
|
||||||
getPackageContents.mockImplementationOnce(() => [partTypeStrings.FRONT_WIPER]);
|
|
||||||
const storeData = {
|
|
||||||
order: {
|
|
||||||
lineItems: {
|
|
||||||
feeItems: [
|
|
||||||
{ partType: partTypeStrings.MOBILE_FEE },
|
|
||||||
{ partNumber: partNumberStrings.RECYCLE_FEE }
|
|
||||||
],
|
|
||||||
vaps: [
|
|
||||||
{ partType: partTypeStrings.RECALIBRATION },
|
|
||||||
{ partType: partTypeStrings.FRONT_WIPER },
|
|
||||||
{ partType: partTypeStrings.REAR_WIPER }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const { wrapper } = getMountedComponent(storeData);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const result = wrapper.vm.nonServicePackageCartItems;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result.length).toBe(3);
|
|
||||||
expect(result).toContainEqual(expect.objectContaining({
|
|
||||||
partType: partTypeStrings.REPLACE_FEE
|
|
||||||
}));
|
|
||||||
expect(result).toContainEqual(expect.objectContaining({
|
|
||||||
partType: partTypeStrings.MOBILE_FEE
|
|
||||||
}));
|
|
||||||
expect(result).toContainEqual(expect.objectContaining({
|
|
||||||
partType: partTypeStrings.REAR_WIPER
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
describe('servicePackageLabelWidget', () => {
|
|
||||||
test.each([
|
|
||||||
[null],
|
|
||||||
[undefined]
|
|
||||||
])('returns "" when servicePackageNames %p', (servicePackageNames) => {
|
|
||||||
// Arrange
|
|
||||||
const mountOptions = getMountOptions({
|
|
||||||
router: { navigate: jest.fn() }
|
|
||||||
});
|
|
||||||
const mockMixin = {
|
|
||||||
methods: {
|
|
||||||
getCmsContent: jest.fn(() => servicePackageNames)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
mountOptions.mixins = [mockMixin];
|
|
||||||
|
|
||||||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const result = wrapper.vm.servicePackageLabelWidget;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).toBe('');
|
|
||||||
});
|
|
||||||
test('returns "" when servicePackageTier not found in servicePackageNames', () => {
|
|
||||||
// Arrange
|
|
||||||
const mountOptions = getMountOptions({
|
|
||||||
router: { navigate: jest.fn() }
|
|
||||||
});
|
|
||||||
getHighestFullySatisfiedTier.mockImplementationOnce(() => 'some other tier');
|
|
||||||
const servicePackageNames = [{ Name: 'some tier' }];
|
|
||||||
const mockMixin = {
|
|
||||||
methods: {
|
|
||||||
getCmsContent: jest.fn(() => servicePackageNames)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
mountOptions.mixins = [mockMixin];
|
|
||||||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const result = wrapper.vm.servicePackageLabelWidget;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).toBe('');
|
|
||||||
});
|
|
||||||
test('returns "" when servicePackageTier has no SubWidgetName', () => {
|
|
||||||
// Arrange
|
|
||||||
const mountOptions = getMountOptions({
|
|
||||||
router: { navigate: jest.fn() }
|
|
||||||
});
|
|
||||||
const tier = 'tier1';
|
|
||||||
getHighestFullySatisfiedTier.mockImplementationOnce(() => tier);
|
|
||||||
const servicePackageNames = [{ Name: tier }];
|
|
||||||
const mockMixin = {
|
|
||||||
methods: {
|
|
||||||
getCmsContent: jest.fn(() => servicePackageNames)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
mountOptions.mixins = [mockMixin];
|
|
||||||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const result = wrapper.vm.servicePackageLabelWidget;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).toBe('');
|
|
||||||
});
|
|
||||||
test('returns expected when servicePackageTier has SubWidgetName', () => {
|
|
||||||
// Arrange
|
|
||||||
const mountOptions = getMountOptions({
|
|
||||||
router: { navigate: jest.fn() }
|
|
||||||
});
|
|
||||||
const tier = 'tier1';
|
|
||||||
getHighestFullySatisfiedTier.mockImplementationOnce(() => tier);
|
|
||||||
const expected = 'name of sub widget';
|
|
||||||
const servicePackageNames = [{
|
|
||||||
Name: tier,
|
|
||||||
SubWidgetName: expected
|
|
||||||
}];
|
|
||||||
const mockMixin = {
|
|
||||||
methods: {
|
|
||||||
getCmsContent: jest.fn(() => servicePackageNames)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
mountOptions.mixins = [mockMixin];
|
|
||||||
const wrapper = shallowMount(cartDropdown, mountOptions);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const result = wrapper.vm.servicePackageLabelWidget;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).toBe(expected);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
describe('recycleFeeCartItem', () => {
|
describe('recycleFeeCartItem', () => {
|
||||||
test.each([
|
test.each([
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1,39 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="cart-table">
|
<div
|
||||||
|
v-if="!isDeductibleOnly"
|
||||||
|
class="cart-table">
|
||||||
<div class="cart-item-list">
|
<div class="cart-item-list">
|
||||||
<div
|
<div
|
||||||
id="cart-deductible-or-base-price"
|
v-if="showDeductibleCartItem"
|
||||||
class="cart-item">
|
class="cart-item">
|
||||||
<div class="price-row">
|
<div class="price-row">
|
||||||
<template
|
<span id="deductible-label">{{ deductibleLabel }}</span>
|
||||||
v-if="showDeductibleCartItem">
|
<span id="deductible-value">{{ getDisplayed(deductible) }}</span>
|
||||||
<span id="deductible-label">{{ deductibleLabel }}</span>
|
</div>
|
||||||
<span id="deductible-value">{{ getDisplayed(deductible) }}</span>
|
</div>
|
||||||
</template>
|
<div
|
||||||
<template
|
v-else
|
||||||
v-else>
|
class="cart-item">
|
||||||
<span id="base-price-label">{{ basePriceLabel }}</span>
|
<div class="price-row">
|
||||||
<span id="base-price-value">{{ getDisplayed(servicePrice) }}</span>
|
<span id="base-price-label">{{ basePriceLabel }}</span>
|
||||||
</template>
|
<span id="base-price-value">{{ getDisplayed(servicePrice) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="showRecalibrationCartItem"
|
||||||
|
class="cart-item">
|
||||||
|
<div class="price-row">
|
||||||
|
<span id="recalibration-label">{{ recalibrationLabel }}</span>
|
||||||
|
<span id="recalibration-value">{{ formatAmountInDollars(recalibrationPrice) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- NonPackaged Cart Items -->
|
|
||||||
<div
|
<div
|
||||||
v-for="(item, i) in nonServicePackageCartItems"
|
v-for="(item, i) in cartItems"
|
||||||
:key="i"
|
:key="i"
|
||||||
class="cart-item non-packaged">
|
class="cart-item packaged">
|
||||||
<div class="price-row">
|
<div class="price-row">
|
||||||
<span
|
<span>{{ item?.name ?? '' }}</span>
|
||||||
v-if="item.cartItemType === cartItemType.RECYCLE_FEE"
|
|
||||||
id="recycle-fee-label">
|
|
||||||
<textLink
|
|
||||||
linkType="text"
|
|
||||||
:text="item?.name ?? ''"
|
|
||||||
href="javascript:void(0)"
|
|
||||||
@clickEvent="openModal(RECYCLING_MODAL_REF_NAME)" />
|
|
||||||
</span>
|
|
||||||
<span v-else>{{ item?.name ?? '' }}</span>
|
|
||||||
<span>{{ formatAmountInDollars(item?.subTotal ?? 0) }}</span>
|
<span>{{ formatAmountInDollars(item?.subTotal ?? 0) }}</span>
|
||||||
</div>
|
</div>
|
||||||
<textLink
|
<textLink
|
||||||
|
|
@ -48,32 +48,12 @@
|
||||||
</template>
|
</template>
|
||||||
</textLink>
|
</textLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Packaged Cart Items -->
|
|
||||||
<div
|
|
||||||
v-for="(item, i) in servicePackageCartItems"
|
|
||||||
:key="i"
|
|
||||||
class="cart-item packaged">
|
|
||||||
<div class="price-row">
|
|
||||||
<span>{{ item?.name ?? '' }}</span>
|
|
||||||
<span>{{ formatAmountInDollars(item?.subTotal ?? 0) }}</span>
|
|
||||||
</div>
|
|
||||||
<textLink
|
|
||||||
v-if="!readOnly"
|
|
||||||
linkType="text"
|
|
||||||
text="Remove"
|
|
||||||
href="javascript:void(0)"
|
|
||||||
@clickEvent="removeVap(item.partType)">
|
|
||||||
<template #after-text>
|
|
||||||
<span class="sr-only">{{ item?.name ?? '' }}</span>
|
|
||||||
</template>
|
|
||||||
</textLink>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
id="cart-footer"
|
id="cart-footer"
|
||||||
class="cart-footer pb-5">
|
class="cart-footer">
|
||||||
<div
|
<div
|
||||||
|
v-if="!isUnverified"
|
||||||
id="cart-subtotal-and-tax"
|
id="cart-subtotal-and-tax"
|
||||||
class="subtotal-and-tax">
|
class="subtotal-and-tax">
|
||||||
<div
|
<div
|
||||||
|
|
@ -103,11 +83,10 @@
|
||||||
<span id="bottom-amount-due-value">{{ getDisplayed(amountDue) }}</span>
|
<span id="bottom-amount-due-value">{{ getDisplayed(amountDue) }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<contentGroupModal
|
</div>
|
||||||
:ref="RECYCLING_MODAL_REF_NAME"
|
<div v-else class="deductible-box">
|
||||||
:cmsWidgetName="recyclingModalCmsWidgetName">
|
<span class="deductible-label">{{ deductibleLabel }}:</span>
|
||||||
<textBlock cmsWidgetName="RecycleTextBlock" />
|
<span class="deductible-value">{{ getDisplayed(deductible) }}</span>
|
||||||
</contentGroupModal>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -117,7 +96,6 @@ import contentGroupModal from '@/iss-components/content-group-modal/content-grou
|
||||||
import textBlock from '@/digital-components/text-block/text-block.vue';
|
import textBlock from '@/digital-components/text-block/text-block.vue';
|
||||||
import textLink from '@/ux-components/text-link/text-link.vue';
|
import textLink from '@/ux-components/text-link/text-link.vue';
|
||||||
import { formatAmountInDollars } from '@/helpers/text-helper.js';
|
import { formatAmountInDollars } from '@/helpers/text-helper.js';
|
||||||
import { getHighestFullySatisfiedTier, getPackageContents } from '@/helpers/service-package-helper.js';
|
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
import { experimentSettings } from '@/constants/experiments';
|
import { experimentSettings } from '@/constants/experiments';
|
||||||
|
|
@ -128,6 +106,7 @@ import {
|
||||||
getCartTotal,
|
getCartTotal,
|
||||||
getDeductible,
|
getDeductible,
|
||||||
getLineItems, getMobileFeeLineItem,
|
getLineItems, getMobileFeeLineItem,
|
||||||
|
getRecalibrationTotal,
|
||||||
getRecycleFeeLineItem, getSalesTax,
|
getRecycleFeeLineItem, getSalesTax,
|
||||||
getServiceLineItems, getSubtotal, isOrderITAC, isOrderNoComp,
|
getServiceLineItems, getSubtotal, isOrderITAC, isOrderNoComp,
|
||||||
isOrderUnverified
|
isOrderUnverified
|
||||||
|
|
@ -136,19 +115,15 @@ import { getPriceOfLineItems, getTaxOfLineItems } from '@/helpers/price-calculat
|
||||||
import { processIfStatements } from '@/helpers/cms-content-helper';
|
import { processIfStatements } from '@/helpers/cms-content-helper';
|
||||||
|
|
||||||
const VERIFYING_COVERAGE = 'Verifying coverage';
|
const VERIFYING_COVERAGE = 'Verifying coverage';
|
||||||
const RECYCLING_MODAL_REF_NAME = 'RecycleModal';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'confirmation-cart',
|
name: 'confirmation-cart',
|
||||||
components: {
|
components: {
|
||||||
contentGroupModal,
|
|
||||||
textBlock,
|
|
||||||
textLink
|
textLink
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
readOnly: Boolean,
|
readOnly: Boolean,
|
||||||
showAsPaid: Boolean,
|
showAsPaid: Boolean,
|
||||||
recyclingModalCmsWidgetName: String,
|
|
||||||
submittedOrder: Object
|
submittedOrder: Object
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -158,6 +133,7 @@ export default {
|
||||||
amountPaid: 'AmountPaidTextWidget',
|
amountPaid: 'AmountPaidTextWidget',
|
||||||
deductible: 'DeductibleWidget',
|
deductible: 'DeductibleWidget',
|
||||||
basePrice: 'BasePriceWidget',
|
basePrice: 'BasePriceWidget',
|
||||||
|
recalibration: 'RecalibrationWidget',
|
||||||
subtotal: 'SubtotalWidget',
|
subtotal: 'SubtotalWidget',
|
||||||
salesTax: 'SalesTaxWidget',
|
salesTax: 'SalesTaxWidget',
|
||||||
recycleFee: 'RecycleFeeWidget',
|
recycleFee: 'RecycleFeeWidget',
|
||||||
|
|
@ -167,8 +143,7 @@ export default {
|
||||||
warrantyText: 'WarrantyCartItemTextWidget',
|
warrantyText: 'WarrantyCartItemTextWidget',
|
||||||
guaranteeText: 'GuaranteeCartItemTextWidget'
|
guaranteeText: 'GuaranteeCartItemTextWidget'
|
||||||
},
|
},
|
||||||
cartItemType,
|
cartItemType
|
||||||
RECYCLING_MODAL_REF_NAME
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -181,9 +156,18 @@ export default {
|
||||||
deductible() {
|
deductible() {
|
||||||
return getDeductible(this.cartOrder);
|
return getDeductible(this.cartOrder);
|
||||||
},
|
},
|
||||||
|
isDeductibleOnly() {
|
||||||
|
return this.cartItems.length === 0;
|
||||||
|
},
|
||||||
showDeductibleCartItem() {
|
showDeductibleCartItem() {
|
||||||
return this.isUnverified || (!this.isNoComp && !this.isITAC);
|
return this.isUnverified || (!this.isNoComp && !this.isITAC);
|
||||||
},
|
},
|
||||||
|
showRecalibrationCartItem() {
|
||||||
|
return this.recalibrationPrice > 0;
|
||||||
|
},
|
||||||
|
recalibrationPrice() {
|
||||||
|
return getRecalibrationTotal(this.cartOrder);
|
||||||
|
},
|
||||||
lineItems() {
|
lineItems() {
|
||||||
return getLineItems(this.cartOrder);
|
return getLineItems(this.cartOrder);
|
||||||
},
|
},
|
||||||
|
|
@ -191,7 +175,8 @@ export default {
|
||||||
return getRecycleFeeLineItem(this.cartOrder);
|
return getRecycleFeeLineItem(this.cartOrder);
|
||||||
},
|
},
|
||||||
servicePrice() {
|
servicePrice() {
|
||||||
return getPriceOfLineItems(getServiceLineItems(this.cartOrder)) ?? 0;
|
const price = getPriceOfLineItems(getServiceLineItems(this.cartOrder)) ?? 0;
|
||||||
|
return price - this.recalibrationPrice;
|
||||||
},
|
},
|
||||||
isUnverified() {
|
isUnverified() {
|
||||||
return isOrderUnverified(this.cartOrder);
|
return isOrderUnverified(this.cartOrder);
|
||||||
|
|
@ -229,47 +214,21 @@ export default {
|
||||||
];
|
];
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
vehicleDamage() {
|
cartItems() {
|
||||||
return this.cartOrder.damage;
|
|
||||||
},
|
|
||||||
servicePackageTier() {
|
|
||||||
const { glassToReplace, isRepair } = this.vehicleDamage;
|
|
||||||
const { vaps } = this.lineItems;
|
|
||||||
return getHighestFullySatisfiedTier(
|
|
||||||
glassToReplace ?? [],
|
|
||||||
this.availableLineItems,
|
|
||||||
isRepair,
|
|
||||||
vaps ?? []
|
|
||||||
);
|
|
||||||
},
|
|
||||||
partTypesInServicePackage() {
|
|
||||||
const { glassToReplace, isRepair } = this.vehicleDamage;
|
|
||||||
return getPackageContents(
|
|
||||||
glassToReplace ?? [],
|
|
||||||
this.availableLineItems,
|
|
||||||
isRepair,
|
|
||||||
this.servicePackageTier
|
|
||||||
) ?? [];
|
|
||||||
},
|
|
||||||
servicePackageCartItems() {
|
|
||||||
const items = [];
|
const items = [];
|
||||||
if (this.partTypesInServicePackage.includes(partTypeStrings.FRONT_WIPER) ?? this.frontWipersCartItem) {
|
const isRecycleFeeHidden = this.getSettingValue(experimentSettings.ISS_FEATURE_TOGGLE_IS_RECYCLE_FEE_HIDDEN) === 'true';
|
||||||
items.push(this.frontWipersCartItem);
|
const isMobileFeeHidden = this.getSettingValue(experimentSettings.ISS_FEATURE_TOGGLE_IS_MOBILE_FEE_HIDDEN) === 'true';
|
||||||
|
if (this.isITAC || this.isNoComp) {
|
||||||
|
items.push(this.warrantyCartItem);
|
||||||
}
|
}
|
||||||
if (this.partTypesInServicePackage.includes(partTypeStrings.REAR_WIPER) ?? this.rearWipersCartItem) {
|
if (this.recycleFeeCartItem && !isRecycleFeeHidden) {
|
||||||
items.push(this.rearWipersCartItem);
|
items.push(this.recycleFeeCartItem);
|
||||||
}
|
}
|
||||||
if (this.partTypesInServicePackage.includes(partTypeStrings.RAIN_DEFENSE) ?? this.rainDefenseCartItem) {
|
if (this.mobileFeeCartItem && !isMobileFeeHidden) {
|
||||||
items.push(this.rainDefenseCartItem);
|
items.push(this.mobileFeeCartItem);
|
||||||
}
|
}
|
||||||
return items;
|
|
||||||
},
|
|
||||||
nonServicePackageCartItems() {
|
|
||||||
const vapPartTypesInOrder = Array.from(new Set(this.lineItems.vaps?.map((vap) => vap.partType) ?? []));
|
const vapPartTypesInOrder = Array.from(new Set(this.lineItems.vaps?.map((vap) => vap.partType) ?? []));
|
||||||
const vapPartTypesInOrderButNotPackage = vapPartTypesInOrder
|
const vapsCartItems = vapPartTypesInOrder.map((partType) => {
|
||||||
.filter((partType) => !this.partTypesInServicePackage.includes(partType))
|
|
||||||
?? [];
|
|
||||||
const vapsCartItemsNotInPackage = vapPartTypesInOrderButNotPackage.map((partType) => {
|
|
||||||
switch (partType) {
|
switch (partType) {
|
||||||
case partTypeStrings.FRONT_WIPER:
|
case partTypeStrings.FRONT_WIPER:
|
||||||
return this.frontWipersCartItem;
|
return this.frontWipersCartItem;
|
||||||
|
|
@ -281,18 +240,7 @@ export default {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const items = vapsCartItemsNotInPackage.filter((item) => item != null);
|
items.push(...vapsCartItems.filter((item) => item != null));
|
||||||
const isRecycleFeeHidden = this.getSettingValue(experimentSettings.ISS_FEATURE_TOGGLE_IS_RECYCLE_FEE_HIDDEN) === 'true';
|
|
||||||
const isMobileFeeHidden = this.getSettingValue(experimentSettings.ISS_FEATURE_TOGGLE_IS_MOBILE_FEE_HIDDEN) === 'true';
|
|
||||||
if (this.recycleFeeCartItem && !isRecycleFeeHidden) {
|
|
||||||
items.push(this.recycleFeeCartItem);
|
|
||||||
}
|
|
||||||
if (this.mobileFeeCartItem && !isMobileFeeHidden) {
|
|
||||||
items.push(this.mobileFeeCartItem);
|
|
||||||
}
|
|
||||||
if (this.isITAC || this.isNoComp) {
|
|
||||||
items.push(this.warrantyCartItem);
|
|
||||||
}
|
|
||||||
return items;
|
return items;
|
||||||
},
|
},
|
||||||
frontWipersCartItem() {
|
frontWipersCartItem() {
|
||||||
|
|
@ -304,12 +252,6 @@ export default {
|
||||||
rainDefenseCartItem() {
|
rainDefenseCartItem() {
|
||||||
return this.getCartItemForVapsPart(partTypeStrings.RAIN_DEFENSE);
|
return this.getCartItemForVapsPart(partTypeStrings.RAIN_DEFENSE);
|
||||||
},
|
},
|
||||||
allCartItems() {
|
|
||||||
return [
|
|
||||||
...this.servicePackageCartItems,
|
|
||||||
...this.nonServicePackageCartItems
|
|
||||||
];
|
|
||||||
},
|
|
||||||
amountDueLabel() {
|
amountDueLabel() {
|
||||||
return this.getCmsContent(this.widget.amountDue, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
return this.getCmsContent(this.widget.amountDue, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||||
},
|
},
|
||||||
|
|
@ -327,27 +269,15 @@ export default {
|
||||||
this.getCustomValueFromString
|
this.getCustomValueFromString
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
recalibrationLabel() {
|
||||||
|
return this.getCmsContent(this.widget.recalibration, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||||
|
},
|
||||||
subtotalLabel() {
|
subtotalLabel() {
|
||||||
return this.getCmsContent(this.widget.subtotal, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
return this.getCmsContent(this.widget.subtotal, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||||
},
|
},
|
||||||
salesTaxLabel() {
|
salesTaxLabel() {
|
||||||
return this.getCmsContent(this.widget.salesTax, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
return this.getCmsContent(this.widget.salesTax, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||||
},
|
},
|
||||||
servicePackageNames() {
|
|
||||||
return this.getCmsContent(
|
|
||||||
this.widget.servicePackage,
|
|
||||||
widgetFields.INPUT_QUESTION_WIDGET.ANSWERS
|
|
||||||
);
|
|
||||||
},
|
|
||||||
servicePackageLabelWidget() {
|
|
||||||
if (!this.servicePackageNames) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
const currentPackage = this.servicePackageNames
|
|
||||||
?.find((entry) => entry?.Name === this.servicePackageTier);
|
|
||||||
|
|
||||||
return currentPackage?.SubWidgetName ?? '';
|
|
||||||
},
|
|
||||||
recycleFeeCartItem() {
|
recycleFeeCartItem() {
|
||||||
return this.recycleFeeLineItem
|
return this.recycleFeeLineItem
|
||||||
? this.getCartItem(
|
? this.getCartItem(
|
||||||
|
|
@ -452,8 +382,6 @@ export default {
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "@/styles/ux-variables-svg-strings.scss";
|
@import "@/styles/ux-variables-svg-strings.scss";
|
||||||
|
|
||||||
$RECYCLING_MODAL_REF_NAME: 'RecycleModal';
|
|
||||||
|
|
||||||
.cart-table {
|
.cart-table {
|
||||||
max-height: 50rem;
|
max-height: 50rem;
|
||||||
transition: all 150ms ease-in;
|
transition: all 150ms ease-in;
|
||||||
|
|
@ -508,22 +436,18 @@ $RECYCLING_MODAL_REF_NAME: 'RecycleModal';
|
||||||
padding: .5rem 1rem;
|
padding: .5rem 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.deductible-box {
|
||||||
:deep(.modal-content a.external-text) {
|
display: flex;
|
||||||
text-underline-offset: 0.25rem;
|
align-items: center;
|
||||||
line-height: 2;
|
gap: .25rem;
|
||||||
padding: 0 0 0.25rem 0;
|
|
||||||
font-weight: 500;
|
|
||||||
max-width: -webkit-fit-content;
|
|
||||||
max-width: -moz-fit-content;
|
|
||||||
max-width: fit-content;
|
|
||||||
}
|
}
|
||||||
|
.deductible-label {
|
||||||
:deep(##{$RECYCLING_MODAL_REF_NAME} h5) {
|
color: $black;
|
||||||
text-align: center;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
.deductible-value {
|
||||||
:deep(#recycle-fee-label a){
|
color: $green;
|
||||||
line-height: initial;
|
font-weight: $font-weight-bold;
|
||||||
|
font-size: 1.625rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
exports[`contact-details-drawer snapshot matches returns the initial data 1`] = `
|
exports[`contact-details-drawer snapshot matches returns the initial data 1`] = `
|
||||||
Object {
|
Object {
|
||||||
|
"detailsSaved": false,
|
||||||
"emailAddress": "fred.tay@gmail.com",
|
"emailAddress": "fred.tay@gmail.com",
|
||||||
"extension": null,
|
"extension": null,
|
||||||
"firstName": "Frederick",
|
"firstName": "Frederick",
|
||||||
|
|
@ -15,8 +16,8 @@ Object {
|
||||||
"rules": Object {
|
"rules": Object {
|
||||||
"emailAddress": "email-required|email-address-format",
|
"emailAddress": "email-required|email-address-format",
|
||||||
"extension": "extension-format",
|
"extension": "extension-format",
|
||||||
"firstName": "first-name-required",
|
"firstName": "policyholder-first-name-required",
|
||||||
"lastName": "last-name-required",
|
"lastName": "policyholder-last-name-required",
|
||||||
"phoneNumber": "phone-number-required|phone-number-format",
|
"phoneNumber": "phone-number-required|phone-number-format",
|
||||||
},
|
},
|
||||||
"widget": Object {
|
"widget": Object {
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// Components
|
// Components
|
||||||
import { shallowMount } from '@vue/test-utils';
|
import { shallowMount } from '@vue/test-utils';
|
||||||
import { createTestingPinia } from '@pinia/testing';
|
import { createTestingPinia } from '@pinia/testing';
|
||||||
import contactDetailsDrawer from '@/layouts/tpa-submit/contact-details-drawer/contact-details-drawer.vue';
|
import contactDetailsDrawer from '@/iss-components/contact-details-drawer/contact-details-drawer.vue';
|
||||||
|
|
||||||
// Supporting Files
|
// Supporting Files
|
||||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||||
|
|
@ -9,25 +9,29 @@
|
||||||
@isModalOpened="setModalStatus"
|
@isModalOpened="setModalStatus"
|
||||||
@footerButtonEvent="clickFooterButtonEvent">
|
@footerButtonEvent="clickFooterButtonEvent">
|
||||||
<template v-if="isModalOpened">
|
<template v-if="isModalOpened">
|
||||||
<div class="row">
|
<span class="name-section-label">{{ nameSectionLabel }}</span>
|
||||||
|
<div class="row form-group">
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
class="col"
|
class="col-lg-6"
|
||||||
ref="firstNameQuestion"
|
ref="firstNameQuestion"
|
||||||
v-model="firstName"
|
v-model="firstName"
|
||||||
inputId="firstName"
|
inputId="firstName"
|
||||||
|
:displayQuestionText="false"
|
||||||
:cmsWidgetName="widget.firstNameQuestion"
|
:cmsWidgetName="widget.firstNameQuestion"
|
||||||
isRequired
|
isRequired
|
||||||
:validationRules="rules.firstName" />
|
:validationRules="rules.firstName" />
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
class="col"
|
class="col-lg-6 last-name-question"
|
||||||
ref="lastNameQuestion"
|
ref="lastNameQuestion"
|
||||||
v-model="lastName"
|
v-model="lastName"
|
||||||
inputId="lastName"
|
inputId="lastName"
|
||||||
|
:displayQuestionText="false"
|
||||||
:cmsWidgetName="widget.lastNameQuestion"
|
:cmsWidgetName="widget.lastNameQuestion"
|
||||||
isRequired
|
isRequired
|
||||||
:validationRules="rules.lastName" />
|
:validationRules="rules.lastName" />
|
||||||
</div>
|
</div>
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
|
class="form-group"
|
||||||
ref="emailQuestion"
|
ref="emailQuestion"
|
||||||
v-model="emailAddress"
|
v-model="emailAddress"
|
||||||
inputId="emailAddress"
|
inputId="emailAddress"
|
||||||
|
|
@ -35,6 +39,7 @@
|
||||||
isRequired
|
isRequired
|
||||||
:validationRules="rules.emailAddress" />
|
:validationRules="rules.emailAddress" />
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
|
class="form-group"
|
||||||
ref="phoneNumberQuestion"
|
ref="phoneNumberQuestion"
|
||||||
v-model="phoneNumber"
|
v-model="phoneNumber"
|
||||||
inputId="phoneNumber"
|
inputId="phoneNumber"
|
||||||
|
|
@ -44,6 +49,7 @@
|
||||||
disableAutoFill
|
disableAutoFill
|
||||||
:validationRules="rules.phoneNumber" />
|
:validationRules="rules.phoneNumber" />
|
||||||
<textboxQuestion
|
<textboxQuestion
|
||||||
|
class="form-group"
|
||||||
ref="extensionQuestion"
|
ref="extensionQuestion"
|
||||||
v-model="extension"
|
v-model="extension"
|
||||||
inputId="extension"
|
inputId="extension"
|
||||||
|
|
@ -73,7 +79,7 @@ export default {
|
||||||
modal,
|
modal,
|
||||||
textboxQuestion
|
textboxQuestion
|
||||||
},
|
},
|
||||||
emits: ['update-contact-details'],
|
emits: ['update-contact-details', 'close-without-saving'],
|
||||||
data() {
|
data() {
|
||||||
const { firstName,
|
const { firstName,
|
||||||
lastName,
|
lastName,
|
||||||
|
|
@ -97,13 +103,14 @@ export default {
|
||||||
drawerFooter: 'ContactDetailsDrawerFooterWidget'
|
drawerFooter: 'ContactDetailsDrawerFooterWidget'
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
firstName: globalRules.FIRST_NAME_REQUIRED,
|
firstName: globalRules.POLICYHOLDER_FIRST_NAME_REQUIRED,
|
||||||
lastName: globalRules.LAST_NAME_REQUIRED,
|
lastName: globalRules.POLICYHOLDER_LAST_NAME_REQUIRED,
|
||||||
emailAddress: `${globalRules.EMAIL_ADDRESS_REQUIRED}|${globalRules.EMAIL_ADDRESS_FORMAT}`,
|
emailAddress: `${globalRules.EMAIL_ADDRESS_REQUIRED}|${globalRules.EMAIL_ADDRESS_FORMAT}`,
|
||||||
phoneNumber: `${globalRules.PHONE_NUMBER_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}`,
|
phoneNumber: `${globalRules.PHONE_NUMBER_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}`,
|
||||||
extension: `${globalRules.EXTENSION_FORMAT}`
|
extension: `${globalRules.EXTENSION_FORMAT}`
|
||||||
},
|
},
|
||||||
modalPositions
|
modalPositions,
|
||||||
|
detailsSaved: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -121,13 +128,17 @@ export default {
|
||||||
},
|
},
|
||||||
phoneMask() {
|
phoneMask() {
|
||||||
return MaskaFormattedMasks.PHONE_NUMBER;
|
return MaskaFormattedMasks.PHONE_NUMBER;
|
||||||
}
|
},
|
||||||
|
nameSectionLabel() {
|
||||||
|
return this.getCmsContent(this.widget.firstNameQuestion, widgetFields.INPUT_QUESTION_WIDGET.QUESTION_TEXT);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
setModalStatus(isOpened) {
|
setModalStatus(isOpened) {
|
||||||
this.isModalOpened = isOpened;
|
this.isModalOpened = isOpened;
|
||||||
},
|
},
|
||||||
clickFooterButtonEvent() {
|
clickFooterButtonEvent() {
|
||||||
|
this.detailsSaved = true;
|
||||||
this.saveContactDetails();
|
this.saveContactDetails();
|
||||||
this.closeModal();
|
this.closeModal();
|
||||||
},
|
},
|
||||||
|
|
@ -146,6 +157,9 @@ export default {
|
||||||
this.$emit('update-contact-details');
|
this.$emit('update-contact-details');
|
||||||
},
|
},
|
||||||
resetFormValues() {
|
resetFormValues() {
|
||||||
|
if (!this.detailsSaved) {
|
||||||
|
this.$emit('close-without-saving');
|
||||||
|
}
|
||||||
const { firstName,
|
const { firstName,
|
||||||
lastName,
|
lastName,
|
||||||
emailAddress,
|
emailAddress,
|
||||||
|
|
@ -156,6 +170,7 @@ export default {
|
||||||
this.emailAddress = emailAddress;
|
this.emailAddress = emailAddress;
|
||||||
this.phoneNumber = servicePhone;
|
this.phoneNumber = servicePhone;
|
||||||
this.extension = extension;
|
this.extension = extension;
|
||||||
|
this.detailsSaved = false;
|
||||||
},
|
},
|
||||||
openModal() {
|
openModal() {
|
||||||
this.modal.openModal();
|
this.modal.openModal();
|
||||||
|
|
@ -174,13 +189,18 @@ export default {
|
||||||
font-size: $h5-font-size;
|
font-size: $h5-font-size;
|
||||||
font-weight: $font-weight-normal !important;
|
font-weight: $font-weight-normal !important;
|
||||||
}
|
}
|
||||||
.textbox-question {
|
.form-group {
|
||||||
margin-bottom: 1.25rem;
|
margin-bottom: 1.25rem;
|
||||||
:deep(label:has(b)) {
|
align-items: start;
|
||||||
font-weight: $font-weight-normal;
|
}
|
||||||
b {
|
.name-section-label {
|
||||||
font-weight: 600;
|
color: $black;
|
||||||
}
|
font-weight: 600;
|
||||||
|
margin-bottom: .25rem;
|
||||||
|
}
|
||||||
|
.last-name-question {
|
||||||
|
@include media-breakpoint-down(lg) {
|
||||||
|
margin-top: .625rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -94,6 +94,7 @@
|
||||||
</div>
|
</div>
|
||||||
<cartDropdown
|
<cartDropdown
|
||||||
v-if="showCart"
|
v-if="showCart"
|
||||||
|
class="cart-dropdown"
|
||||||
:showAsPaid="payment.isPayInAdvance"
|
:showAsPaid="payment.isPayInAdvance"
|
||||||
:readOnly="true"
|
:readOnly="true"
|
||||||
recyclingModalCmsWidgetName="RecycleModal"
|
recyclingModalCmsWidgetName="RecycleModal"
|
||||||
|
|
@ -230,7 +231,7 @@ export default {
|
||||||
address: this.appointmentLocation,
|
address: this.appointmentLocation,
|
||||||
inShopDuration: this.inShopAppointmentDuration,
|
inShopDuration: this.inShopAppointmentDuration,
|
||||||
email: this.customerEmail,
|
email: this.customerEmail,
|
||||||
phone: this.contactInfo.servicePhone,
|
phone: this.contactPhone,
|
||||||
workOrderNumber: this.workOrderNumber,
|
workOrderNumber: this.workOrderNumber,
|
||||||
CUSTOMER_PORTAL_URL: applicationConfig.CUSTOMER_PORTAL_URL,
|
CUSTOMER_PORTAL_URL: applicationConfig.CUSTOMER_PORTAL_URL,
|
||||||
CUSTOMER_PORTAL_LOGIN_TOKEN: this.customerPortalLoginToken
|
CUSTOMER_PORTAL_LOGIN_TOKEN: this.customerPortalLoginToken
|
||||||
|
|
@ -492,6 +493,13 @@ export default {
|
||||||
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
contactPhone() {
|
||||||
|
let phoneNumber = this.contactInfo.servicePhone;
|
||||||
|
if (this.contactInfo.extension) {
|
||||||
|
phoneNumber += `-${this.contactInfo.extension}`;
|
||||||
|
}
|
||||||
|
return phoneNumber;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
@ -718,5 +726,10 @@ export default {
|
||||||
color: $black;
|
color: $black;
|
||||||
font-weight: $font-weight-bold;
|
font-weight: $font-weight-bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cart-dropdown:deep(.deductible-value) {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: $font-weight-bold;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -2,32 +2,30 @@
|
||||||
<baseInputButton
|
<baseInputButton
|
||||||
v-bind="$props"
|
v-bind="$props"
|
||||||
v-model="selectedValue"
|
v-model="selectedValue"
|
||||||
buttonWrapperClasses="list-group base-input-button list-button rounded-3 d-flex flex-column w-100 mb-2">
|
buttonWrapperClasses="list-group base-input-button list-button payment-method d-flex flex-column w-100 no-hover">
|
||||||
<div class="button-content list-button-content d-flex flex-row py-3 px-4">
|
<div class="button-content list-button-content d-flex flex-row">
|
||||||
|
<div>
|
||||||
|
<span
|
||||||
|
v-for="token in tokens"
|
||||||
|
:key="token">
|
||||||
|
<span v-if="isInlineImageToken(token)">
|
||||||
|
<img
|
||||||
|
v-if="hasImage"
|
||||||
|
:src="buttonImage"
|
||||||
|
:alt="getInlineAltText(token)" />
|
||||||
|
<span v-else> {{ getInlineAltText(token) }}</span>
|
||||||
|
</span>
|
||||||
|
<span v-else>
|
||||||
|
{{ token }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
<img
|
<img
|
||||||
v-if="shouldDisplaySideImage"
|
v-if="shouldDisplaySideImage"
|
||||||
:id="buttonImageId"
|
:id="buttonImageId"
|
||||||
class="ms-auto order-3"
|
class="ms-auto"
|
||||||
:src="buttonImage"
|
:src="buttonImage"
|
||||||
:alt="altText" />
|
:alt="altText" />
|
||||||
<div class="order-2">
|
|
||||||
<p class="m-0">
|
|
||||||
<span
|
|
||||||
v-for="token in tokens"
|
|
||||||
:key="token">
|
|
||||||
<span v-if="isInlineImageToken(token)">
|
|
||||||
<img
|
|
||||||
v-if="hasImage"
|
|
||||||
:src="buttonImage"
|
|
||||||
:alt="getInlineAltText(token)" />
|
|
||||||
<span v-else> {{ getInlineAltText(token) }}</span>
|
|
||||||
</span>
|
|
||||||
<span v-else>
|
|
||||||
{{ token }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</baseInputButton>
|
</baseInputButton>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -74,52 +72,41 @@ export default {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.list-button {
|
.list-button.payment-method.list-group {
|
||||||
outline: none;
|
outline: none;
|
||||||
input[type="radio"],
|
input[type="radio"],
|
||||||
input[type="checkbox"] {
|
input[type="checkbox"] {
|
||||||
position: static; //override bootstrap
|
|
||||||
|
|
||||||
&:focus-visible + .list-button-content {
|
|
||||||
box-shadow: 0 0 0 2.5px $blue;
|
|
||||||
}
|
|
||||||
&:focus + .list-button-content {
|
|
||||||
box-shadow: 0 0 0 2.5px $blue;
|
|
||||||
}
|
|
||||||
&:checked + .list-button-content {
|
&:checked + .list-button-content {
|
||||||
|
background: $background-color-selected;
|
||||||
|
border-color: $heritage-blue-primary;
|
||||||
|
font-weight: 600;
|
||||||
color: $black;
|
color: $black;
|
||||||
font-weight: 500;
|
|
||||||
background: $blue-100;
|
|
||||||
box-shadow: 0 0 0 1px $blue;
|
|
||||||
}
|
}
|
||||||
&:checked:focus + .list-button-content {
|
}
|
||||||
box-shadow: 0 0 0 2.5px $blue;
|
margin-bottom: .75rem;
|
||||||
}
|
border: none;
|
||||||
&:checked + .list-button-content p,
|
|
||||||
&:checked + .list-button-content span {
|
&.has-error {
|
||||||
font-weight: 500;
|
input[type="radio"],
|
||||||
}
|
input[type="checkbox"] {
|
||||||
&:checked + .list-button-content span:nth-child(2) {
|
&:focus + .list-button-content {
|
||||||
font-weight: 400;
|
box-shadow: none;
|
||||||
color: $gray-600;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.list-button-content {
|
.list-button-content {
|
||||||
color: $gray-600;
|
color: $darker-gray;
|
||||||
|
font-weight: 400;
|
||||||
position: relative;
|
position: relative;
|
||||||
background: $white;
|
background: $white;
|
||||||
transition: all 150ms linear;
|
transition: all 150ms linear;
|
||||||
border-radius: $border-radius-lg;
|
border-radius: 3.75rem;
|
||||||
border: 1px solid $gray-500;
|
border: 1px solid #CACBCC;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
padding: .625rem 2rem;
|
||||||
span {
|
height: 2.875rem;
|
||||||
&.small {
|
|
||||||
font-size: $font-size-xsm;
|
|
||||||
color: $gray-550;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
import buttonQuestion from '@/digital-components/button-question/button-question.vue';
|
import buttonQuestion from '@/digital-components/button-question/button-question.vue';
|
||||||
import widgetFields from '@/constants/cms-widget-fields.js';
|
import widgetFields from '@/constants/cms-widget-fields.js';
|
||||||
import paymentMethodListButton from './payment-method-list-button/payment-method-list-button.vue';
|
import paymentMethodListButton from './payment-method-list-button/payment-method-list-button.vue';
|
||||||
|
import { markRaw } from 'vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'payment-method-question',
|
name: 'payment-method-question',
|
||||||
|
|
@ -33,7 +34,7 @@ export default {
|
||||||
emits: ['update:modelValue'],
|
emits: ['update:modelValue'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
paymentMethodListButton
|
paymentMethodListButton: markRaw(paymentMethodListButton)
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
|
||||||
|
|
@ -48,15 +48,19 @@ function setupMocks({ customMountOptions = {}, queryString }, mainInitialState =
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const cmsMixin = {
|
||||||
|
methods: {
|
||||||
|
getCmsContent: jest.fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
mountOptions.global.plugins = [testingPinia];
|
mountOptions.global.plugins = [testingPinia];
|
||||||
mountOptions.global.mixins = [mockMixin];
|
mountOptions.mixins = [mockMixin, cmsMixin];
|
||||||
|
|
||||||
const wrapper = shallowMount(paymentMethod, mountOptions);
|
const wrapper = shallowMount(paymentMethod, mountOptions);
|
||||||
|
|
||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
const store = useMainStore();
|
const store = useMainStore();
|
||||||
const defaultState = getDefaultState();
|
const defaultState = getDefaultState();
|
||||||
|
|
|
||||||
|
|
@ -10,23 +10,32 @@
|
||||||
ref="siteHeader"
|
ref="siteHeader"
|
||||||
cmsWidgetName="SiteHeaderWidget" />
|
cmsWidgetName="SiteHeaderWidget" />
|
||||||
</div>
|
</div>
|
||||||
<div class="iss-heritage-container-width">
|
<div class="iss-heritage-container-width payment-method-container row">
|
||||||
<div class="payment-method-container iss-heritage-content-container-width">
|
<div class="order-info-container col-md-7">
|
||||||
|
<alert
|
||||||
|
class=""
|
||||||
|
alertClass="alert-warning"
|
||||||
|
cmsWidgetName="AlertIncompleteWidget" />
|
||||||
<siteSubHeader
|
<siteSubHeader
|
||||||
class="mb-5 mt-4 px-3"
|
class="subheader"
|
||||||
cmsWidgetName="SiteSubHeaderWidget"
|
cmsWidgetName="SiteSubHeaderWidget" />
|
||||||
secondaryTextClasses="text-center"
|
|
||||||
subHeaderClasses="mt-4" />
|
|
||||||
<div class="main-content-container">
|
<div class="main-content-container">
|
||||||
<hr class="my-0" />
|
<hr class="section-divider" />
|
||||||
<reviewDropdown ref="reviewDropdown" />
|
<reviewDropdown
|
||||||
<hr class="my-0" />
|
ref="reviewDropdown"
|
||||||
<cartDropdown
|
@edit-clicked="handleEditClicked"
|
||||||
:showAsPaid="false"
|
/>
|
||||||
:readOnly="false"
|
<div class="cart-header">
|
||||||
recyclingModalCmsWidgetName="RecycleModal"
|
<span class="cart-label">{{ cartHeaderText }}</span>
|
||||||
servicePackageTitleWidgetName="ServicePackageTitle" />
|
<span v-if="showCartTotal" class="cart-value">{{ cartTotal }}</span>
|
||||||
<hr class="mt-0 mb-5" />
|
</div>
|
||||||
|
<div class="cart-dropdown-container">
|
||||||
|
<cartDropdown
|
||||||
|
:showAsPaid="false"
|
||||||
|
:readOnly="false"
|
||||||
|
recyclingModalCmsWidgetName="RecycleModal"
|
||||||
|
servicePackageTitleWidgetName="ServicePackageTitle" />
|
||||||
|
</div>
|
||||||
<alert
|
<alert
|
||||||
v-if="displayPayInAdvanceAlert"
|
v-if="displayPayInAdvanceAlert"
|
||||||
name="payInAdvanceErrorAlert"
|
name="payInAdvanceErrorAlert"
|
||||||
|
|
@ -34,27 +43,79 @@
|
||||||
cmsWidgetName="PayInAdvanceErrorAlertWidget"
|
cmsWidgetName="PayInAdvanceErrorAlertWidget"
|
||||||
alertClass="alert-danger"
|
alertClass="alert-danger"
|
||||||
:isDismissible="false" />
|
:isDismissible="false" />
|
||||||
|
<hr
|
||||||
|
v-if="!isPayInAdvanceDisabled"
|
||||||
|
class="pia-divider" />
|
||||||
<paymentMethodQuestion
|
<paymentMethodQuestion
|
||||||
v-if="!isPayInAdvanceDisabled"
|
v-if="!isPayInAdvanceDisabled"
|
||||||
v-model="paymentMethod"
|
v-model="paymentMethod"
|
||||||
:cmsWidgetName="paymentMethodWidgetName"
|
:cmsWidgetName="paymentMethodWidgetName"
|
||||||
:validationRules="rules.optionRequired" />
|
:validationRules="rules.optionRequired" />
|
||||||
<alert
|
<div
|
||||||
v-if="isPayInAdvanceDisabled"
|
v-if="!hideSMSOptIn"
|
||||||
:isDismissible="false"
|
class="sms-opt-in-section">
|
||||||
alertClass="alert-info"
|
<hr class="sms-divider" />
|
||||||
cmsWidgetName="NoPayInAdvanceDisclaimerWidget"
|
<div class="sms-opt-in-header">{{ smsOptInHeaderText }}</div>
|
||||||
:shouldScrollToOnMount="false" />
|
<buttonQuestion
|
||||||
|
class="sms-opt-in-question"
|
||||||
|
v-model="smsOptIn"
|
||||||
|
groupName="sms-opt-in"
|
||||||
|
buttonTypeString="listButtonHorizontal"
|
||||||
|
:questionText="smsOptinQuestionText"
|
||||||
|
:answers="smsOptinQuestionAnswers"
|
||||||
|
isRequired
|
||||||
|
:validationRules="rules.optionRequired" />
|
||||||
|
<textboxQuestion
|
||||||
|
v-if="smsOptIn === 'Yes'"
|
||||||
|
v-model="smsPhoneNumber"
|
||||||
|
inputId="phoneNumber"
|
||||||
|
:cmsWidgetName="widget.smsPhoneNumber"
|
||||||
|
isRequired
|
||||||
|
:mask="phoneMask"
|
||||||
|
placeholderText="###-###-####"
|
||||||
|
disableAutoFill
|
||||||
|
:validationRules="rules.phoneNumber" />
|
||||||
|
</div>
|
||||||
<siteFooter
|
<siteFooter
|
||||||
ref="siteFooter"
|
ref="siteFooter"
|
||||||
cmsWidgetName="SiteFooterWidget"
|
cmsWidgetName="SiteFooterWidget"
|
||||||
:isForwardActionDisabled="!meta.valid"
|
:isForwardActionDisabled="!meta.valid"
|
||||||
:isStackedVertically="true"
|
|
||||||
@backClicked="backButtonAction"
|
@backClicked="backButtonAction"
|
||||||
@ForwardClicked="forwardButtonAction" />
|
@ForwardClicked="forwardButtonAction" />
|
||||||
|
<div
|
||||||
|
v-if="!hideSMSOptIn"
|
||||||
|
class="disclaimer">
|
||||||
|
<textBlock :cmsWidgetName="widget.smsDisclaimer" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="offer-container col-lg-4 offset-lg-1 col-md-5 d-none d-md-flex">
|
||||||
|
<div
|
||||||
|
v-if="offerWipers"
|
||||||
|
class="wiper-offer-panel">
|
||||||
|
<div class="wiper-logo">
|
||||||
|
<img
|
||||||
|
:src="wiperOfferPanel.image"
|
||||||
|
alt="Wiper Offer Logo" />
|
||||||
|
</div>
|
||||||
|
<div class="panel-body">
|
||||||
|
<div class="wiper-header">{{ wiperOfferPanel.header }}</div>
|
||||||
|
<div class="wiper-body">{{ wiperOfferPanel.body }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="panel-footer">
|
||||||
|
<buttonMain
|
||||||
|
class="edit-wiper-offer-button"
|
||||||
|
:buttonText="wiperOfferPanel.edit"
|
||||||
|
@click="handleEditClicked('wipers')" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<contactDetailsDrawer
|
||||||
|
ref="contactDetailsDrawer"
|
||||||
|
class="contact-details-drawer"
|
||||||
|
@updateContactDetails="refreshContactDetails"
|
||||||
|
@closeWithoutSaving="closeContactDetailsWithoutSaving" />
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -69,6 +130,11 @@ import reviewDropdown from '@/layouts/payment-method/review-dropdown/review-drop
|
||||||
import cartDropdown from '@/iss-components/cart-dropdown/cart-dropdown.vue';
|
import cartDropdown from '@/iss-components/cart-dropdown/cart-dropdown.vue';
|
||||||
import paymentMethodQuestion from '@/layouts/payment-method/payment-method-question/payment-method-question.vue';
|
import paymentMethodQuestion from '@/layouts/payment-method/payment-method-question/payment-method-question.vue';
|
||||||
import alert from '@/ux-components/alert/alert.vue';
|
import alert from '@/ux-components/alert/alert.vue';
|
||||||
|
import textBlock from '@/digital-components/text-block/text-block.vue';
|
||||||
|
import buttonQuestion from '@/digital-components/button-question/button-question.vue';
|
||||||
|
import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue';
|
||||||
|
import contactDetailsDrawer from '@/iss-components/contact-details-drawer/contact-details-drawer.vue';
|
||||||
|
import buttonMain from '@/ux-components/button-main/button-main.vue';
|
||||||
|
|
||||||
// Supporting Items
|
// Supporting Items
|
||||||
import settleAllPromises from '@/helpers/layout-helper';
|
import settleAllPromises from '@/helpers/layout-helper';
|
||||||
|
|
@ -86,6 +152,10 @@ import { experimentSettings } from '@/constants/experiments';
|
||||||
import submitType from '@/constants/submit-type';
|
import submitType from '@/constants/submit-type';
|
||||||
import routerParams from '@/router/router-constants/router-params';
|
import routerParams from '@/router/router-constants/router-params';
|
||||||
import { supportsApplePay } from '@/helpers/browser-helper';
|
import { supportsApplePay } from '@/helpers/browser-helper';
|
||||||
|
import { getCartTotal } from '@/helpers/cart-helper';
|
||||||
|
import { formatAmountInDollars } from '@/helpers/text-helper';
|
||||||
|
import widgetFields from '@/constants/cms-widget-fields';
|
||||||
|
import MaskaFormattedMasks from '@/constants/maska-masks';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'payment-method',
|
name: 'payment-method',
|
||||||
|
|
@ -97,7 +167,12 @@ export default {
|
||||||
reviewDropdown,
|
reviewDropdown,
|
||||||
cartDropdown,
|
cartDropdown,
|
||||||
paymentMethodQuestion,
|
paymentMethodQuestion,
|
||||||
alert
|
alert,
|
||||||
|
textBlock,
|
||||||
|
buttonQuestion,
|
||||||
|
textboxQuestion,
|
||||||
|
contactDetailsDrawer,
|
||||||
|
buttonMain
|
||||||
},
|
},
|
||||||
mixins: [baseFormMixin],
|
mixins: [baseFormMixin],
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
|
|
@ -126,20 +201,29 @@ export default {
|
||||||
paymentMethod: null,
|
paymentMethod: null,
|
||||||
widget: {
|
widget: {
|
||||||
paymentMethod: 'PaymentMethodWidget',
|
paymentMethod: 'PaymentMethodWidget',
|
||||||
paymentMethodApplePay: 'PaymentMethodWidgetApplePay'
|
paymentMethodApplePay: 'PaymentMethodWidgetApplePay',
|
||||||
|
amountDue: 'AmountDueCartHeaderTextWidget',
|
||||||
|
payAtAppointment: 'PayAtAppointmentTextWidget',
|
||||||
|
smsOptInHeader: 'SMSOptInHeaderWidget',
|
||||||
|
smsOptInQuestion: 'SMSOptInQuestionWidget',
|
||||||
|
smsPhoneNumber: 'SMSPhoneNumberWidget',
|
||||||
|
smsDisclaimer: 'SMSDisclaimerWidget',
|
||||||
|
wiperOffer: 'WiperOfferPanelWidget'
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
optionRequired: globalRules.OPTION_REQUIRED
|
optionRequired: globalRules.OPTION_REQUIRED,
|
||||||
}
|
phoneNumber: `${globalRules.PHONE_NUMBER_REQUIRED}|${globalRules.PHONE_NUMBER_FORMAT}`
|
||||||
|
},
|
||||||
|
smsOptIn: '',
|
||||||
|
smsPhoneNumber: ''
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
customCallToActionButtonCopy() {
|
customCallToActionButtonCopy() {
|
||||||
switch (this.paymentMethod) {
|
switch (this.paymentMethod) {
|
||||||
case paymentMethods.PayNow:
|
case paymentMethods.PayNow:
|
||||||
return 'Continue to checkout';
|
|
||||||
case paymentMethods.AFTERPAY:
|
case paymentMethods.AFTERPAY:
|
||||||
return 'Continue to Afterpay';
|
return 'Continue to checkout';
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
@ -156,6 +240,60 @@ export default {
|
||||||
},
|
},
|
||||||
paymentMethodWidgetName() {
|
paymentMethodWidgetName() {
|
||||||
return supportsApplePay() ? this.widget.paymentMethodApplePay : this.widget.paymentMethod;
|
return supportsApplePay() ? this.widget.paymentMethodApplePay : this.widget.paymentMethod;
|
||||||
|
},
|
||||||
|
showCartTotal() {
|
||||||
|
return this.mainStore.isVerified && (this.mainStore.isITAC || this.mainStore.isNoComp
|
||||||
|
|| this.mainStore.order.lineItems?.vaps?.length > 0);
|
||||||
|
},
|
||||||
|
cartHeaderText() {
|
||||||
|
if (this.isPayInAdvanceDisabled) {
|
||||||
|
return this.getCmsContent(
|
||||||
|
this.widget.payAtAppointment,
|
||||||
|
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return this.getCmsContent(
|
||||||
|
this.widget.amountDue,
|
||||||
|
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
cartTotal() {
|
||||||
|
return formatAmountInDollars(getCartTotal(this.mainStore.order));
|
||||||
|
},
|
||||||
|
hideSMSOptIn() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
smsOptInHeaderText() {
|
||||||
|
return this.getCmsContent(
|
||||||
|
this.widget.smsOptInHeader,
|
||||||
|
widgetFields.TEXT_BLOCK_WIDGET.TEXT
|
||||||
|
);
|
||||||
|
},
|
||||||
|
smsOptinQuestionText() {
|
||||||
|
return this.getCmsContent(this.widget.smsOptInQuestion, widgetFields.INPUT_QUESTION_WIDGET.QUESTION_TEXT);
|
||||||
|
},
|
||||||
|
smsOptinQuestionAnswers() {
|
||||||
|
return this.getCmsContent(this.widget.smsOptInQuestion, widgetFields.INPUT_QUESTION_WIDGET.ANSWERS) || [];
|
||||||
|
},
|
||||||
|
phoneMask() {
|
||||||
|
return MaskaFormattedMasks.PHONE_NUMBER;
|
||||||
|
},
|
||||||
|
offerWipers() {
|
||||||
|
return !this.mainStore.lineItems.vaps?.some((part) => part.partType.toLowerCase().includes('wiper'));
|
||||||
|
},
|
||||||
|
wiperOfferPanel() {
|
||||||
|
const wiperImage = this.getCmsContent(this.widget.wiperOffer, widgetFields.CONTENT_GROUP_WIDGET.IMAGE);
|
||||||
|
const wiperHeader = this.getCmsContent(this.widget.wiperOffer, widgetFields.CONTENT_GROUP_WIDGET.HEADER_TEXT);
|
||||||
|
const wiperBody = this.getCmsContent(this.widget.wiperOffer, widgetFields.CONTENT_GROUP_WIDGET.BODY_TEXT);
|
||||||
|
const wiperEdit = this.getCmsContent(this.widget.wiperOffer, widgetFields.CONTENT_GROUP_WIDGET.FOOTER_TEXT);
|
||||||
|
|
||||||
|
return {
|
||||||
|
image: wiperImage,
|
||||||
|
header: wiperHeader,
|
||||||
|
body: wiperBody,
|
||||||
|
edit: wiperEdit
|
||||||
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
@ -245,7 +383,12 @@ export default {
|
||||||
&& contactInfoReqs);
|
&& contactInfoReqs);
|
||||||
},
|
},
|
||||||
initializeComponent() {
|
initializeComponent() {
|
||||||
this.paymentMethod = this.getPaymentMethodFromStore();
|
const paymentMethodFromStore = this.getPaymentMethodFromStore();
|
||||||
|
// SMS Opt In defaults to 'no', but we don't actually want to use it if the user hasn't saved data form this page yet
|
||||||
|
if (paymentMethodFromStore) {
|
||||||
|
this.smsOptIn = this.getSMSOptInFromStore();
|
||||||
|
}
|
||||||
|
this.smsPhoneNumber = this.getSMSPhoneFromStore();
|
||||||
},
|
},
|
||||||
getPaymentMethodFromStore() {
|
getPaymentMethodFromStore() {
|
||||||
if (this.isPayInAdvanceDisabled) {
|
if (this.isPayInAdvanceDisabled) {
|
||||||
|
|
@ -258,7 +401,14 @@ export default {
|
||||||
this.$refs.siteFooter.updateButtonText(newValue);
|
this.$refs.siteFooter.updateButtonText(newValue);
|
||||||
},
|
},
|
||||||
async forwardButtonAction() {
|
async forwardButtonAction() {
|
||||||
useMainStore().savePaymentMethodChoice(this.paymentMethod);
|
this.mainStore.savePaymentMethodChoice(this.paymentMethod);
|
||||||
|
if (!this.hideSMSOptIn) {
|
||||||
|
const requestTextUpdates = this.smsOptIn === 'Yes';
|
||||||
|
this.mainStore.updateContactInfo({ requestTextUpdates });
|
||||||
|
if (requestTextUpdates) {
|
||||||
|
this.mainStore.updatePhoneNumbers({ alternative: this.smsPhoneNumber, service: this.smsPhoneNumber });
|
||||||
|
}
|
||||||
|
}
|
||||||
if (this.paymentMethod === paymentMethods.PAY_AT_TIME_OF_SERVICE) {
|
if (this.paymentMethod === paymentMethods.PAY_AT_TIME_OF_SERVICE) {
|
||||||
await submitWorkOrder({ submitType: submitType.SAFELITE });
|
await submitWorkOrder({ submitType: submitType.SAFELITE });
|
||||||
this.$router.navigate(
|
this.$router.navigate(
|
||||||
|
|
@ -284,10 +434,56 @@ export default {
|
||||||
const scenario = this.mainStore.isMobileAppointment
|
const scenario = this.mainStore.isMobileAppointment
|
||||||
? this.navigationScenarios.CLICKED_BACK_MOBILE
|
? this.navigationScenarios.CLICKED_BACK_MOBILE
|
||||||
: this.navigationScenarios.CLICKED_BACK_INSHOP;
|
: this.navigationScenarios.CLICKED_BACK_INSHOP;
|
||||||
this.$router.navigate(
|
this.$router.navigateWithSpinner(
|
||||||
scenario,
|
scenario,
|
||||||
this.$route
|
this.$route
|
||||||
);
|
);
|
||||||
|
},
|
||||||
|
getSMSOptInFromStore() {
|
||||||
|
return this.mainStore.contactInfo.requestTextUpdates ? 'Yes' : 'No';
|
||||||
|
},
|
||||||
|
getSMSPhoneFromStore() {
|
||||||
|
return this.mainStore.contactInfo.alternativePhone ?? this.mainStore.contactInfo.servicePhone;
|
||||||
|
},
|
||||||
|
handleEditClicked(section) {
|
||||||
|
switch (section) {
|
||||||
|
case 'location':
|
||||||
|
this.$router.navigateWithSpinner(
|
||||||
|
this.navigationScenarios.EDIT_SERVICE_LOCATION,
|
||||||
|
this.$route
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'schedule':
|
||||||
|
this.$router.navigateWithSpinner(
|
||||||
|
this.navigationScenarios.EDIT_SCHEDULE,
|
||||||
|
this.$route
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'vehicle':
|
||||||
|
this.$router.navigateWithSpinner(
|
||||||
|
this.navigationScenarios.EDIT_VEHICLE,
|
||||||
|
this.$route
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case 'customer':
|
||||||
|
this.openContactDetailsModal();
|
||||||
|
break;
|
||||||
|
case 'wipers':
|
||||||
|
this.$router.navigateWithSpinner(
|
||||||
|
this.navigationScenarios.EDIT_WIPERS,
|
||||||
|
this.$route
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openContactDetailsModal() {
|
||||||
|
this.$refs.contactDetailsDrawer.openModal();
|
||||||
|
},
|
||||||
|
refreshContactDetails() {
|
||||||
|
this.smsPhoneNumber = this.mainStore.contactInfo.servicePhone;
|
||||||
|
},
|
||||||
|
closeContactDetailsWithoutSaving() {
|
||||||
|
this.smsOptIn = 'No';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -295,20 +491,145 @@ export default {
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
$page-side-padding: 1.5rem;
|
$page-side-padding: 1.5rem;
|
||||||
.iss-heritage-container-width {
|
#app .iss-heritage-container-width.payment-method-container {
|
||||||
.payment-method-container {
|
.alert-warning {
|
||||||
position: relative;
|
margin-top: 1.25rem;
|
||||||
min-height: 1px;
|
margin-bottom: 0rem;
|
||||||
padding-left: .9375rem;
|
}
|
||||||
padding-right: .9375rem;
|
|
||||||
|
.subheader {
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
|
||||||
|
:deep(strong) {
|
||||||
|
color: $black;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.subheader-secondary) {
|
||||||
|
margin-top: .625rem;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin-bottom: 0;
|
||||||
|
color: #4d4e53;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-divider {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
margin-bottom: 1.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: .25rem;
|
||||||
|
margin-bottom: .625rem;
|
||||||
|
|
||||||
|
.cart-label {
|
||||||
|
color: $black;
|
||||||
|
font-weight: $font-weight-bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-value {
|
||||||
|
color: $green;
|
||||||
|
font-weight: $font-weight-bold;
|
||||||
|
font-size: 1.625rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-dropdown-container {
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pia-divider {
|
||||||
|
margin-top: .5rem;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sms-divider {
|
||||||
|
margin-top: 1.75rem;
|
||||||
|
margin-bottom: 1.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sms-opt-in-section {
|
||||||
|
margin-bottom: 1.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sms-opt-in-header {
|
||||||
|
color: $black;
|
||||||
|
font-weight: $font-weight-bold;
|
||||||
|
margin-bottom: 1.125rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sms-opt-in-question {
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
|
||||||
|
:deep(.question-text) {
|
||||||
|
margin-bottom: .625rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.disclaimer {
|
||||||
|
margin-bottom: 1.875rem;
|
||||||
|
font-weight: $font-weight-light;
|
||||||
|
font-size: .8125rem;
|
||||||
|
line-height: 1.54;
|
||||||
|
color: #4d4e53;
|
||||||
|
|
||||||
|
:deep(a) {
|
||||||
|
color: $heritage-blue-primary;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: $font-weight-bold;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
color: $heritage-blue-secondary;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact-details-drawer {
|
||||||
|
:deep(strong) {
|
||||||
|
color: $black;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wiper-offer-panel {
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
margin-bottom: 1.375rem;
|
||||||
|
box-shadow: 0 0 .625rem 1px #cacbcc;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: .25rem;
|
||||||
|
max-height: fit-content;
|
||||||
|
|
||||||
|
.wiper-logo {
|
||||||
|
margin: 1.25rem 2.25rem 0 2.25rem;
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-body {
|
||||||
|
padding: 0 1rem 1.25rem 1rem;
|
||||||
|
|
||||||
|
.wiper-header {
|
||||||
|
margin: 1.25rem 0 .625rem 0;
|
||||||
|
color: $black;
|
||||||
|
font-weight: $font-weight-bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel-footer {
|
||||||
|
padding: 0 1rem 1.25rem 1rem;
|
||||||
|
|
||||||
|
.edit-wiper-offer-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content-container {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page-container-grouped-styles {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,55 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="py-3">
|
<div class="block-container">
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<textBlock
|
<textBlock
|
||||||
:cmsWidgetName="headerCmsWidgetName"
|
:cmsWidgetName="headerCmsWidgetName"
|
||||||
:customText="customHeaderText"
|
:customText="customHeaderText"
|
||||||
typeStyle="body small dark"
|
typeStyle="body dark"
|
||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
:marginTopSizeOverride="0" />
|
:marginTopSizeOverride="0" />
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="review-block-body-container">
|
||||||
v-for="item in content"
|
<div
|
||||||
:key="item"
|
v-for="item in content"
|
||||||
class="small review-block-content"
|
:key="item"
|
||||||
data-test="contentLine"
|
class="review-block-content"
|
||||||
v-html="item"></div>
|
data-test="contentLine"
|
||||||
|
v-html="item"></div>
|
||||||
|
<div
|
||||||
|
class="link-container"
|
||||||
|
:class="{ 'expanded': linkVisible }">
|
||||||
|
<textLink
|
||||||
|
linkType="text"
|
||||||
|
:text="editLinkText"
|
||||||
|
useLoadingModal
|
||||||
|
href="javascript:void(0)"
|
||||||
|
class="link"
|
||||||
|
@clickEvent="linkClicked">
|
||||||
|
</textLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import textBlock from '@/digital-components/text-block/text-block.vue';
|
import textBlock from '@/digital-components/text-block/text-block.vue';
|
||||||
|
import textLink from '@/ux-components/text-link/text-link.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'review-block',
|
name: 'review-block',
|
||||||
components: {
|
components: {
|
||||||
textBlock
|
textBlock,
|
||||||
|
textLink
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
headerCmsWidgetName: String,
|
headerCmsWidgetName: String,
|
||||||
customHeaderText: String,
|
customHeaderText: String,
|
||||||
content: Array // Specifically an array of strings.
|
content: Array, // Specifically an array of strings.
|
||||||
|
editLinkText: {
|
||||||
|
type: String,
|
||||||
|
default: 'Edit'
|
||||||
|
},
|
||||||
|
linkVisible: Boolean
|
||||||
},
|
},
|
||||||
emits: ['edit-clicked'],
|
emits: ['edit-clicked'],
|
||||||
data() {
|
data() {
|
||||||
|
|
@ -51,7 +72,17 @@ export default {
|
||||||
:deep(.review-block-content > ul) {
|
:deep(.review-block-content > ul) {
|
||||||
margin-bottom: 0rem;
|
margin-bottom: 0rem;
|
||||||
}
|
}
|
||||||
.review-block-content {
|
.review-block-body-container {
|
||||||
width: 90%;
|
margin-top: .625rem;
|
||||||
|
}
|
||||||
|
.link-container {
|
||||||
|
max-height: 0;
|
||||||
|
transition: $transition-swing;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&.expanded {
|
||||||
|
margin-top: .625rem;
|
||||||
|
max-height: 2rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,45 +1,39 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="[isExpanded ? 'pb-3' : 'pb-4']">
|
<div>
|
||||||
<div
|
<div
|
||||||
class="row review-toggle flex align-items-center pt-4"
|
class="review-toggle d-flex align-items-center justify-content-between"
|
||||||
:class="[isExpanded ? 'expanded' : '']"
|
:class="[isExpanded ? 'expanded' : '']"
|
||||||
@click="toggleIsExpanded">
|
@click="toggleIsExpanded">
|
||||||
<a
|
<a
|
||||||
aria-label="expand appointment details"
|
aria-label="expand appointment details"
|
||||||
href="javascript:void(0)"
|
href="javascript:void(0)"
|
||||||
class="col d-flex justify-content-between py-0">
|
class="d-flex py-0">
|
||||||
<span class="label">Appointment details</span>
|
<span class="label">Appointment details</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="review-table px-4">
|
<div class="review-table">
|
||||||
<vehicleReview
|
|
||||||
cmsWidgetName="VehicleReviewWidget"
|
|
||||||
:vehicle="vehicleInfo" />
|
|
||||||
<hr class="my-0" />
|
|
||||||
<damageReview
|
|
||||||
cmsWidgetName="DamageReviewWidget"
|
|
||||||
damageLocationsWidgetName="DamageLocationsWidget"
|
|
||||||
:damage="damageInfo" />
|
|
||||||
<hr class="my-0" />
|
|
||||||
<servicePackageReview
|
|
||||||
ref="servicePackageReview"
|
|
||||||
servicePackageOptionsCmsName="ServicePackageTitle"
|
|
||||||
defaultPackageItemsCmsName="DefaultPackageItemDescriptions"
|
|
||||||
vapsItemsCmsName="VapsItemDescriptions"
|
|
||||||
:damage="damageInfo"
|
|
||||||
:lineItems="lineItems" />
|
|
||||||
<hr class="my-0" />
|
|
||||||
<serviceLocationReview
|
<serviceLocationReview
|
||||||
cmsWidgetName="ServiceLocationTitleWidget"
|
:linkVisible="isExpanded"
|
||||||
:serviceLocation="serviceLocationInfo" />
|
cmsWidgetName="ServiceLocationWidget"
|
||||||
<hr class="my-0" />
|
:serviceLocation="serviceLocationInfo"
|
||||||
|
@edit-clicked="editServiceLocation" />
|
||||||
<scheduleReview
|
<scheduleReview
|
||||||
|
:linkVisible="isExpanded"
|
||||||
cmsWidgetName="ScheduleWidget"
|
cmsWidgetName="ScheduleWidget"
|
||||||
:appointmentType="appointmentType" />
|
@edit-clicked="editSchedule" />
|
||||||
<hr class="my-0" />
|
<hr class="section-divider" />
|
||||||
<customerReview
|
<div class="review-table-expander">
|
||||||
cmsWidgetName="CustomerReviewWidget"
|
<vehicleReview
|
||||||
:customer="customerInfo" />
|
:linkVisible="true"
|
||||||
|
cmsWidgetName="VehicleReviewWidget"
|
||||||
|
@edit-clicked="editVehicle" />
|
||||||
|
<hr class="section-divider" />
|
||||||
|
<customerReview
|
||||||
|
:linkVisible="true"
|
||||||
|
cmsWidgetName="CustomerReviewWidget"
|
||||||
|
@edit-clicked="editCustomer" />
|
||||||
|
<hr class="section-divider" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -47,26 +41,18 @@
|
||||||
<script>
|
<script>
|
||||||
// import textBlock from '@/digital-components/text-block/text-block.vue';
|
// import textBlock from '@/digital-components/text-block/text-block.vue';
|
||||||
import customerReview from '@/layouts/payment-method/review-dropdown/review-sections/customer-review/customer-review.vue';
|
import customerReview from '@/layouts/payment-method/review-dropdown/review-sections/customer-review/customer-review.vue';
|
||||||
import damageReview from '@/layouts/payment-method/review-dropdown/review-sections/damage-review/damage-review.vue';
|
|
||||||
import scheduleReview from '@/layouts/payment-method/review-dropdown/review-sections/schedule-review/schedule-review.vue';
|
import scheduleReview from '@/layouts/payment-method/review-dropdown/review-sections/schedule-review/schedule-review.vue';
|
||||||
import
|
import serviceLocationReview from '@/layouts/payment-method/review-dropdown/review-sections/service-location-review/service-location-review.vue';
|
||||||
serviceLocationReview from '@/layouts/payment-method/review-dropdown/review-sections/service-location-review/service-location-review.vue';
|
|
||||||
import
|
|
||||||
servicePackageReview
|
|
||||||
from '@/layouts/payment-method/review-dropdown/review-sections/service-package-review/service-package-review.vue';
|
|
||||||
import vehicleReview from '@/layouts/payment-method/review-dropdown/review-sections/vehicle-review/vehicle-review.vue';
|
import vehicleReview from '@/layouts/payment-method/review-dropdown/review-sections/vehicle-review/vehicle-review.vue';
|
||||||
|
|
||||||
import { useMainStore } from '@/store';
|
import { useMainStore } from '@/store';
|
||||||
import settleAllPromises from '@/helpers/layout-helper';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'review-dropdown',
|
name: 'review-dropdown',
|
||||||
components: {
|
components: {
|
||||||
customerReview,
|
customerReview,
|
||||||
damageReview,
|
|
||||||
scheduleReview,
|
scheduleReview,
|
||||||
serviceLocationReview,
|
serviceLocationReview,
|
||||||
servicePackageReview,
|
|
||||||
vehicleReview
|
vehicleReview
|
||||||
},
|
},
|
||||||
props: {},
|
props: {},
|
||||||
|
|
@ -79,12 +65,6 @@ export default {
|
||||||
vehicleInfo() {
|
vehicleInfo() {
|
||||||
return useMainStore().vehicle;
|
return useMainStore().vehicle;
|
||||||
},
|
},
|
||||||
damageInfo() {
|
|
||||||
return useMainStore().damage;
|
|
||||||
},
|
|
||||||
lineItems() {
|
|
||||||
return useMainStore().lineItems;
|
|
||||||
},
|
|
||||||
serviceLocationInfo() {
|
serviceLocationInfo() {
|
||||||
return useMainStore().order.serviceLocation;
|
return useMainStore().order.serviceLocation;
|
||||||
},
|
},
|
||||||
|
|
@ -95,9 +75,22 @@ export default {
|
||||||
return useMainStore().contactInfo;
|
return useMainStore().contactInfo;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
emits: ['edit-clicked'],
|
||||||
methods: {
|
methods: {
|
||||||
toggleIsExpanded() {
|
toggleIsExpanded() {
|
||||||
this.isExpanded = !this.isExpanded;
|
this.isExpanded = !this.isExpanded;
|
||||||
|
},
|
||||||
|
editServiceLocation() {
|
||||||
|
this.$emit('edit-clicked', 'location');
|
||||||
|
},
|
||||||
|
editSchedule() {
|
||||||
|
this.$emit('edit-clicked', 'schedule');
|
||||||
|
},
|
||||||
|
editVehicle() {
|
||||||
|
this.$emit('edit-clicked', 'vehicle');
|
||||||
|
},
|
||||||
|
editCustomer() {
|
||||||
|
this.$emit('edit-clicked', 'customer');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -111,34 +104,37 @@ export default {
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-table {
|
.review-table-expander {
|
||||||
max-height: 0;
|
max-height: 0;
|
||||||
transition: all 350ms ease-in;
|
transition: $transition-swing;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
visibility: hidden;
|
}
|
||||||
|
|
||||||
|
.section-divider {
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
margin-bottom: 1.875rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.review-toggle {
|
.review-toggle {
|
||||||
|
margin-bottom: .625rem;
|
||||||
|
flex-direction: row;
|
||||||
&:after {
|
&:after {
|
||||||
content: "";
|
content: "";
|
||||||
transition: all 0.5s ease;
|
transition: transform none;
|
||||||
background-image: url($svg-payment-method-review-toggle);
|
background-image: url(~@/assets/img/icons/chevron-circle_review_dropdown.png);
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: right center;
|
background-position: right center;
|
||||||
width: 16px;
|
background-size: 1.25rem 1.25rem;
|
||||||
height: 9px;
|
width: 1.25rem;
|
||||||
|
height: 1.25rem;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
position: relative;
|
|
||||||
right: 0.75rem;
|
|
||||||
margin: 0.5rem 0 0.5rem 1rem;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
&.expanded:after {
|
&.expanded:after {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
&.expanded + .review-table {
|
&.expanded + .review-table .review-table-expander {
|
||||||
max-height: 800px;
|
max-height: 24rem;
|
||||||
transition: all 150ms ease-in;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,129 +0,0 @@
|
||||||
// Components
|
|
||||||
import customerReview from '@/layouts/payment-method/review-dropdown/review-sections/customer-review/customer-review.vue';
|
|
||||||
|
|
||||||
// Supporting Files
|
|
||||||
import { shallowMount } from '@vue/test-utils';
|
|
||||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
|
||||||
|
|
||||||
const testConstants = {
|
|
||||||
cms: {
|
|
||||||
header: {
|
|
||||||
text: 'Header'
|
|
||||||
},
|
|
||||||
sms: {
|
|
||||||
text: 'Sms'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
customerInfo: {
|
|
||||||
firstName: 'First',
|
|
||||||
lastName: 'Last',
|
|
||||||
servicePhone: '111-111-1111',
|
|
||||||
emailAddress: 'builddigitaltest@safelite.com',
|
|
||||||
isSmsOptIn: false
|
|
||||||
},
|
|
||||||
displayContent: {
|
|
||||||
fullName: 'First Last',
|
|
||||||
phoneNumber: '111-111-1111',
|
|
||||||
emailAddress: 'builddigitaltest@safelite.com',
|
|
||||||
smsOptIn: 'Sms'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function generateDefaultProps() {
|
|
||||||
return {
|
|
||||||
cmsWidgetName: 'CustomerWidget',
|
|
||||||
customer: {
|
|
||||||
firstName: testConstants.customerInfo.firstName,
|
|
||||||
lastName: testConstants.customerInfo.lastName,
|
|
||||||
servicePhone: testConstants.customerInfo.servicePhone,
|
|
||||||
emailAddress: testConstants.customerInfo.emailAddress,
|
|
||||||
isSmsOptIn: testConstants.customerInfo.isSmsOptIn
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let cmsContent;
|
|
||||||
const mockMixin = {
|
|
||||||
methods: {
|
|
||||||
getCmsContent: jest.fn((widgetName, cmsFieldName) => cmsContent?.[widgetName]?.[cmsFieldName] ?? '')
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function getShallowMountedComponent(initialData = {}, methodToRun = () => {}) {
|
|
||||||
const mountOptions = getMountOptions({
|
|
||||||
router: {
|
|
||||||
navigate: jest.fn()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
methodToRun();
|
|
||||||
|
|
||||||
mountOptions.data = () => (
|
|
||||||
initialData
|
|
||||||
);
|
|
||||||
|
|
||||||
mountOptions.mixins = [mockMixin];
|
|
||||||
|
|
||||||
const wrapper = shallowMount(customerReview, mountOptions);
|
|
||||||
return { wrapper };
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
cmsContent = {
|
|
||||||
CustomerWidget: {
|
|
||||||
HeaderText: testConstants.cms.header.text,
|
|
||||||
SubheaderText: testConstants.cms.sms.text
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Customer Review Block', () => {
|
|
||||||
test('Should display header text from cms', async () => {
|
|
||||||
// Arrange
|
|
||||||
const props = generateDefaultProps();
|
|
||||||
const { wrapper } = getShallowMountedComponent({
|
|
||||||
...props
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.header).toEqual(testConstants.cms.header.text);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should display sms text from cms', async () => {
|
|
||||||
// Arrange
|
|
||||||
const props = generateDefaultProps();
|
|
||||||
|
|
||||||
const { wrapper } = getShallowMountedComponent({
|
|
||||||
...props
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.smsOptIn).toEqual(testConstants.cms.sms.text);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should render correct display content', async () => {
|
|
||||||
// Arrange
|
|
||||||
const props = generateDefaultProps();
|
|
||||||
|
|
||||||
const { wrapper } = getShallowMountedComponent({
|
|
||||||
...props
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.displayContent).toEqual([
|
|
||||||
testConstants.displayContent.fullName,
|
|
||||||
testConstants.displayContent.emailAddress,
|
|
||||||
testConstants.displayContent.phoneNumber,
|
|
||||||
testConstants.displayContent.smsOptIn
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<reviewBlock
|
<reviewBlock
|
||||||
:customHeaderText="header"
|
:customHeaderText="header"
|
||||||
:content="displayContent" />
|
:content="displayContent"
|
||||||
|
:editLinkText="editLinkText" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -13,30 +14,20 @@ export default {
|
||||||
reviewBlock
|
reviewBlock
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String
|
||||||
customer: Object
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
displayContent() {
|
displayContent() {
|
||||||
return [this.fullName, this.email, this.phoneNumber, this.smsOptIn];
|
return [this.getCmsContent(this.cmsWidgetName, 'BodyText')];
|
||||||
},
|
},
|
||||||
header() {
|
header() {
|
||||||
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
|
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
|
||||||
},
|
},
|
||||||
fullName() {
|
editLinkText() {
|
||||||
return `${this.customer?.firstName} ${this.customer?.lastName}`;
|
return this.getCmsContent(this.cmsWidgetName, 'FooterText');
|
||||||
},
|
|
||||||
email() {
|
|
||||||
return this.customer?.emailAddress;
|
|
||||||
},
|
|
||||||
phoneNumber() {
|
|
||||||
return this.customer?.servicePhone;
|
|
||||||
},
|
|
||||||
smsOptIn() {
|
|
||||||
return this.getCmsContent(this.cmsWidgetName, 'SubheaderText');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
// Components
|
|
||||||
import damageReview from '@/layouts/payment-method/review-dropdown/review-sections/damage-review/damage-review.vue';
|
|
||||||
|
|
||||||
// Supporting Files
|
|
||||||
import { createTestingPinia } from '@pinia/testing';
|
|
||||||
import { useMainStore } from '@/store';
|
|
||||||
import { shallowMount } from '@vue/test-utils';
|
|
||||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
|
||||||
import { getDamageDisplayContent } from '@/helpers/damage-review-content-generator.js';
|
|
||||||
import baseMixin from '@/mixins/base-mixin';
|
|
||||||
|
|
||||||
jest.mock('@/helpers/damage-review-content-generator.js', () => ({
|
|
||||||
getDamageDisplayContent: jest.fn(),
|
|
||||||
getLocationAnswer: jest.fn()
|
|
||||||
}));
|
|
||||||
|
|
||||||
let cmsContent;
|
|
||||||
const mockMixin = {
|
|
||||||
methods: {
|
|
||||||
...baseMixin.methods,
|
|
||||||
getCmsContent: jest.fn((widgetName, cmsFieldName) => cmsContent?.[widgetName]?.[cmsFieldName] ?? '')
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function getShallowMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}) {
|
|
||||||
const mountOptions = getMountOptions({
|
|
||||||
router: {
|
|
||||||
navigate: jest.fn()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const testingPinia = createTestingPinia({
|
|
||||||
initialState: {
|
|
||||||
main: mainInitialState
|
|
||||||
}
|
|
||||||
});
|
|
||||||
useMainStore(testingPinia);
|
|
||||||
methodToRun();
|
|
||||||
|
|
||||||
mountOptions.global.plugins = [testingPinia];
|
|
||||||
mountOptions.data = () => (
|
|
||||||
initialData
|
|
||||||
);
|
|
||||||
|
|
||||||
mountOptions.mixins = [mockMixin];
|
|
||||||
|
|
||||||
const wrapper = shallowMount(damageReview, mountOptions);
|
|
||||||
return { wrapper };
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
getDamageDisplayContent.mockClear();
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Damage Review Block', () => {
|
|
||||||
test('computed displayContent calls getDamageDisplayContent with expected', () => {
|
|
||||||
// Arrange
|
|
||||||
const glassToReplace = ['front-window', 'side-window'];
|
|
||||||
const isRepair = false;
|
|
||||||
const initialStore = {
|
|
||||||
order: {
|
|
||||||
damage: {
|
|
||||||
glassToReplace,
|
|
||||||
isRepair
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const expected = ['a', 'v', 'n'];
|
|
||||||
getDamageDisplayContent.mockImplementationOnce(() => expected);
|
|
||||||
const { wrapper } = getShallowMountedComponent(initialStore, {});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
const result = wrapper.vm.displayContent;
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(result).toStrictEqual(expected);
|
|
||||||
expect(getDamageDisplayContent).toBeCalledTimes(1);
|
|
||||||
expect(getDamageDisplayContent).toBeCalledWith([], [], [], glassToReplace, isRepair);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
<template>
|
|
||||||
<reviewBlock
|
|
||||||
:headerCmsWidgetName="cmsWidgetName"
|
|
||||||
:content="displayContent" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import damageLocationsSelected from '@/constants/damage-locations-selected.js';
|
|
||||||
import { useMainStore } from '@/store';
|
|
||||||
import { getDamageDisplayContent, getLocationAnswer } from '@/helpers/damage-review-content-generator.js';
|
|
||||||
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'damage-review',
|
|
||||||
components: {
|
|
||||||
reviewBlock
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
cmsWidgetName: String,
|
|
||||||
damageLocationsWidgetName: String,
|
|
||||||
damage: Object
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
displayContent() {
|
|
||||||
const { glassToReplace, isRepair } = useMainStore().order.damage;
|
|
||||||
return getDamageDisplayContent(
|
|
||||||
this.locationAnswers,
|
|
||||||
this.driverSideDamageAnswers,
|
|
||||||
this.passengerSideDamageAnswers,
|
|
||||||
glassToReplace,
|
|
||||||
isRepair
|
|
||||||
);
|
|
||||||
},
|
|
||||||
driverSideDamageAnswers() {
|
|
||||||
const answerContent = getLocationAnswer(damageLocationsSelected.DRIVER, this.locationAnswers);
|
|
||||||
return this.getInputQuestionWidgetAnswersNullSafe(answerContent?.SubWidgetName);
|
|
||||||
},
|
|
||||||
passengerSideDamageAnswers() {
|
|
||||||
const answerContent = getLocationAnswer(damageLocationsSelected.PASSENGER, this.locationAnswers);
|
|
||||||
return this.getInputQuestionWidgetAnswersNullSafe(answerContent?.SubWidgetName);
|
|
||||||
},
|
|
||||||
locationAnswers() {
|
|
||||||
return this.getInputQuestionWidgetAnswersNullSafe(this.damageLocationsWidgetName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<reviewBlock
|
<reviewBlock
|
||||||
:customHeaderText="headerText"
|
:customHeaderText="headerText"
|
||||||
:content="displayContent" />
|
:content="displayContent"
|
||||||
|
:editLinkText="editLinkText" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -13,8 +14,7 @@ export default {
|
||||||
reviewBlock
|
reviewBlock
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String
|
||||||
appointmentType: String
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {};
|
return {};
|
||||||
|
|
@ -28,6 +28,9 @@ export default {
|
||||||
},
|
},
|
||||||
headerText() {
|
headerText() {
|
||||||
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
|
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
|
||||||
|
},
|
||||||
|
editLinkText() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, 'FooterText');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,135 +0,0 @@
|
||||||
// Components
|
|
||||||
import
|
|
||||||
serviceLocationReview from '@/layouts/payment-method/review-dropdown/review-sections/service-location-review/service-location-review.vue';
|
|
||||||
|
|
||||||
// Supporting Files
|
|
||||||
import { shallowMount } from '@vue/test-utils';
|
|
||||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
|
||||||
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
|
|
||||||
|
|
||||||
const cmsContent = {
|
|
||||||
ServiceLocationTitleWidget: {
|
|
||||||
Text: 'Title Text'
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockMixin = {
|
|
||||||
methods: {
|
|
||||||
getCmsContent: jest.fn((widgetName, cmsFieldName) => cmsContent?.[widgetName]?.[cmsFieldName] ?? '')
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function generateDefaultProps() {
|
|
||||||
return {
|
|
||||||
cmsWidgetName: 'ServiceLocationTitleWidget',
|
|
||||||
serviceLocation: {
|
|
||||||
address: 'Mobile Address 1',
|
|
||||||
address2: 'Mobile Address 2',
|
|
||||||
city: 'Mobile City',
|
|
||||||
state: 'MO',
|
|
||||||
zipCode: '11111',
|
|
||||||
zipCodeCtu: '',
|
|
||||||
appointmentType: AppointmentTypeStrings.MOBILE,
|
|
||||||
isVehicleProtected: false,
|
|
||||||
provider: {
|
|
||||||
providerNumber: '',
|
|
||||||
address: {
|
|
||||||
streetAddress: 'Service Location Address',
|
|
||||||
city: 'Service Location City',
|
|
||||||
state: 'SL',
|
|
||||||
zipCode: '22222',
|
|
||||||
zipCodeCtu: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getShallowMountedComponent(initialData = {}, methodToRun = () => {}) {
|
|
||||||
const mountOptions = getMountOptions({
|
|
||||||
router: {
|
|
||||||
navigate: jest.fn()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
methodToRun();
|
|
||||||
|
|
||||||
mountOptions.data = () => (
|
|
||||||
initialData
|
|
||||||
);
|
|
||||||
|
|
||||||
mountOptions.mixins = [mockMixin];
|
|
||||||
|
|
||||||
const wrapper = shallowMount(serviceLocationReview, mountOptions);
|
|
||||||
return { wrapper };
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Service Location Review Block', () => {
|
|
||||||
test('Should render mobile address if mobile appointment', async () => {
|
|
||||||
// Arrange
|
|
||||||
const props = generateDefaultProps();
|
|
||||||
const { wrapper } = getShallowMountedComponent({
|
|
||||||
...props
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.displayContent).toEqual([
|
|
||||||
'Mobile Address 1, Mobile Address 2, Mobile City, MO 11111'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should render service location address if inshop appointment.', async () => {
|
|
||||||
// Arrange
|
|
||||||
const props = generateDefaultProps();
|
|
||||||
props.serviceLocation.appointmentType = AppointmentTypeStrings.IN_SHOP;
|
|
||||||
|
|
||||||
const { wrapper } = getShallowMountedComponent({
|
|
||||||
...props
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.displayContent).toEqual([
|
|
||||||
'Service Location Address, Service Location City, SL 22222'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should render service location address if drop-off appointment', async () => {
|
|
||||||
// Arrange
|
|
||||||
const props = generateDefaultProps();
|
|
||||||
props.serviceLocation.appointmentType = AppointmentTypeStrings.DROP_OFF;
|
|
||||||
|
|
||||||
const { wrapper } = getShallowMountedComponent({
|
|
||||||
...props
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.displayContent).toEqual([
|
|
||||||
'Service Location Address, Service Location City, SL 22222'
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should not add comma or any text if address2 is null', async () => {
|
|
||||||
// Arrange
|
|
||||||
const props = generateDefaultProps();
|
|
||||||
props.serviceLocation.address2 = null;
|
|
||||||
|
|
||||||
const { wrapper } = getShallowMountedComponent({
|
|
||||||
...props
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.displayContent).toEqual(['Mobile Address 1, Mobile City, MO 11111']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<reviewBlock
|
<reviewBlock
|
||||||
:headerCmsWidgetName="cmsWidgetName"
|
:customHeaderText="headerText"
|
||||||
:content="displayContent" />
|
:content="displayContent"
|
||||||
|
:editLinkText="editLinkText" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
|
import { AppointmentTypeStrings } from '@/constants/schedule-constants';
|
||||||
|
import { toTitleCase } from '@/helpers/text-helper';
|
||||||
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
|
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -21,11 +23,14 @@ export default {
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
headerText() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
|
||||||
|
},
|
||||||
displayContent() {
|
displayContent() {
|
||||||
return [
|
return [
|
||||||
`${this.addressInfo.address}${this.addressInfo.address2 ? ', ' : ''}${
|
`${this.addressInfo.address}${this.addressInfo.address2 ? ', ' : ''}${
|
||||||
this.addressInfo.address2
|
this.addressInfo.address2
|
||||||
}, ${this.addressInfo.city}, ${this.addressInfo.state} ${this.addressInfo.zipCode}`
|
}, ${toTitleCase(this.addressInfo.city)}, ${this.addressInfo.state} ${this.addressInfo.zipCode}`
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
addressInfo() {
|
addressInfo() {
|
||||||
|
|
@ -46,6 +51,9 @@ export default {
|
||||||
state: this.serviceLocation?.provider?.address?.state,
|
state: this.serviceLocation?.provider?.address?.state,
|
||||||
zipCode: this.serviceLocation?.provider?.address?.zipCode
|
zipCode: this.serviceLocation?.provider?.address?.zipCode
|
||||||
};
|
};
|
||||||
|
},
|
||||||
|
editLinkText() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, 'FooterText');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,965 +0,0 @@
|
||||||
// Components
|
|
||||||
import
|
|
||||||
servicePackageReview from '@/layouts/payment-method/review-dropdown/review-sections/service-package-review/service-package-review.vue';
|
|
||||||
|
|
||||||
// Supporting Files
|
|
||||||
import { shallowMount } from '@vue/test-utils';
|
|
||||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
|
||||||
import packageNames from '@/constants/package-names';
|
|
||||||
import partTypeStrings from '@/constants/part-type-strings';
|
|
||||||
import damageLocationsSelected from '@/constants/damage-locations-selected';
|
|
||||||
import { useMainStore } from '@/store';
|
|
||||||
|
|
||||||
const testConstants = {
|
|
||||||
cmsPropValues: {
|
|
||||||
servicePackageOptionsCmsName: 'ServicePackageTitle',
|
|
||||||
defaultPackageItemsCmsName: 'DefaultPackageItemDescriptions',
|
|
||||||
vapsItemsCmsName: 'VapsItemDescriptions'
|
|
||||||
},
|
|
||||||
widgetNames: {
|
|
||||||
tierOneTitle: 'EconomyServiceTitle',
|
|
||||||
tierTwoTitle: 'StandardServiceTitle',
|
|
||||||
tierThreeTitle: 'PremiumServiceTitle'
|
|
||||||
},
|
|
||||||
defaultItemCopy: {
|
|
||||||
itemOne: 'Item Description 1',
|
|
||||||
itemTwo: 'Item Description 2',
|
|
||||||
itemThree: 'Item Description 3',
|
|
||||||
itemFour: 'Item Description 4',
|
|
||||||
defaultItemCopyArray: ['Item Description 1', 'Item Description 2', 'Item Description 3']
|
|
||||||
},
|
|
||||||
vapsCopy: {
|
|
||||||
frontWiperCopy: 'Front Wiper copy',
|
|
||||||
rearWiperCopy: 'Rear Wiper copy',
|
|
||||||
rainDefenseCopy: 'Rain repel copy'
|
|
||||||
},
|
|
||||||
parts: {
|
|
||||||
frontWiperPart: {
|
|
||||||
partNumber: 'SBB16',
|
|
||||||
description: 'SAFELITE BEAM BLADE 16',
|
|
||||||
partType: 'FRONT WIPER',
|
|
||||||
price: 32.64
|
|
||||||
},
|
|
||||||
rearWiperPart: {
|
|
||||||
partNumber: 'SBBR12A',
|
|
||||||
description: 'SAFELITE REAR BLADE 12A',
|
|
||||||
partType: 'REAR WIPER',
|
|
||||||
price: 24.48
|
|
||||||
},
|
|
||||||
rainDefensePart: {
|
|
||||||
partNumber: 'RAIN REPEL',
|
|
||||||
description: null,
|
|
||||||
partType: 'RAIN REPEL',
|
|
||||||
price: 35.5
|
|
||||||
},
|
|
||||||
recalPart: {
|
|
||||||
partNumber: 'RECAL STATIC',
|
|
||||||
Description: 'Recalibration',
|
|
||||||
partType: 'recalibration',
|
|
||||||
Quantity: '1',
|
|
||||||
price: 150.0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
damages: {
|
|
||||||
frontWindshield: {
|
|
||||||
glassLocation: damageLocationsSelected.WINDSHIELD,
|
|
||||||
glassName: damageLocationsSelected.SINGLE
|
|
||||||
},
|
|
||||||
rearWindshield: {
|
|
||||||
glassLocation: damageLocationsSelected.REAR,
|
|
||||||
glassName: damageLocationsSelected.STATIONARY
|
|
||||||
},
|
|
||||||
sideGlass: {
|
|
||||||
glassLocation: damageLocationsSelected.PASSENGER,
|
|
||||||
glassName: damageLocationsSelected.QUARTER
|
|
||||||
}
|
|
||||||
},
|
|
||||||
imageId: '00000000-0000-0000-0000-000000000000'
|
|
||||||
};
|
|
||||||
|
|
||||||
const figmaScenarios = [
|
|
||||||
{
|
|
||||||
name: '05_01_CSR_Quote_Cash',
|
|
||||||
params: {
|
|
||||||
wiperResponse: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
|
||||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
|
||||||
damage: {
|
|
||||||
isRepair: false,
|
|
||||||
glassToReplace: [testConstants.damages.frontWindshield]
|
|
||||||
},
|
|
||||||
glassParts: [],
|
|
||||||
supportingItems: []
|
|
||||||
},
|
|
||||||
iterations: [
|
|
||||||
{
|
|
||||||
name: 'Economy',
|
|
||||||
vapsCombo: [],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Standard',
|
|
||||||
vapsCombo: [testConstants.parts.frontWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Premium',
|
|
||||||
vapsCombo: [
|
|
||||||
testConstants.parts.rainDefensePart,
|
|
||||||
testConstants.parts.frontWiperPart
|
|
||||||
],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Standard+RearWiper',
|
|
||||||
vapsCombo: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '05_01_CSR_Quote_Standard_Repair',
|
|
||||||
params: {
|
|
||||||
wiperResponse: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
|
||||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
|
||||||
damage: {
|
|
||||||
isRepair: true,
|
|
||||||
glassToReplace: []
|
|
||||||
},
|
|
||||||
glassParts: [],
|
|
||||||
supportingItems: []
|
|
||||||
},
|
|
||||||
iterations: [
|
|
||||||
{
|
|
||||||
name: 'Economy',
|
|
||||||
vapsCombo: [],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Standard',
|
|
||||||
vapsCombo: [testConstants.parts.frontWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Premium',
|
|
||||||
vapsCombo: [
|
|
||||||
testConstants.parts.rainDefensePart,
|
|
||||||
testConstants.parts.frontWiperPart
|
|
||||||
],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Standard+RearWiper',
|
|
||||||
vapsCombo: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '05_01_CSR_Quote_Recal',
|
|
||||||
params: {
|
|
||||||
wiperResponse: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
|
||||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
|
||||||
damage: {
|
|
||||||
isRepair: false,
|
|
||||||
glassToReplace: [testConstants.damages.frontWindshield]
|
|
||||||
},
|
|
||||||
glassParts: [],
|
|
||||||
supportingItems: [testConstants.parts.recalPart]
|
|
||||||
},
|
|
||||||
iterations: [
|
|
||||||
{
|
|
||||||
name: 'Economy',
|
|
||||||
vapsCombo: [],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Standard',
|
|
||||||
vapsCombo: [testConstants.parts.frontWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Premium',
|
|
||||||
vapsCombo: [
|
|
||||||
testConstants.parts.rainDefensePart,
|
|
||||||
testConstants.parts.frontWiperPart
|
|
||||||
],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Standard+RearWiper',
|
|
||||||
vapsCombo: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// 05_01_CSR_Quote_RearGlass omitted as a duplicate of below.
|
|
||||||
{
|
|
||||||
name: '05_01_CSR_Quote_RearGlass+NonWindshield',
|
|
||||||
params: {
|
|
||||||
wiperResponse: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
|
||||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
|
||||||
damage: {
|
|
||||||
isRepair: false,
|
|
||||||
glassToReplace: [testConstants.damages.rearWindshield]
|
|
||||||
},
|
|
||||||
glassParts: [],
|
|
||||||
supportingItems: []
|
|
||||||
},
|
|
||||||
iterations: [
|
|
||||||
{
|
|
||||||
name: 'Economy',
|
|
||||||
vapsCombo: [],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Economy+Frontwiper',
|
|
||||||
vapsCombo: [testConstants.parts.frontWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Standard',
|
|
||||||
vapsCombo: [testConstants.parts.rearWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Standard+RainDefense',
|
|
||||||
vapsCombo: [testConstants.parts.rearWiperPart, testConstants.parts.rainDefensePart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Premium',
|
|
||||||
vapsCombo: [testConstants.parts.rearWiperPart, testConstants.parts.frontWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '05_01_CSR_Quote_RearGlass+Windshield',
|
|
||||||
params: {
|
|
||||||
wiperResponse: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
|
||||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
|
||||||
damage: {
|
|
||||||
isRepair: false,
|
|
||||||
glassToReplace: [
|
|
||||||
testConstants.damages.frontWindshield,
|
|
||||||
testConstants.damages.rearWindshield
|
|
||||||
]
|
|
||||||
},
|
|
||||||
glassParts: [],
|
|
||||||
supportingItems: []
|
|
||||||
},
|
|
||||||
iterations: [
|
|
||||||
{
|
|
||||||
name: 'Economy',
|
|
||||||
vapsCombo: [],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Economy+Frontwiper',
|
|
||||||
vapsCombo: [testConstants.parts.frontWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Economy+Rearwiper',
|
|
||||||
vapsCombo: [testConstants.parts.rearWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Economy+RainDefense',
|
|
||||||
vapsCombo: [testConstants.parts.rainDefensePart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Economy+Frontwiper+RainDefense',
|
|
||||||
vapsCombo: [
|
|
||||||
testConstants.parts.frontWiperPart,
|
|
||||||
testConstants.parts.rainDefensePart
|
|
||||||
],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Economy+Rearwiper+RainDefense',
|
|
||||||
vapsCombo: [testConstants.parts.rearWiperPart, testConstants.parts.rainDefensePart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Standard',
|
|
||||||
vapsCombo: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Premium',
|
|
||||||
vapsCombo: [
|
|
||||||
testConstants.parts.rearWiperPart,
|
|
||||||
testConstants.parts.frontWiperPart,
|
|
||||||
testConstants.parts.rainDefensePart
|
|
||||||
],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '05_01_CSR_Quote_RearGlassNoFrontFit',
|
|
||||||
params: {
|
|
||||||
wiperResponse: [testConstants.parts.rearWiperPart],
|
|
||||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
|
||||||
damage: {
|
|
||||||
isRepair: false,
|
|
||||||
glassToReplace: [testConstants.damages.rearWindshield]
|
|
||||||
},
|
|
||||||
glassParts: [],
|
|
||||||
supportingItems: []
|
|
||||||
},
|
|
||||||
iterations: [
|
|
||||||
{
|
|
||||||
name: 'Economy',
|
|
||||||
vapsCombo: [],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Economy+Raindefense',
|
|
||||||
vapsCombo: [testConstants.parts.rainDefensePart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Standard',
|
|
||||||
vapsCombo: [testConstants.parts.rearWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Premium',
|
|
||||||
vapsCombo: [testConstants.parts.rearWiperPart, testConstants.parts.rainDefensePart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// 05_01_CSR_Quote_Windshield+SideGlass has identical outcomes to 05_01_CSR_Quote_Cash, but included in case that changes in the future.
|
|
||||||
{
|
|
||||||
name: '05_01_CSR_Quote_Windshield+SideGlass',
|
|
||||||
params: {
|
|
||||||
wiperResponse: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
|
||||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
|
||||||
damage: {
|
|
||||||
isRepair: false,
|
|
||||||
glassToReplace: [
|
|
||||||
testConstants.damages.frontWindshield,
|
|
||||||
testConstants.damages.sideGlass
|
|
||||||
]
|
|
||||||
},
|
|
||||||
glassParts: [],
|
|
||||||
supportingItems: []
|
|
||||||
},
|
|
||||||
iterations: [
|
|
||||||
{
|
|
||||||
name: 'Economy',
|
|
||||||
vapsCombo: [],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Standard',
|
|
||||||
vapsCombo: [testConstants.parts.frontWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Premium',
|
|
||||||
vapsCombo: [
|
|
||||||
testConstants.parts.rainDefensePart,
|
|
||||||
testConstants.parts.frontWiperPart
|
|
||||||
],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Standard+RearWiper',
|
|
||||||
vapsCombo: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierTwoTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// Has no standard package
|
|
||||||
{
|
|
||||||
name: '05_01_CSR_Quote_SideGlass',
|
|
||||||
params: {
|
|
||||||
wiperResponse: [testConstants.parts.frontWiperPart, testConstants.parts.rearWiperPart],
|
|
||||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
|
||||||
damage: {
|
|
||||||
isRepair: false,
|
|
||||||
glassToReplace: [testConstants.damages.sideGlass]
|
|
||||||
},
|
|
||||||
glassParts: [],
|
|
||||||
supportingItems: []
|
|
||||||
},
|
|
||||||
iterations: [
|
|
||||||
{
|
|
||||||
name: 'Economy',
|
|
||||||
vapsCombo: [],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Economy+Frontwiper',
|
|
||||||
vapsCombo: [testConstants.parts.frontWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Economy+RainDefense',
|
|
||||||
vapsCombo: [testConstants.parts.rainDefensePart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Economy+Rearwiper',
|
|
||||||
vapsCombo: [testConstants.parts.rearWiperPart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Premium',
|
|
||||||
vapsCombo: [
|
|
||||||
testConstants.parts.rainDefensePart,
|
|
||||||
testConstants.parts.frontWiperPart
|
|
||||||
],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Premium+Rearwiper',
|
|
||||||
vapsCombo: [
|
|
||||||
testConstants.parts.rainDefensePart,
|
|
||||||
testConstants.parts.frontWiperPart,
|
|
||||||
testConstants.parts.rearWiperPart
|
|
||||||
],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
testConstants.vapsCopy.rearWiperCopy,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
// Has no standard package
|
|
||||||
{
|
|
||||||
name: '05_01_CSR_Quote_NoWiperFit',
|
|
||||||
params: {
|
|
||||||
wiperResponse: [],
|
|
||||||
rainDefenseResponse: testConstants.parts.rainDefensePart,
|
|
||||||
damage: {
|
|
||||||
isRepair: false,
|
|
||||||
glassToReplace: [testConstants.damages.frontWindshield]
|
|
||||||
},
|
|
||||||
glassParts: [],
|
|
||||||
supportingItems: []
|
|
||||||
},
|
|
||||||
iterations: [
|
|
||||||
{
|
|
||||||
name: 'Economy',
|
|
||||||
vapsCombo: [],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierOneTitle,
|
|
||||||
displayContent: testConstants.defaultItemCopy.defaultItemCopyArray
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Premium',
|
|
||||||
vapsCombo: [testConstants.parts.rainDefensePart],
|
|
||||||
expected: {
|
|
||||||
packageNameWidget: testConstants.widgetNames.tierThreeTitle,
|
|
||||||
displayContent: [
|
|
||||||
...testConstants.defaultItemCopy.defaultItemCopyArray,
|
|
||||||
testConstants.vapsCopy.rainDefenseCopy
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
function generateDefaultProps() {
|
|
||||||
return {
|
|
||||||
servicePackageOptionsCmsName: testConstants.cmsPropValues.servicePackageOptionsCmsName,
|
|
||||||
defaultPackageItemsCmsName: testConstants.cmsPropValues.defaultPackageItemsCmsName,
|
|
||||||
vapsItemsCmsName: testConstants.cmsPropValues.vapsItemsCmsName,
|
|
||||||
lineItems: {
|
|
||||||
glassParts: [],
|
|
||||||
supportingItems: [],
|
|
||||||
vaps: [testConstants.parts.frontWiperPart]
|
|
||||||
},
|
|
||||||
damage: {
|
|
||||||
isRepair: false,
|
|
||||||
glassToReplace: [testConstants.damages.frontWindshield]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let cmsContent;
|
|
||||||
const mockMixin = {
|
|
||||||
methods: {
|
|
||||||
getCmsContent: jest.fn((widgetName, cmsFieldName) => cmsContent?.[widgetName]?.[cmsFieldName] ?? '')
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function initializeWithDefault() {
|
|
||||||
useMainStore().order.availableVaps = [testConstants.parts.frontWiperPart, testConstants.parts.rainDefensePart];
|
|
||||||
}
|
|
||||||
|
|
||||||
function getShallowMountedComponent(initialData = {}, methodToRun = () => {}) {
|
|
||||||
const mountOptions = getMountOptions({
|
|
||||||
router: {
|
|
||||||
navigate: jest.fn()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
methodToRun();
|
|
||||||
|
|
||||||
mountOptions.data = () => (
|
|
||||||
initialData
|
|
||||||
);
|
|
||||||
|
|
||||||
mountOptions.mixins = [mockMixin];
|
|
||||||
|
|
||||||
const wrapper = shallowMount(servicePackageReview, mountOptions);
|
|
||||||
wrapper.vm.setCmsContent = jest.fn();
|
|
||||||
return { wrapper };
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
cmsContent = {
|
|
||||||
ServicePackageTitle: {
|
|
||||||
Answers: [
|
|
||||||
{
|
|
||||||
Name: packageNames.TIER_ONE,
|
|
||||||
Text: '',
|
|
||||||
SubText: '',
|
|
||||||
ImageId: testConstants.imageId,
|
|
||||||
Image: '',
|
|
||||||
SubWidgetName: testConstants.widgetNames.tierOneTitle
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: packageNames.TIER_TWO,
|
|
||||||
Text: '',
|
|
||||||
SubText: '',
|
|
||||||
ImageId: testConstants.imageId,
|
|
||||||
Image: '',
|
|
||||||
SubWidgetName: testConstants.widgetNames.tierTwoTitle
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: packageNames.TIER_THREE,
|
|
||||||
Text: '',
|
|
||||||
SubText: '',
|
|
||||||
ImageId: testConstants.imageId,
|
|
||||||
Image: '',
|
|
||||||
SubWidgetName: testConstants.widgetNames.tierThreeTitle
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
DefaultPackageItemDescriptions: {
|
|
||||||
Answers: [
|
|
||||||
{
|
|
||||||
Name: 'Item1',
|
|
||||||
Text: testConstants.defaultItemCopy.itemOne,
|
|
||||||
SubText: '',
|
|
||||||
ImageId: testConstants.imageId,
|
|
||||||
Image: '',
|
|
||||||
SubWidgetName: ''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: 'Item2',
|
|
||||||
Text: testConstants.defaultItemCopy.itemTwo,
|
|
||||||
SubText: '',
|
|
||||||
ImageId: testConstants.imageId,
|
|
||||||
Image: '',
|
|
||||||
SubWidgetName: ''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: 'Item3',
|
|
||||||
Text: testConstants.defaultItemCopy.itemThree,
|
|
||||||
SubText: '',
|
|
||||||
ImageId: testConstants.imageId,
|
|
||||||
Image: '',
|
|
||||||
SubWidgetName: ''
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
VapsItemDescriptions: {
|
|
||||||
Answers: [
|
|
||||||
{
|
|
||||||
Name: partTypeStrings.FRONT_WIPER,
|
|
||||||
Text: testConstants.vapsCopy.frontWiperCopy,
|
|
||||||
SubText: '',
|
|
||||||
ImageId: testConstants.imageId,
|
|
||||||
Image: '',
|
|
||||||
SubWidgetName: ''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: partTypeStrings.REAR_WIPER,
|
|
||||||
Text: testConstants.vapsCopy.rearWiperCopy,
|
|
||||||
SubText: '',
|
|
||||||
ImageId: testConstants.imageId,
|
|
||||||
Image: '',
|
|
||||||
SubWidgetName: ''
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: partTypeStrings.RAIN_DEFENSE,
|
|
||||||
Text: testConstants.vapsCopy.rainDefenseCopy,
|
|
||||||
SubText: '',
|
|
||||||
ImageId: testConstants.imageId,
|
|
||||||
Image: '',
|
|
||||||
SubWidgetName: ''
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Service Package Review Block', () => {
|
|
||||||
describe('General functionality', () => {
|
|
||||||
test('Should properly "Round Down" package tier', async () => {
|
|
||||||
// Slightly longer explanation:
|
|
||||||
// Should only return the highest tier where *every* offered VAP is part of the order.
|
|
||||||
// However, there may be vaps not offered in the qualifying tier. Hence rounding *down*.
|
|
||||||
//
|
|
||||||
// I.e. Economy=[], Standard=[front wipers], Premium=[front wipers, rain defense].
|
|
||||||
// Current vaps=[rain defense]. Though rain defense is in Premium, we don't satisfy it or standard.
|
|
||||||
// So our tier should still be Economy.
|
|
||||||
// Should still display extra vaps.
|
|
||||||
|
|
||||||
// Arrange
|
|
||||||
const props = generateDefaultProps();
|
|
||||||
props.lineItems.vaps = [testConstants.parts.rainDefensePart];
|
|
||||||
|
|
||||||
const { wrapper } = getShallowMountedComponent({
|
|
||||||
...props
|
|
||||||
});
|
|
||||||
|
|
||||||
initializeWithDefault();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.packageNameWidget).toEqual(testConstants.widgetNames.tierOneTitle);
|
|
||||||
|
|
||||||
const containsRainDefenseCopy = wrapper.vm.displayContent.includes(testConstants.vapsCopy.rainDefenseCopy);
|
|
||||||
expect(containsRainDefenseCopy).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should display all default items from cms', async () => {
|
|
||||||
// Arrange
|
|
||||||
cmsContent.DefaultPackageItemDescriptions.Answers.push({
|
|
||||||
Name: 'Item4',
|
|
||||||
Text: testConstants.defaultItemCopy.itemFour,
|
|
||||||
SubText: '',
|
|
||||||
ImageId: testConstants.imageId,
|
|
||||||
Image: '',
|
|
||||||
SubWidgetName: ''
|
|
||||||
});
|
|
||||||
|
|
||||||
const props = generateDefaultProps();
|
|
||||||
props.lineItems.vaps = [];
|
|
||||||
|
|
||||||
const { wrapper } = getShallowMountedComponent({
|
|
||||||
...props
|
|
||||||
});
|
|
||||||
|
|
||||||
initializeWithDefault();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
const expectedResult = [
|
|
||||||
testConstants.defaultItemCopy.itemOne,
|
|
||||||
testConstants.defaultItemCopy.itemTwo,
|
|
||||||
testConstants.defaultItemCopy.itemThree,
|
|
||||||
testConstants.defaultItemCopy.itemFour
|
|
||||||
];
|
|
||||||
|
|
||||||
expect(wrapper.vm.displayContent).toEqual(expectedResult);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should display vaps if and only if they are added', async () => {
|
|
||||||
// Arrange
|
|
||||||
const props = generateDefaultProps();
|
|
||||||
const { wrapper } = getShallowMountedComponent({
|
|
||||||
...props
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
initializeWithDefault();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
const includesFrontWiperCopy = wrapper.vm.displayContent.includes(testConstants.vapsCopy.frontWiperCopy);
|
|
||||||
const includesRainDefenseCopy = wrapper.vm.displayContent.includes(testConstants.vapsCopy.rainDefenseCopy);
|
|
||||||
expect(includesFrontWiperCopy).toBe(true);
|
|
||||||
expect(includesRainDefenseCopy).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('Should not error if cms content is missing (though may display poorly).', async () => {
|
|
||||||
// Arrange
|
|
||||||
cmsContent = {};
|
|
||||||
const props = generateDefaultProps();
|
|
||||||
const { wrapper } = getShallowMountedComponent({
|
|
||||||
...props
|
|
||||||
});
|
|
||||||
|
|
||||||
initializeWithDefault();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.packageNameWidget).toEqual('');
|
|
||||||
expect(wrapper.vm.displayContent).toEqual([]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Match Figma Scenarios', () => {
|
|
||||||
figmaScenarios.forEach((scenario) => {
|
|
||||||
scenario.iterations.forEach((iteration) => {
|
|
||||||
it(`Should match figma scenario "${scenario.name}", iteration "${iteration.name}"`, async () => {
|
|
||||||
// Arrange
|
|
||||||
const props = generateDefaultProps();
|
|
||||||
props.damage = scenario.params.damage;
|
|
||||||
props.glassParts = scenario.params.glassParts;
|
|
||||||
props.lineItems.supportingItems = scenario.params.supportingItems;
|
|
||||||
props.lineItems.vaps = iteration.vapsCombo;
|
|
||||||
|
|
||||||
const { wrapper } = getShallowMountedComponent({
|
|
||||||
...props
|
|
||||||
});
|
|
||||||
|
|
||||||
useMainStore().order.availableVaps = [scenario.params.rainDefenseResponse, ...scenario.params.wiperResponse];
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.packageNameWidget).toEqual(iteration.expected.packageNameWidget);
|
|
||||||
expect(wrapper.vm.displayContent).toEqual(iteration.expected.displayContent);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,100 +0,0 @@
|
||||||
<template>
|
|
||||||
<reviewBlock
|
|
||||||
:headerCmsWidgetName="packageNameWidget"
|
|
||||||
:content="displayContent" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import settleAllPromises from '@/helpers/layout-helper';
|
|
||||||
// import baseMixin from '@/mixins/base-mixin.js';
|
|
||||||
import { useMainStore } from '@/store';
|
|
||||||
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
|
|
||||||
import {
|
|
||||||
getHighestFullySatisfiedTier,
|
|
||||||
containsLineItemWithPartType
|
|
||||||
} from '@/helpers/service-package-helper.js';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'service-package-review',
|
|
||||||
components: {
|
|
||||||
reviewBlock
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
servicePackageOptionsCmsName: String,
|
|
||||||
defaultPackageItemsCmsName: String,
|
|
||||||
vapsItemsCmsName: String,
|
|
||||||
lineItems: Object,
|
|
||||||
damage: Object
|
|
||||||
},
|
|
||||||
emits: ['edit-clicked'],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
availableVaps: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
displayContent() {
|
|
||||||
return [...this.defaultPackageText, ...this.vapsItemText];
|
|
||||||
},
|
|
||||||
packageNameWidget() {
|
|
||||||
const servicePackageNames = this.getCmsContent(
|
|
||||||
this.servicePackageOptionsCmsName,
|
|
||||||
'Answers'
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!servicePackageNames) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentPackage = servicePackageNames.find((entry) => entry.Name === this.packageLevel);
|
|
||||||
|
|
||||||
return currentPackage.SubWidgetName;
|
|
||||||
},
|
|
||||||
defaultPackageText() {
|
|
||||||
const defaultItems = this.getCmsContent(this.defaultPackageItemsCmsName, 'Answers');
|
|
||||||
|
|
||||||
if (!defaultItems) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultItems.map((answer) => answer.Text);
|
|
||||||
},
|
|
||||||
vapsItemText() {
|
|
||||||
const vapsDescriptions = this.getCmsContent(this.vapsItemsCmsName, 'Answers');
|
|
||||||
|
|
||||||
if (!vapsDescriptions) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const vapsDescriptionsOnOrder = vapsDescriptions.filter((answer) =>
|
|
||||||
containsLineItemWithPartType(answer.Name, this.vaps));
|
|
||||||
|
|
||||||
return vapsDescriptionsOnOrder.map((answer) => answer.Text);
|
|
||||||
},
|
|
||||||
packageLevel() {
|
|
||||||
return getHighestFullySatisfiedTier(
|
|
||||||
this.glassToReplace,
|
|
||||||
this.availableLineItems,
|
|
||||||
this.isRepair,
|
|
||||||
this.vaps
|
|
||||||
);
|
|
||||||
},
|
|
||||||
glassToReplace() {
|
|
||||||
return this.damage.glassToReplace;
|
|
||||||
},
|
|
||||||
isRepair() {
|
|
||||||
return this.damage.isRepair;
|
|
||||||
},
|
|
||||||
vaps() {
|
|
||||||
return this.lineItems?.vaps;
|
|
||||||
},
|
|
||||||
availableLineItems() {
|
|
||||||
return [
|
|
||||||
...(this.lineItems?.glassParts ?? []),
|
|
||||||
...(this.lineItems?.supportingItems ?? []),
|
|
||||||
...(useMainStore().order.availableVaps ?? [])
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
// Components
|
|
||||||
import vehicleReview from '@/layouts/payment-method/review-dropdown/review-sections/vehicle-review/vehicle-review.vue';
|
|
||||||
|
|
||||||
// Supporting Files
|
|
||||||
import { shallowMount } from '@vue/test-utils';
|
|
||||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
|
||||||
|
|
||||||
jest.mock('@/helpers/cms-content-helper', () => ({
|
|
||||||
fetchCmsContentForPage: () => Promise.resolve('content')
|
|
||||||
}));
|
|
||||||
|
|
||||||
function getShallowMountedComponent(initialData = {}, methodToRun = () => {}) {
|
|
||||||
const mountOptions = getMountOptions({
|
|
||||||
router: {
|
|
||||||
navigate: jest.fn()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
methodToRun();
|
|
||||||
|
|
||||||
mountOptions.data = () => (
|
|
||||||
initialData
|
|
||||||
);
|
|
||||||
|
|
||||||
const wrapper = shallowMount(vehicleReview, mountOptions);
|
|
||||||
wrapper.vm.setCmsContent = jest.fn();
|
|
||||||
return { wrapper };
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Vehicle Review Block', () => {
|
|
||||||
test('Correctly assembles vehicle info into a display string', async () => {
|
|
||||||
// Arrange
|
|
||||||
const { wrapper } = getShallowMountedComponent({
|
|
||||||
vehicle: {
|
|
||||||
year: '2019',
|
|
||||||
make: 'Honda',
|
|
||||||
model: 'Odyssey'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.$nextTick();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.displayContent).toStrictEqual(['2019 Honda Odyssey']);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
<template>
|
<template>
|
||||||
<reviewBlock
|
<reviewBlock
|
||||||
:headerCmsWidgetName="cmsWidgetName"
|
:customHeaderText="header"
|
||||||
:content="displayContent" />
|
:content="displayContent"
|
||||||
|
:editLinkText="editLinkText" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import { getStringWithCustomValues, processIfStatements } from '@/helpers/cms-content-helper';
|
||||||
|
import { getGlassList } from '@/helpers/damage-helper';
|
||||||
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
|
import reviewBlock from '@/layouts/payment-method/review-dropdown/review-block/review-block.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
@ -13,15 +16,40 @@ export default {
|
||||||
reviewBlock
|
reviewBlock
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: String
|
||||||
vehicle: Object
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
header() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
|
||||||
|
},
|
||||||
displayContent() {
|
displayContent() {
|
||||||
return [`${this.vehicle.year} ${this.vehicle.make} ${this.vehicle.model}`];
|
return [this.getCmsContentWithCustomValues(this.cmsWidgetName, 'BodyText')];
|
||||||
|
},
|
||||||
|
editLinkText() {
|
||||||
|
return this.getCmsContent(this.cmsWidgetName, 'FooterText');
|
||||||
|
},
|
||||||
|
customValueMap() {
|
||||||
|
return {
|
||||||
|
glassList: this.glassList,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
glassList() {
|
||||||
|
const glassPieces = this.mainStore.lineItems.glassParts?.map((part) => part.partType.toLowerCase()) ?? [];
|
||||||
|
return getGlassList(glassPieces);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getCmsContentWithCustomValues(widgetName, widgetField) {
|
||||||
|
const rawText = this.getCmsContent(widgetName, widgetField);
|
||||||
|
const processedIfStatements = processIfStatements(
|
||||||
|
rawText,
|
||||||
|
'custom',
|
||||||
|
(v) => this.customValueMap[v]
|
||||||
|
);
|
||||||
|
return getStringWithCustomValues(processedIfStatements, this.customValueMap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@ export default {
|
||||||
|
|
||||||
const wipersPromise = await store.getWipers();
|
const wipersPromise = await store.getWipers();
|
||||||
const rainDefensePromise = await store.getRainDefense();
|
const rainDefensePromise = await store.getRainDefense();
|
||||||
|
const supportingItemsPromise = await store.getSupportingItems();
|
||||||
const promiseResultMap = [
|
const promiseResultMap = [
|
||||||
{
|
{
|
||||||
resultKey: 'cmsContent',
|
resultKey: 'cmsContent',
|
||||||
|
|
@ -115,10 +116,18 @@ export default {
|
||||||
{
|
{
|
||||||
resultKey: 'rainDefense',
|
resultKey: 'rainDefense',
|
||||||
promise: rainDefensePromise
|
promise: rainDefensePromise
|
||||||
|
},
|
||||||
|
{
|
||||||
|
resultKey: 'supportingItems',
|
||||||
|
promise: supportingItemsPromise
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
const resultMap = await settleAllPromises(promiseResultMap);
|
const resultMap = await settleAllPromises(promiseResultMap);
|
||||||
|
|
||||||
|
if (resultMap.supportingItems) {
|
||||||
|
useMainStore().updateSupportingItems(resultMap.supportingItems);
|
||||||
|
}
|
||||||
|
|
||||||
const { feeItems } = store.order.lineItems;
|
const { feeItems } = store.order.lineItems;
|
||||||
|
|
||||||
const availableLineItems = [];
|
const availableLineItems = [];
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ import buttonMain from '@/ux-components/button-main/button-main.vue';
|
||||||
import textBlock from '@/digital-components/text-block/text-block.vue';
|
import textBlock from '@/digital-components/text-block/text-block.vue';
|
||||||
import reviewBlock from '@/layouts/tpa-submit/review-block/review-block.vue';
|
import reviewBlock from '@/layouts/tpa-submit/review-block/review-block.vue';
|
||||||
import deductibleBox from '@/layouts/tpa-submit/deductible-box/deductible-box.vue';
|
import deductibleBox from '@/layouts/tpa-submit/deductible-box/deductible-box.vue';
|
||||||
import contactDetailsDrawer from '@/layouts/tpa-submit/contact-details-drawer/contact-details-drawer.vue';
|
import contactDetailsDrawer from '@/iss-components/contact-details-drawer/contact-details-drawer.vue';
|
||||||
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
|
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
|
||||||
import alert from '@/ux-components/alert/alert.vue';
|
import alert from '@/ux-components/alert/alert.vue';
|
||||||
import modal from '@/digital-components/modal/modal.vue';
|
import modal from '@/digital-components/modal/modal.vue';
|
||||||
|
|
|
||||||
|
|
@ -105,6 +105,9 @@ const navigationScenarios = Object.freeze({
|
||||||
PAY_IN_ADVANCE_ERROR: 'PAY_IN_ADVANCE_ERROR',
|
PAY_IN_ADVANCE_ERROR: 'PAY_IN_ADVANCE_ERROR',
|
||||||
PAY_IN_ADVANCE_CREDIT_CARD_ERROR: 'PAY_IN_ADVANCE_CREDIT_CARD_ERROR',
|
PAY_IN_ADVANCE_CREDIT_CARD_ERROR: 'PAY_IN_ADVANCE_CREDIT_CARD_ERROR',
|
||||||
PAY_IN_ADVANCE_SUCCESS: 'PAY_IN_ADVANCE_SUCCESS',
|
PAY_IN_ADVANCE_SUCCESS: 'PAY_IN_ADVANCE_SUCCESS',
|
||||||
|
EDIT_SERVICE_LOCATION: 'EDIT_SERVICE_LOCATION',
|
||||||
|
EDIT_SCHEDULE: 'EDIT_SCHEDULE',
|
||||||
|
EDIT_WIPERS: 'EDIT_WIPERS',
|
||||||
|
|
||||||
// Bailout
|
// Bailout
|
||||||
BAILOUT: 'BAILOUT'
|
BAILOUT: 'BAILOUT'
|
||||||
|
|
|
||||||
|
|
@ -636,6 +636,22 @@ const routingTable = () => [
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.CLICKED_PAY_NOW,
|
scenario: navigationScenarios.CLICKED_PAY_NOW,
|
||||||
destinationIssPageValue: issPageValues.PAYMENT_PAGE
|
destinationIssPageValue: issPageValues.PAYMENT_PAGE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.EDIT_SERVICE_LOCATION,
|
||||||
|
destinationIssPageValue: issPageValues.SCHEDULE_PAGE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.EDIT_SCHEDULE,
|
||||||
|
destinationIssPageValue: issPageValues.SCHEDULE_PAGE
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.EDIT_VEHICLE,
|
||||||
|
destinationIssPageValue: issPageValues.VEHICLE_DAMAGE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.EDIT_WIPERS,
|
||||||
|
destinationIssPageValue: issPageValues.SERVICE_PACKAGES
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -235,3 +235,6 @@ $border-input-focus: 2.5px solid $heritage-blue-primary;
|
||||||
|
|
||||||
$svg-calendar-error-fill-color: $alert-red-bg;
|
$svg-calendar-error-fill-color: $alert-red-bg;
|
||||||
$svg-calendar-error-stroke-color: '%23db0020'; // URL encoded #db0020
|
$svg-calendar-error-stroke-color: '%23db0020'; // URL encoded #db0020
|
||||||
|
|
||||||
|
// Swing easing to replicate Heritage's slideVisible
|
||||||
|
$transition-swing: all 400ms cubic-bezier(.02, .01, .47, 1);
|
||||||
Loading…
Reference in a new issue