Merge branch 'develop' into feature/SSR-1118
This commit is contained in:
commit
84d4f2ad27
14 changed files with 579 additions and 150 deletions
|
|
@ -82,6 +82,11 @@ export function formatAddress(addressLine1, addressLine2, city, state, zipCode)
|
|||
return address;
|
||||
}
|
||||
|
||||
const currencyFormatter = new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD'
|
||||
});
|
||||
|
||||
/**
|
||||
* @function formatAmountInDollars
|
||||
* @param {string, number} amount
|
||||
|
|
@ -89,11 +94,8 @@ export function formatAddress(addressLine1, addressLine2, city, state, zipCode)
|
|||
*/
|
||||
export function formatAmountInDollars(amount) {
|
||||
const numericAmount = typeof amount === 'string' ? parseFloat(amount) : amount;
|
||||
|
||||
if (Number.isNaN(numericAmount) || amount === null || amount === undefined) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const roundedAmount = numericAmount.toFixed(2);
|
||||
return `$${roundedAmount}`;
|
||||
return currencyFormatter.format(amount);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,14 +50,14 @@ describe('text-helper', () => {
|
|||
expect(formatAddress(addressLine1, addressLine2, city, state, zipCode)).toBe(expected);
|
||||
});
|
||||
test.each([
|
||||
[1234.5678, '$1234.57'],
|
||||
['1234.5678', '$1234.57'],
|
||||
[1234.56, '$1234.56'],
|
||||
['1234.56', '$1234.56'],
|
||||
[1234.5, '$1234.50'],
|
||||
['1234.5', '$1234.50'],
|
||||
[1234, '$1234.00'],
|
||||
['1234', '$1234.00'],
|
||||
[1234.5678, '$1,234.57'],
|
||||
['1234.5678', '$1,234.57'],
|
||||
[1234.56, '$1,234.56'],
|
||||
['1234.56', '$1,234.56'],
|
||||
[1234.5, '$1,234.50'],
|
||||
['1234.5', '$1,234.50'],
|
||||
[1234, '$1,234.00'],
|
||||
['1234', '$1,234.00'],
|
||||
[0, '$0.00'],
|
||||
['0', '$0.00'],
|
||||
[NaN, ''],
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@
|
|||
|
||||
exports[`cart-dropdown component initial data rendered as expected 1`] = `
|
||||
Object {
|
||||
"currencyFormatter": NumberFormat {},
|
||||
"baseServiceLineItems": Array [],
|
||||
"deductible": null,
|
||||
"isExpanded": false,
|
||||
"widget": Object {
|
||||
"amountDue": "AmountDueTextWidget",
|
||||
},
|
||||
}
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,14 @@ import cartDropdown from '@/iss-components/cart-dropdown/cart-dropdown.vue';
|
|||
// Supporting Files
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
import { useMainStore } from '@/store';
|
||||
import coverageStatuses from '@/constants/coverage-statuses';
|
||||
import { formatAmountInDollars } from '@/helpers/text-helper.js';
|
||||
|
||||
const VERIFYING_COVERAGE = 'Verifying coverage';
|
||||
|
||||
jest.mock('@/helpers/text-helper', () => ({
|
||||
formatAmountInDollars: jest.fn()
|
||||
}));
|
||||
|
||||
function getMountedComponent(mainInitialState = {}, initialData = {}, propsData = {}) {
|
||||
const mountOptions = getMountOptions({
|
||||
|
|
@ -55,7 +63,7 @@ describe('cart-dropdown component', () => {
|
|||
const reference = '#cart-table';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const { wrapper } = getMountedComponent(cartDropdown, {}, initialData);
|
||||
const { wrapper } = getMountedComponent({}, {}, initialData);
|
||||
|
||||
// Act
|
||||
const cartTable = wrapper.find(reference);
|
||||
|
|
@ -63,52 +71,295 @@ describe('cart-dropdown component', () => {
|
|||
// Assert
|
||||
expect(cartTable.exists()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
describe('computed', () => {
|
||||
test.each([
|
||||
[true, true],
|
||||
[false, false],
|
||||
[false, null]
|
||||
])('isVerified returns %p when isVerified store value is %p', (expected, isVerified) => {
|
||||
test('cart deductible when showDeductibleLineItem is true', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-deductible';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: { isVerified }
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.PENDING
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: false
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
const { wrapper } = getMountedComponent(storeData, {}, initialData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.isVerified;
|
||||
const cartDeductible = wrapper.find(reference);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(expected);
|
||||
expect(cartDeductible.exists()).toBeTruthy();
|
||||
});
|
||||
describe('amountDueDisplayed', () => {
|
||||
test('returns verifying coverage text when isVerified false', () => {
|
||||
test('cart base price when showDeductibleLineItem is false', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-base-price';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.PENDING
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: true
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData, {}, initialData);
|
||||
|
||||
// Act
|
||||
const cartBasePrice = wrapper.find(reference);
|
||||
|
||||
// Assert
|
||||
expect(cartBasePrice.exists()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
describe('does not display', () => {
|
||||
test('cart deductible when showDeductibleLineItem is false', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-deductible';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.PENDING
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: true
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData, {}, initialData);
|
||||
|
||||
// Act
|
||||
const cartDeductible = wrapper.find(reference);
|
||||
|
||||
// Assert
|
||||
expect(cartDeductible.exists()).toBeFalsy();
|
||||
});
|
||||
test('cart base price when showDeductibleLineItem is true', () => {
|
||||
// Arrange
|
||||
const reference = '#cart-base-price';
|
||||
const isExpanded = true;
|
||||
const initialData = { isExpanded };
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.PENDING
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: false
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData, {}, initialData);
|
||||
|
||||
// Act
|
||||
const cartBasePrice = wrapper.find(reference);
|
||||
|
||||
// Assert
|
||||
expect(cartBasePrice.exists()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
describe('computed', () => {
|
||||
describe('showDeductibleLineItem', () => {
|
||||
test.each([[true], [false]])('returns false when isNoComp true', (isItac) => {
|
||||
// Arrange
|
||||
const VERIFYING_COVERAGE = 'Verifying coverage';
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
isVerified: false
|
||||
coverageStatus: coverageStatuses.NO_COMP
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: isItac
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.amountDueDisplayed;
|
||||
const result = wrapper.vm.showDeductibleLineItem;
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(VERIFYING_COVERAGE);
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
test('returns formatted amount due when isVerified false', () => {
|
||||
// TODO finish when methods done
|
||||
test.each([
|
||||
[coverageStatuses.NO_COMP],
|
||||
[coverageStatuses.PENDING]])('returns false when isITAC true', (coverageStatus) => {
|
||||
// Arrange
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: true
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.showDeductibleLineItem;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
test('returns true when isNoComp false and isITAC false', () => {
|
||||
// Arrange
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.PENDING
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: false
|
||||
}
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.showDeductibleLineItem;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
});
|
||||
describe('isUnverified', () => {
|
||||
test('returns false when no comp', () => {
|
||||
// Arrange
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.NO_COMP
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: false
|
||||
},
|
||||
currentDeductible: null
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.isUnverified;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
test('returns false when itac', () => {
|
||||
// Arrange
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.PENDING
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: true
|
||||
},
|
||||
currentDeductible: null
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.isUnverified;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
test('returns false when deductible not null and verified coverage status', () => {
|
||||
// Arrange
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: false
|
||||
},
|
||||
currentDeductible: 23
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.isUnverified;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
test('returns true when not no comp, not itac, and deductible is null', () => {
|
||||
// Arrange
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: false
|
||||
},
|
||||
currentDeductible: null
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.isUnverified;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
test('returns true when not no comp, not itac, and coverage status is not verified', () => {
|
||||
// Arrange
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.PENDING
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: false
|
||||
},
|
||||
currentDeductible: 12
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.isUnverified;
|
||||
|
||||
// Assert
|
||||
expect(result).toBeTruthy();
|
||||
});
|
||||
});
|
||||
describe('amountDue', () => {
|
||||
|
|
@ -137,23 +388,133 @@ describe('cart-dropdown component', () => {
|
|||
});
|
||||
});
|
||||
describe('method', () => {
|
||||
test.each([
|
||||
['$0.00', 0],
|
||||
['$12.00', 12],
|
||||
['$12.30', 12.3],
|
||||
['$12.34', 12.34],
|
||||
['$12.35', 12.345],
|
||||
['$12.34', 12.344],
|
||||
['-$1.00', -1]
|
||||
])('get FormattedAmount returns `%` when amount %', (expected, amount) => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
describe('getDisplayed', () => {
|
||||
test('returns "Verifying coverage" when not no comp, not itac, and deductible null', () => {
|
||||
// Arrange
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: false
|
||||
},
|
||||
currentDeductible: null
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
const amount = 123;
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.getFormattedAmount(amount);
|
||||
// Act
|
||||
const result = wrapper.vm.getDisplayed(amount);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(expected);
|
||||
// Assert
|
||||
expect(result).toBe(VERIFYING_COVERAGE);
|
||||
});
|
||||
test('returns "Verifying coverage" when not no comp, not itac, and coverage status PENDING', () => {
|
||||
// Arrange
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.PENDING
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: false
|
||||
},
|
||||
currentDeductible: 321
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
const amount = 123;
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.getDisplayed(amount);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(VERIFYING_COVERAGE);
|
||||
});
|
||||
test('returns dollar amount when itac', () => {
|
||||
// Arrange
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.PENDING
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: true
|
||||
},
|
||||
currentDeductible: 321
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
const amount = 123;
|
||||
const dollarAmount = '$84.00';
|
||||
formatAmountInDollars.mockImplementationOnce(() => dollarAmount);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.getDisplayed(amount);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(dollarAmount);
|
||||
});
|
||||
test('returns dollar amount when no comp', () => {
|
||||
// Arrange
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.NO_COMP
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: false
|
||||
},
|
||||
currentDeductible: 321
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
const amount = 123;
|
||||
const dollarAmount = '$84.00';
|
||||
formatAmountInDollars.mockImplementationOnce(() => dollarAmount);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.getDisplayed(amount);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(dollarAmount);
|
||||
});
|
||||
test('returns dollar amount when deductible set, not itac, not no comp, and verified', () => {
|
||||
// Arrange
|
||||
const storeData = {
|
||||
order: {
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
coverageStatus: coverageStatuses.VERIFIED
|
||||
}
|
||||
},
|
||||
policy: {
|
||||
isITAC: false
|
||||
},
|
||||
currentDeductible: 321
|
||||
}
|
||||
};
|
||||
const { wrapper } = getMountedComponent(storeData);
|
||||
const amount = 123;
|
||||
const dollarAmount = '$84.00';
|
||||
formatAmountInDollars.mockImplementationOnce(() => dollarAmount);
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.getDisplayed(amount);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(dollarAmount);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,20 +9,43 @@
|
|||
aria-label="expand cart details"
|
||||
href="javascript:void(0)"
|
||||
class="col d-flex justify-content-between py-0">
|
||||
<span class="label color-black">{{ amountDueLabel }}</span>
|
||||
<span class="label amount-due">{{ amountDueDisplayed }}</span>
|
||||
<span class="cart-label color-black">{{ amountDueLabel }}</span>
|
||||
<span class="cart-label amount-due">{{ getDisplayed(amountDue) }}</span>
|
||||
</a>
|
||||
</div>
|
||||
<div
|
||||
id="cart-table"
|
||||
class="cart-table px-4">
|
||||
<span>Cart Table Placeholder</span>
|
||||
class="cart-table">
|
||||
<div
|
||||
id="cart-deductible-or-base-price"
|
||||
class="mt-4 cart-line-item color-gray-100 px-5 py-1">
|
||||
<div
|
||||
v-if="showDeductibleLineItem"
|
||||
id="cart-deductible"
|
||||
class="d-flex justify-content-between align-items-center">
|
||||
<span
|
||||
id="deductible-label"
|
||||
class="cart-label">{{ deductibleLabel }}</span>
|
||||
<span id="deductible-value">{{ getDisplayed(deductible) }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
id="cart-base-price"
|
||||
class="d-flex justify-content-between align-items-center">
|
||||
<span
|
||||
id="base-price-label"
|
||||
class="cart-label">{{ basePriceLabel }}</span>
|
||||
<span id="base-price-value">{{ getDisplayed(baseServicePrice) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useMainStore } from '@/store';
|
||||
import getPriceOfLineItems from '@/helpers/price-calculator.js';
|
||||
import { formatAmountInDollars } from '@/helpers/text-helper.js';
|
||||
|
||||
const VERIFYING_COVERAGE = 'Verifying coverage';
|
||||
|
||||
|
|
@ -31,47 +54,56 @@ export default {
|
|||
components: {},
|
||||
props: {
|
||||
showAsPaid: Boolean,
|
||||
amountDueLabel: String
|
||||
amountDueLabel: String,
|
||||
deductibleLabel: String,
|
||||
basePriceLabel: String
|
||||
},
|
||||
data() {
|
||||
const { currentDeductible } = useMainStore().order;
|
||||
const { supportingItems, glassParts, otherParts } = useMainStore().lineItems;
|
||||
const baseServiceLineItems = [
|
||||
...(supportingItems ?? []),
|
||||
...(glassParts ?? []),
|
||||
...(otherParts ?? [])
|
||||
];
|
||||
return {
|
||||
isExpanded: false,
|
||||
currencyFormatter: new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD'
|
||||
}),
|
||||
widget: {
|
||||
amountDue: 'AmountDueTextWidget'
|
||||
}
|
||||
deductible: currentDeductible,
|
||||
baseServiceLineItems,
|
||||
isExpanded: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isVerified() {
|
||||
return useMainStore().payment?.insuranceCoverage?.isVerified ?? false;
|
||||
baseServicePrice() {
|
||||
return getPriceOfLineItems(this.baseServiceLineItems);
|
||||
},
|
||||
amountDueDisplayed() {
|
||||
return this.isVerified
|
||||
? this.getFormattedAmount(this.amountDue)
|
||||
: VERIFYING_COVERAGE;
|
||||
showDeductibleLineItem() {
|
||||
return !useMainStore().isNoComp && !useMainStore().policy.isITAC;
|
||||
},
|
||||
isUnverified() {
|
||||
return this.showDeductibleLineItem
|
||||
&& (this.deductible == null || !useMainStore().isVerifiedCoverageStatus);
|
||||
},
|
||||
subTotal() {
|
||||
// TODO partial calculation for now
|
||||
return this.showDeductibleLineItem ? this.deductible : this.baseServicePrice;
|
||||
},
|
||||
salesTax() {
|
||||
return 0; // TODO
|
||||
},
|
||||
amountDue() {
|
||||
return this.showAsPaid
|
||||
? 0
|
||||
: this.subTotal + this.salesTax;
|
||||
},
|
||||
subTotal() {
|
||||
return 0;
|
||||
},
|
||||
salesTax() {
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleIsExpanded() {
|
||||
this.isExpanded = !this.isExpanded;
|
||||
},
|
||||
getFormattedAmount(amount) {
|
||||
return this.currencyFormatter.format(amount);
|
||||
getDisplayed(amount) {
|
||||
return this.isUnverified && this.showDeductibleLineItem
|
||||
? VERIFYING_COVERAGE
|
||||
: formatAmountInDollars(amount);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -84,6 +116,13 @@ export default {
|
|||
.color-black {
|
||||
color: $black;
|
||||
}
|
||||
.color-gray-100 {
|
||||
background-color: $gray-100;
|
||||
}
|
||||
|
||||
.cart-line-item {
|
||||
color: $darker-gray;
|
||||
}
|
||||
|
||||
.cart-table {
|
||||
max-height: 0;
|
||||
|
|
@ -93,10 +132,6 @@ export default {
|
|||
}
|
||||
|
||||
.cart-toggle {
|
||||
.amount-due {
|
||||
color: $green;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
transition: all 0.5s ease;
|
||||
|
|
@ -123,9 +158,13 @@ export default {
|
|||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.label {
|
||||
.cart-label {
|
||||
font-weight: $font-weight-bold;
|
||||
line-height: 1.625;
|
||||
}
|
||||
.amount-due {
|
||||
color: $green;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
exports[`coverageStatement.vue-working returns the initial data 1`] = `
|
||||
Object {
|
||||
"baseServiceLineItems": Array [],
|
||||
"currencyFormatter": NumberFormat {},
|
||||
"deductibleText": "Your deductible is",
|
||||
"isNoComp": false,
|
||||
"isRepair": true,
|
||||
|
|
|
|||
|
|
@ -882,25 +882,6 @@ describe('coverageStatement.vue-working', () => {
|
|||
expect(result).toBeTruthy();
|
||||
});
|
||||
});
|
||||
test.each([
|
||||
[0, '$0.00'],
|
||||
[1, '$1.00'],
|
||||
[12, '$12.00'],
|
||||
[1.2, '$1.20'],
|
||||
[1.25, '$1.25'],
|
||||
[1.254, '$1.25'],
|
||||
[1.255, '$1.26'],
|
||||
[-1, '-$1.00']
|
||||
])('getFormattedAmount given %p returns "%p"', (value, expected) => {
|
||||
// Arrange
|
||||
const { wrapper } = getMountedComponent();
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.getFormattedAmount(value);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
describe('navigateForward', () => {
|
||||
const servicePrice = 82;
|
||||
beforeEach(() => {
|
||||
|
|
|
|||
|
|
@ -128,8 +128,10 @@ import baseFormMixin from '@/mixins/base-form-mixin.js';
|
|||
import navigationScenarios from '@/router/router-constants/navigation-scenarios.js';
|
||||
import issPageValues from '@/router/router-constants/issPage-values';
|
||||
import bailoutMessage from '@/constants/bailoutMessage';
|
||||
import coverageStatuses from '@/constants/coverage-statuses';
|
||||
import widgetFields from '@/constants/cms-widget-fields.js';
|
||||
import getPriceOfLineItems from '@/helpers/price-calculator.js';
|
||||
import { formatAmountInDollars } from '@/helpers/text-helper.js';
|
||||
|
||||
const SAFELITE_PROVIDER = 'Safelite';
|
||||
|
||||
|
|
@ -212,10 +214,6 @@ export default {
|
|||
const { isRepair } = useMainStore().damage;
|
||||
const { policyLookupSuccessful, noCoverage } = useMainStore().policy;
|
||||
return {
|
||||
currencyFormatter: new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD'
|
||||
}),
|
||||
isRepair,
|
||||
policyLookupSuccessful,
|
||||
isNoComp: noCoverage ?? false,
|
||||
|
|
@ -292,7 +290,7 @@ export default {
|
|||
return useMainStore().order.currentDeductible;
|
||||
},
|
||||
deductibleForDisplay() {
|
||||
return this.getFormattedAmount(this.deductibleValue);
|
||||
return formatAmountInDollars(this.deductibleValue);
|
||||
},
|
||||
registerClaimSuccessful() {
|
||||
return useMainStore().payment.insuranceCoverage.isVerified;
|
||||
|
|
@ -327,13 +325,13 @@ export default {
|
|||
return getPriceOfLineItems(this.baseServiceLineItems);
|
||||
},
|
||||
servicePriceForDisplay() {
|
||||
return this.getFormattedAmount(this.totalServicePrice);
|
||||
return formatAmountInDollars(this.totalServicePrice);
|
||||
},
|
||||
itacCostSavings() {
|
||||
return this.deductibleValue - this.totalServicePrice;
|
||||
},
|
||||
itacCostSavingsForDisplay() {
|
||||
return this.getFormattedAmount(this.itacCostSavings);
|
||||
return formatAmountInDollars(this.itacCostSavings);
|
||||
},
|
||||
serviceProviderQuestionText() {
|
||||
return this.getCmsContent(
|
||||
|
|
@ -378,11 +376,12 @@ export default {
|
|||
arePagePrerequisitesValid() {
|
||||
return !!useMainStore().vehicle.carId;
|
||||
},
|
||||
getFormattedAmount(amount) {
|
||||
return this.currencyFormatter.format(amount);
|
||||
},
|
||||
async initializeComponent() {
|
||||
useMainStore().updatePolicyITACFlag(this.verifiedITAC);
|
||||
const coverageStatus = this.verifiedITAC || this.verifiedNoComp
|
||||
? coverageStatuses.VERIFIED
|
||||
: coverageStatuses.PENDING;
|
||||
useMainStore().updateCoverageStatus(coverageStatus);
|
||||
if (this.shouldRegisterClaim) {
|
||||
await useMainStore().registerClaim()?.catch(() => {});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,9 @@
|
|||
<hr class="my-0" />
|
||||
<cartDropdown
|
||||
:showAsPaid="false"
|
||||
:amountDueLabel="amountDueText" />
|
||||
:amountDueLabel="amountDueText"
|
||||
:deductibleLabel="deductibleLabel"
|
||||
:basePriceLabel="basePriceLabel" />
|
||||
<hr class="mt-0 mb-5" />
|
||||
<div>Pia Alert Placeholder</div>
|
||||
<paymentMethodQuestion
|
||||
|
|
@ -108,14 +110,13 @@ export default {
|
|||
optionRequired: globalRules.OPTION_REQUIRED
|
||||
},
|
||||
widget: {
|
||||
amountDue: 'AmountDueTextWidget'
|
||||
amountDue: 'AmountDueTextWidget',
|
||||
deductible: 'DeductibleWidget',
|
||||
basePrice: 'BasePriceWidget'
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
damageInfo() {
|
||||
return useMainStore().order.damage;
|
||||
},
|
||||
customCallToActionButtonCopy() {
|
||||
switch (this.paymentMethod) {
|
||||
case paymentMethods.CREDIT_CARD:
|
||||
|
|
@ -133,6 +134,12 @@ export default {
|
|||
},
|
||||
amountDueText() {
|
||||
return this.getCmsContent(this.widget.amountDue, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
},
|
||||
deductibleLabel() {
|
||||
return this.getCmsContent(this.widget.deductible, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
},
|
||||
basePriceLabel() {
|
||||
return this.getCmsContent(this.widget.basePrice, widgetFields.TEXT_BLOCK_WIDGET.TEXT);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
@ -147,16 +154,16 @@ export default {
|
|||
const vehicleReqs = !!(vehicle.year && vehicle.make && vehicle.model && vehicle.style);
|
||||
|
||||
// Damage
|
||||
const { damage } = useMainStore().order;
|
||||
const { isRepair, numberOfChips, glassToReplace } = useMainStore().order.damage;
|
||||
const damageReqs = !!(
|
||||
(damage.isRepair && damage.numberOfChips)
|
||||
|| (!damage.isRepair && damage.glassToReplace?.length)
|
||||
(isRepair && numberOfChips)
|
||||
|| (!isRepair && glassToReplace?.length)
|
||||
);
|
||||
|
||||
// Service Package
|
||||
const { lineItems } = useMainStore().order;
|
||||
const packageReqs = !!(
|
||||
(damage.isRepair || lineItems.glassParts)
|
||||
(isRepair || lineItems.glassParts)
|
||||
&& lineItems.supportingItems
|
||||
);
|
||||
|
||||
|
|
@ -183,22 +190,22 @@ export default {
|
|||
(isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);
|
||||
|
||||
// Schedule
|
||||
const { schedule } = useMainStore().order;
|
||||
const { date, startTime, endTime, jobMaxMinutes, jobMinMinutes } = useMainStore().order.schedule;
|
||||
const scheduleReqs = !!(
|
||||
schedule.date
|
||||
&& schedule.startTime
|
||||
&& schedule.endTime
|
||||
&& schedule.jobMaxMinutes
|
||||
&& schedule.jobMinMinutes
|
||||
date
|
||||
&& startTime
|
||||
&& endTime
|
||||
&& jobMaxMinutes
|
||||
&& jobMinMinutes
|
||||
);
|
||||
|
||||
// Customer
|
||||
const { customer } = useMainStore().order;
|
||||
const { firstName, lastName, phoneNumber, emailAddress } = useMainStore().order.customer;
|
||||
const customerReqs = !!(
|
||||
customer.firstName
|
||||
&& customer.lastName
|
||||
&& customer.phoneNumber
|
||||
&& customer.emailAddress
|
||||
firstName
|
||||
&& lastName
|
||||
&& phoneNumber
|
||||
&& emailAddress
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { shallowMount } from '@vue/test-utils';
|
|||
import { getMountOptions } from '@/helpers/unit-test-helper';
|
||||
import { useMainStore } from '@/store';
|
||||
import { paymentMethods, hopPaymentMethods } from '@/constants/payment-method-constants.js';
|
||||
import queryStrings from '@/constants/query-strings';
|
||||
|
||||
// Constants
|
||||
const parts = {
|
||||
|
|
@ -116,10 +117,10 @@ jest.mock('@/helpers/cms-content-helper', () => ({
|
|||
fetchCmsContentForPage: () => Promise.resolve('content')
|
||||
}));
|
||||
|
||||
function setupMocks({ customMountOptions }) {
|
||||
function setupMocks({ customMountOptions = {}, queryString }) {
|
||||
const mountOptions = getMountOptions({
|
||||
...customMountOptions,
|
||||
route: { query: { issPage: 'page-name' }, params: {} }
|
||||
route: { query: { issPage: 'page-name', ...queryString }, params: {} }
|
||||
});
|
||||
|
||||
// set all mock stuff
|
||||
|
|
@ -450,4 +451,21 @@ describe('payment-page.vue', () => {
|
|||
expect(clickFn).not.toBeCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Credit Card Error Alert', () => {
|
||||
test('show credit card error alert', () => {
|
||||
// Arrange
|
||||
const queryString = {
|
||||
[queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT]: paymentMethods.CREDIT_CARD
|
||||
};
|
||||
const wrapper = setupMocks({ queryString });
|
||||
|
||||
// Act
|
||||
|
||||
// Assert
|
||||
const creditCardErrorAlert = wrapper.find('[name="payInAdvanceCreditCardErrorAlert"]');
|
||||
expect(creditCardErrorAlert.exists()).toBeTruthy();
|
||||
expect(creditCardErrorAlert.isVisible()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,6 +4,15 @@
|
|||
<div class="fade-on-route-transition position-relative">
|
||||
<siteHeader cmsWidgetName="SiteHeaderWidget" />
|
||||
<div class="container-fluid pb-2 payment">
|
||||
<alert
|
||||
v-if="displayPayInAdvanceCreditCardAlert"
|
||||
name="payInAdvanceCreditCardErrorAlert"
|
||||
class="my-4"
|
||||
cmsWidgetName="PayInAdvanceCreditCardErrorAlertWidget"
|
||||
alertClass="alert-danger"
|
||||
@textLinkClicked="paymentFailedPayLater"
|
||||
:isDismissible="false" />
|
||||
|
||||
<div id="card-container">
|
||||
<iframe
|
||||
id="card-frame"
|
||||
|
|
@ -371,6 +380,8 @@ import externalUrls from '@/router/router-constants/externalUrl-values.js';
|
|||
import widgetFields from '@/constants/cms-widget-fields.js';
|
||||
import { hopPaymentMethods, paymentMethods } from '@/constants/payment-method-constants.js';
|
||||
import { AppointmentTypeStrings } from '@/constants/schedule-constants.js';
|
||||
import queryStrings from '@/constants/query-strings';
|
||||
import alert from '@/ux-components/alert/alert.vue';
|
||||
|
||||
export default {
|
||||
name: 'payment-page',
|
||||
|
|
@ -378,7 +389,8 @@ export default {
|
|||
siteHeader,
|
||||
siteFooter,
|
||||
// eslint-disable-next-line vue/no-reserved-component-names
|
||||
Form
|
||||
Form,
|
||||
alert
|
||||
},
|
||||
directives: {
|
||||
resize: {
|
||||
|
|
@ -522,6 +534,9 @@ export default {
|
|||
},
|
||||
isPaypal() {
|
||||
return this.paymentType === hopPaymentMethods.PAYPAL;
|
||||
},
|
||||
displayPayInAdvanceCreditCardAlert() {
|
||||
return this.displayPayInAdvanceAlert(paymentMethods.CREDIT_CARD);
|
||||
}
|
||||
},
|
||||
methods:
|
||||
|
|
@ -708,6 +723,17 @@ export default {
|
|||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
displayPayInAdvanceAlert(payMethod) {
|
||||
return (
|
||||
this.$route.query[queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT] === payMethod
|
||||
);
|
||||
},
|
||||
paymentFailedPayLater() {
|
||||
this.$router.navigate(
|
||||
this.navigationScenarios.CLICKED_FORWARD,
|
||||
this.$route
|
||||
);
|
||||
},
|
||||
backButtonAction() {
|
||||
// page will not render with normal navigation due to navigating from the iframe
|
||||
window.location = this.payInAdvanceCancelUrl;
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ router.beforeEach(async (to, from) => {
|
|||
|
||||
const toQueryPage = to.query?.issPage;
|
||||
const notToPayInAdvanceReturn = toQueryPage !== issPageValues.PAYMENT_RETURN;
|
||||
const isInIframe = fromQueryPage === issPageValues.PAYMENT_PAGE;
|
||||
const isInIframe = window !== window.top || fromQueryPage === issPageValues.PAYMENT_PAGE;
|
||||
|
||||
// isFromPaymentPageToOrderConfirmation workaround for navigating from an iframe but
|
||||
// isInIframe evaluates to false for some reason when navigating from payment to confirmation
|
||||
|
|
|
|||
|
|
@ -258,6 +258,8 @@ export const useMainStore = defineStore({
|
|||
isClaimRegistrationRequired: (state) => state.issConfig.isClaimRegistrationRequired,
|
||||
isClaimAlreadyRegistered: (state) => state.order.payment.insuranceCoverage.claimNumber !== null,
|
||||
isBailout: (state) => state.applicationUser.pageData[issPageValues.BAILOUT_PAGE] != null,
|
||||
isNoComp: (state) => state.order.payment.insuranceCoverage.coverageStatus === coverageStatuses.NO_COMP,
|
||||
isVerifiedCoverageStatus: (state) => state.order.payment.insuranceCoverage.coverageStatus === coverageStatuses.VERIFIED,
|
||||
eventBusItem: (state) => (eventCategory, eventSubCategory) => {
|
||||
const matchedEvent = state.applicationUser.eventBus.find(({ category, subCategory }) => category === eventCategory && subCategory === eventSubCategory);
|
||||
return matchedEvent?.eventValue;
|
||||
|
|
@ -516,6 +518,9 @@ export const useMainStore = defineStore({
|
|||
});
|
||||
});
|
||||
},
|
||||
updateCoverageStatus(newStatus) {
|
||||
this.order.payment.insuranceCoverage.coverageStatus = newStatus;
|
||||
},
|
||||
registerClaim() {
|
||||
const nonNumberCharRegex = /[^0-9]/g;
|
||||
const { order } = this;
|
||||
|
|
@ -580,18 +585,15 @@ export const useMainStore = defineStore({
|
|||
const registerClaimFailed = response.data.isError;
|
||||
order.payment.insuranceCoverage.isVerified = !registerClaimFailed;
|
||||
order.payment.insuranceCoverage.claimNumber = null;
|
||||
if (registerClaimFailed) {
|
||||
this.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.PENDING;
|
||||
} else if (this.policy.noCoverage) {
|
||||
this.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.NO_COMP;
|
||||
} else {
|
||||
this.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.VERIFIED;
|
||||
if (this.policy?.noCoverage ?? false) {
|
||||
this.updateCoverageStatus(coverageStatuses.NO_COMP);
|
||||
} else if (!registerClaimFailed) {
|
||||
this.updateCoverageStatus(coverageStatuses.VERIFIED);
|
||||
this.order.payment.insuranceCoverage.claimNumber = response.data.claimNumber;
|
||||
}
|
||||
return resolve(response);
|
||||
}, (error) => {
|
||||
this.order.payment.insuranceCoverage.isVerified = false;
|
||||
this.order.payment.insuranceCoverage.coverageStatus = coverageStatuses.PENDING;
|
||||
this.order.payment.insuranceCoverage.claimNumber = null;
|
||||
return reject(error);
|
||||
});
|
||||
|
|
@ -1546,12 +1548,8 @@ export const useMainStore = defineStore({
|
|||
|
||||
// These could be undefined
|
||||
this.order.policy.noCoverage = vehicle.noCoverage;
|
||||
if (this.order.payment.insuranceCoverage.coverageStatus === coverageStatuses.PENDING) {
|
||||
this.order.payment.insuranceCoverage.coverageStatus = vehicle.noCoverage
|
||||
? coverageStatuses.NO_COMP
|
||||
: coverageStatuses.PENDING;
|
||||
}
|
||||
|
||||
const currentCoverageStatus = vehicle.noCoverage ? coverageStatuses.NO_COMP : coverageStatuses.PENDING;
|
||||
this.updateCoverageStatus(currentCoverageStatus);
|
||||
this.order.policy.deductible.replace = vehicle.deductible;
|
||||
this.order.policy.deductible.repair = vehicle?.repairWaived ?? false ? 0 : vehicle.deductible;
|
||||
this.order.policy.endorsements = vehicle?.endorsements;
|
||||
|
|
|
|||
|
|
@ -441,6 +441,7 @@ describe('Store', () => {
|
|||
expect.assertions(5);
|
||||
const error = 'register claim error';
|
||||
globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error));
|
||||
store.payment.insuranceCoverage.coverageStatus = coverageStatuses.PENDING;
|
||||
|
||||
// Act
|
||||
await store.registerClaim().catch((e) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue