525 lines
16 KiB
JavaScript
525 lines
16 KiB
JavaScript
// Components
|
|
import payment from '@/layouts/payment-page/payment-page.vue';
|
|
|
|
// Supporting Files
|
|
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';
|
|
import CartDropdown from '@/iss-components/cart-dropdown/cart-dropdown.vue';
|
|
import { createTestingPinia } from '@pinia/testing';
|
|
import settleAllPromises from '@/helpers/layout-helper';
|
|
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
|
|
import baseMixin from '@/mixins/base-mixin';
|
|
|
|
// Constants
|
|
const parts = {
|
|
windshield: {
|
|
name: 'windshield',
|
|
canSafeliteRecalibrate: true,
|
|
childParts: [],
|
|
color: 'Green Tint',
|
|
description:
|
|
'solar, soundproofing, lane keep assist, lane departure warning system, w/adaptive cruise control',
|
|
id: 'db22fd44-10dd-456f-979b-ff88cf68cca6',
|
|
partNumber: 'FW04896GTYN',
|
|
partType: 'WINDSHIELD',
|
|
recalibrationType: 'STATIC',
|
|
requiresCapabilityQuestions: false,
|
|
requiresRecalibration: true,
|
|
salesTax: 63.86,
|
|
sellingPrice: 791.46
|
|
},
|
|
frontWipers: {
|
|
name: 'front wipers',
|
|
partNumber: 'SBB16',
|
|
description: 'SAFELITE BEAM BLADE 16',
|
|
partType: 'FRONT WIPER',
|
|
price: 32.64
|
|
},
|
|
rearWipers: {
|
|
name: 'rear wipers',
|
|
partNumber: 'SBBR12A',
|
|
description: 'SAFELITE REAR BLADE 12A',
|
|
partType: 'REAR WIPER',
|
|
price: 24.48
|
|
},
|
|
rainDefense: {
|
|
name: 'rain defense',
|
|
partNumber: 'RAIN DEFENSE',
|
|
description: null,
|
|
partType: 'RAIN DEFENSE',
|
|
price: 35.5
|
|
}
|
|
};
|
|
|
|
const pricedParts = {
|
|
windshield: {
|
|
...parts.windshield,
|
|
kitPrice: 0,
|
|
laborAmount: 60,
|
|
salesTax: 0,
|
|
sellingPrice: 791.46
|
|
},
|
|
frontWipers: {
|
|
...parts.frontWipers,
|
|
kitPrice: 0,
|
|
laborAmount: 20,
|
|
salesTax: 0,
|
|
sellingPrice: 32.64
|
|
},
|
|
rearWipers: {
|
|
...parts.rearWipers,
|
|
kitPrice: 10,
|
|
laborAmount: 10,
|
|
salesTax: 0,
|
|
sellingPrice: 24.48
|
|
},
|
|
rainDefense: {
|
|
...parts.rainDefense,
|
|
kitPrice: 20,
|
|
laborAmount: 0,
|
|
salesTax: 0,
|
|
sellingPrice: 35.5
|
|
}
|
|
};
|
|
|
|
const taxedParts = {
|
|
windshield: {
|
|
...pricedParts.windshield,
|
|
salesTax: 63.86,
|
|
subTotal: 100
|
|
},
|
|
frontWipers: {
|
|
...pricedParts.frontWipers,
|
|
salesTax: 2.36,
|
|
subTotal: 100
|
|
},
|
|
rearWipers: {
|
|
...pricedParts.rearWipers,
|
|
salesTax: 6.0,
|
|
subTotal: 100
|
|
},
|
|
rainDefense: {
|
|
...pricedParts.rainDefense,
|
|
salesTax: 1.0,
|
|
subTotal: 100
|
|
}
|
|
};
|
|
|
|
// Setup global mocks
|
|
// Mock fetchCmsContentForPage
|
|
jest.mock('@/helpers/cms-content-helper', () => ({
|
|
fetchCmsContentForPage: jest.fn(),
|
|
doesCopyContainRouterLink: jest.fn()
|
|
}));
|
|
|
|
// Mock our module for promises.
|
|
jest.mock('@/helpers/layout-helper.js', () => jest.fn());
|
|
|
|
function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRunAfterInitializingStore = () => {}, queryString = {}) {
|
|
const mountOptions = getMountOptions({
|
|
router: {
|
|
navigate: jest.fn()
|
|
},
|
|
route: { query: { issPage: 'page-name', ...queryString }, params: {} }
|
|
});
|
|
|
|
const testingPinia = createTestingPinia({
|
|
initialState: {
|
|
main: mainInitialState
|
|
}
|
|
});
|
|
useMainStore(testingPinia);
|
|
methodToRunAfterInitializingStore();
|
|
|
|
mountOptions.global.plugins = [testingPinia];
|
|
mountOptions.data = () => (initialData);
|
|
|
|
const apiResponses = { cmsContent: {} };
|
|
|
|
settleAllPromises.mockImplementation(() => apiResponses);
|
|
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
|
|
|
|
const wrapper = shallowMount(payment, mountOptions);
|
|
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {});
|
|
wrapper.vm.setCmsContent = jest.fn();
|
|
wrapper.vm.$router.navigateWithSpinner = jest.fn();
|
|
wrapper.vm.navigateBack = baseMixin.methods.navigateBack;
|
|
|
|
return { wrapper };
|
|
}
|
|
|
|
const defaultOrder = {
|
|
order: {
|
|
vehicle: {
|
|
year: '2020',
|
|
make: 'acura',
|
|
model: 'mdx',
|
|
style: '4-door sedan',
|
|
carId: 'dummyCarId',
|
|
category: 'dummyCategory',
|
|
vin: 'dummyVin'
|
|
},
|
|
serviceLocation: {
|
|
address: 'add1',
|
|
address2: 'add2',
|
|
city: 'city',
|
|
state: 'state',
|
|
zipCode: 'zip',
|
|
zipCodeCtu: 'zipCtu',
|
|
appointmentType: 'IN_SHOP',
|
|
isVehicleProtected: true,
|
|
provider: {
|
|
providerNumber: 2,
|
|
address: {
|
|
streetAddress: 'add3',
|
|
city: 'city2',
|
|
state: 'state2',
|
|
zipCode: 'zip2',
|
|
zipCodeCtu: 'zipCtu2'
|
|
}
|
|
},
|
|
techNotes: ''
|
|
},
|
|
contactInfo: {
|
|
firstName: 'first',
|
|
lastName: 'last',
|
|
emailAddress: 'builddigitaltest@safelite.com',
|
|
servicePhone: '555-555-5555'
|
|
},
|
|
damage: {
|
|
isRepair: false,
|
|
numberOfChips: null,
|
|
glassToReplace: [{location: 'windshield'}]
|
|
},
|
|
lineItems: {
|
|
glassParts: [parts.windshield],
|
|
supportingItems: [],
|
|
vaps: [parts.frontWipers],
|
|
promos: []
|
|
},
|
|
insuranceCoverage: {
|
|
coverageStatus: null,
|
|
coverageType: null,
|
|
claimNumber: null
|
|
},
|
|
payment: {
|
|
isInsurance: false,
|
|
isPayInAdvance: true,
|
|
payInAdvanceType: 'Afterpay',
|
|
inactivePromos: []
|
|
},
|
|
schedule: {
|
|
date: 'date',
|
|
startTime: 'start',
|
|
endTime: 'end',
|
|
jobMinMinutes: '30',
|
|
jobMaxMinutes: '45'
|
|
}
|
|
}
|
|
};
|
|
|
|
describe('payment-page.vue', () => {
|
|
describe('arePagePrerequisitesValid', () => {
|
|
test('Returns true in nominal conditions', () => {
|
|
// Arrange
|
|
// Store defaults are valid
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
|
|
// Act
|
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
|
|
|
// Assert
|
|
expect(result).toBe(true);
|
|
});
|
|
|
|
describe('Payment method cases', () => {
|
|
test('Returns false if pia options are not valid', () => {
|
|
// Arrange
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
useMainStore().order.payment.isPayInAdvance = null;
|
|
useMainStore().order.payment.payInAdvanceType = null;
|
|
|
|
// Act
|
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
|
|
|
// Assert
|
|
expect(result).toBe(false);
|
|
});
|
|
|
|
test('Returns false if pay at time of service', () => {
|
|
// Arrange
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
useMainStore().order.payment.isPayInAdvance = false;
|
|
useMainStore().order.payment.payInAdvanceType = null;
|
|
|
|
// Act
|
|
const result = wrapper.vm.arePagePrerequisitesValid();
|
|
|
|
// Assert
|
|
expect(result).toBe(false);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('payment type mapping', () => {
|
|
describe('getPaymentType', () => {
|
|
test('Maps AFTERPAY -> hopPaymentMethods.AFTERPAY', () => {
|
|
// Arrange
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
useMainStore().order.payment.payInAdvanceType = paymentMethods.AFTERPAY;
|
|
|
|
// Act
|
|
const mapped = wrapper.vm.getPaymentType();
|
|
|
|
// Assert
|
|
expect(mapped).toBe(hopPaymentMethods.AFTERPAY);
|
|
});
|
|
|
|
test('Maps CREDIT_CARD -> hopPaymentMethods.CREDIT_CARD', () => {
|
|
// Arrange
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
useMainStore().order.payment.payInAdvanceType = paymentMethods.CREDIT_CARD;
|
|
|
|
// Act
|
|
const mapped = wrapper.vm.getPaymentType();
|
|
|
|
// Assert
|
|
expect(mapped).toBe(hopPaymentMethods.CREDIT_CARD);
|
|
});
|
|
|
|
test('Maps PAYPAL -> hopPaymentMethods.PAYPAL', () => {
|
|
// Arrange
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
useMainStore().order.payment.payInAdvanceType = paymentMethods.PAYPAL;
|
|
|
|
// Act
|
|
const mapped = wrapper.vm.getPaymentType();
|
|
|
|
// Assert
|
|
expect(mapped).toBe(hopPaymentMethods.PAYPAL);
|
|
});
|
|
|
|
test('Maps other values to self', () => {
|
|
// Arrange
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
useMainStore().order.payment.payInAdvanceType = 'SomeOtherText';
|
|
|
|
// Act
|
|
const mapped = wrapper.vm.getPaymentType();
|
|
|
|
// Assert
|
|
expect(mapped).toBe('SomeOtherText');
|
|
});
|
|
});
|
|
|
|
describe('cart-dropdown', () => {
|
|
test('Show cart dropdown if credit card', async () => {
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
wrapper.vm.paymentType = hopPaymentMethods.CREDIT_CARD;
|
|
|
|
// Act
|
|
await wrapper.vm.$nextTick();
|
|
const cartTable = wrapper.findComponent(CartDropdown);
|
|
|
|
// Assert
|
|
expect(cartTable.exists()).toBeTruthy();
|
|
});
|
|
|
|
test('show cart dropdown if afterpay', async () => {
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
wrapper.vm.paymentType = hopPaymentMethods.AFTERPAY;
|
|
|
|
// Act
|
|
await wrapper.vm.$nextTick();
|
|
const cartTable = wrapper.findComponent(CartDropdown);
|
|
|
|
// Assert
|
|
expect(cartTable.exists()).toBeTruthy();
|
|
});
|
|
|
|
test('Hide cart dropdown if paypal', async () => {
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
wrapper.vm.paymentType = hopPaymentMethods.PAYPAL;
|
|
|
|
// Act
|
|
await wrapper.vm.$nextTick();
|
|
const cartTable = wrapper.findComponent(CartDropdown);
|
|
|
|
// Assert
|
|
expect(cartTable.exists()).toBeFalsy();
|
|
});
|
|
});
|
|
|
|
describe('isPaypal', () => {
|
|
test('Is responsive if initial data changes', async () => {
|
|
// Arrange
|
|
// note: begins with payment-type = afterpay
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
|
|
// Act
|
|
const preVal = wrapper.vm.isPaypal;
|
|
wrapper.vm.paymentType = hopPaymentMethods.PAYPAL;
|
|
await wrapper.vm.$nextTick();
|
|
|
|
const postVal = wrapper.vm.isPaypal;
|
|
|
|
// Assert
|
|
expect(preVal).toBe(false);
|
|
expect(postVal).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('hop form', () => {
|
|
describe('fetchSignatureInfo', () => {
|
|
test('Sets auth fields', async () => {
|
|
// Arrange
|
|
const signature = {
|
|
token: 'TOKEN',
|
|
signature: 'SIGNATURE',
|
|
startDate: 'STARTDATE'
|
|
};
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
|
|
wrapper.vm.submitHopForm = jest.fn();
|
|
|
|
// Act
|
|
await wrapper.vm.fetchSignatureInfo(signature);
|
|
|
|
// Assert
|
|
expect(wrapper.vm.authToken).toBe(signature.token);
|
|
expect(wrapper.vm.authSignature).toBe(signature.signature);
|
|
expect(wrapper.vm.authSignatureStart).toBe(signature.startDate);
|
|
|
|
expect(wrapper.vm.submitHopForm).toBeCalled();
|
|
});
|
|
});
|
|
|
|
describe('submitHopForm', () => {
|
|
test('Submits form', async () => {
|
|
// Arrange
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
|
|
wrapper.vm.$refs.hopForm.submit = jest.fn();
|
|
wrapper.vm.$refs.paymentFrame = null;
|
|
|
|
// Act
|
|
wrapper.vm.submitHopForm();
|
|
|
|
await wrapper.vm.$nextTick();
|
|
|
|
// Assert
|
|
expect(wrapper.vm.$refs.hopForm.submit).toBeCalled();
|
|
});
|
|
});
|
|
|
|
describe('handleIFrameContentWindowMessage', () => {
|
|
test('Navigates back if afterpay is closed', () => {
|
|
// Arrange
|
|
const event = {
|
|
data: 'afterpayClosed'
|
|
};
|
|
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
|
|
wrapper.vm.backButtonAction = jest.fn();
|
|
|
|
// Act
|
|
wrapper.vm.handleIFrameContentWindowMessage(event);
|
|
|
|
// Assert
|
|
expect(wrapper.vm.backButtonAction).toBeCalled();
|
|
});
|
|
|
|
test('Blocks UI interaction if credit card is submitted', () => {
|
|
// Arrange
|
|
const event = {
|
|
data: 'creditCardSubmit'
|
|
};
|
|
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
|
|
// Act
|
|
wrapper.vm.handleIFrameContentWindowMessage(event);
|
|
|
|
// Assert
|
|
expect(wrapper.vm.shouldBlockInteraction).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('UI Blocking', () => {
|
|
test('UI block appears when toggled', async () => {
|
|
// Arrange
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
|
|
// Act
|
|
wrapper.vm.setUIBlock(true);
|
|
|
|
await wrapper.vm.$nextTick();
|
|
|
|
const uiBlockElements = wrapper.findAll('.ui-block');
|
|
|
|
// Assert
|
|
expect(uiBlockElements.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
test("Don't propogate clicks from UI block", async () => {
|
|
// Arrange
|
|
const { wrapper } = getMountedComponent(defaultOrder);
|
|
|
|
const outerDiv = wrapper.find('.container-fluid');
|
|
const clickFn = jest.fn();
|
|
outerDiv.element.addEventListener('click', clickFn);
|
|
|
|
// Act
|
|
wrapper.vm.setUIBlock(true);
|
|
|
|
await wrapper.vm.$nextTick();
|
|
|
|
const uiBlockElement = wrapper.find('.ui-block');
|
|
|
|
await uiBlockElement.trigger('click');
|
|
|
|
// Assert
|
|
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 } = getMountedComponent(defaultOrder, {}, () => {}, queryString);
|
|
|
|
// Act
|
|
|
|
// Assert
|
|
const creditCardErrorAlert = wrapper.find('[name="payInAdvanceCreditCardErrorAlert"]');
|
|
expect(creditCardErrorAlert.exists()).toBeTruthy();
|
|
expect(creditCardErrorAlert.isVisible()).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
describe('AfterPay Error Alert', () => {
|
|
test('show after pay error alert', () => {
|
|
// Arrange
|
|
const queryString = {
|
|
[queryStrings.DISPLAY_PAY_IN_ADVANCE_ALERT]: paymentMethods.AFTERPAY
|
|
};
|
|
const { wrapper } = getMountedComponent(defaultOrder, {}, () => {}, queryString);
|
|
|
|
// Act
|
|
|
|
// Assert
|
|
const afterPayErrorAlert = wrapper.find('[name="payInAdvanceAfterPayErrorAlert"]');
|
|
expect(afterPayErrorAlert.exists()).toBeTruthy();
|
|
expect(afterPayErrorAlert.isVisible()).toBeTruthy();
|
|
});
|
|
});
|
|
});
|