DigitalConsumer.ISS/src/layouts/payment-page/payment-page.spec.js

458 lines
14 KiB
JavaScript

// Components
import payment from '@/layouts/payment-page/payment-page.vue';
// Supporting Files
import { shallowMount } from '@vue/test-utils';
import { getEnumName, getMountOptions } from '@/helpers/unit-test-helper';
import { useMainStore } from '@/store';
import { paymentMethods } from '@/constants/payment-method-constants.js';
import queryStrings from '@/constants/query-strings';
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 repel',
partNumber: 'RAIN REPEL',
description: null,
partType: 'RAIN REPEL',
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: ''
},
customer: {
firstName: 'first',
lastName: 'last',
emailAddress: 'builddigitaltest@safelite.com'
},
contactInfo: {
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,
paymentMethod: paymentMethods.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.paymentMethod = 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.paymentMethod = paymentMethods.PAY_AT_TIME_OF_SERVICE;
// Act
const result = wrapper.vm.arePagePrerequisitesValid();
// Assert
expect(result).toBe(false);
});
});
});
describe('payment type mapping', () => {
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 = paymentMethods.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);
useMainStore().order.payment.paymentMethod = paymentMethods.PayNow;
// 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('.fade-on-route-transition');
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();
});
});
describe.each([
[paymentMethods.CREDIT_CARD, { data: 'creditCardSubmit' }],
[paymentMethods.AFTERPAY, { data: 'afterpayPaymentReceived' }],
[paymentMethods.APPLEPAY, { data: 'applepayOpened' }],
[paymentMethods.PAYPAL, { data: 'showLoaderForPaypal' }]
])('handleIFrameContentWindowMessage', (expected, event) => {
test(`sets paymentMethod to ${getEnumName(paymentMethods, expected)} when data is ${event.data}`, () => {
// Arrange
const { wrapper } = getMountedComponent(defaultOrder);
useMainStore().order.payment.paymentMethod = paymentMethods.PayNow;
useMainStore().savePaymentMethodChoice = jest.fn().mockImplementation((value) => { useMainStore().order.payment.paymentMethod = value; });
// Act
wrapper.vm.handleIFrameContentWindowMessage(event);
const result = useMainStore().order.payment.paymentMethod;
// Assert
expect(result).toBe(expected);
});
});
});