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

491 lines
14 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';
// 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
let mockCmsContent = {};
jest.mock('@/mixins/base-mixin.js', () => ({
methods: {
getAmountDue: jest.fn().mockImplementation(() => 5),
getDisplayAmountDue: jest.fn().mockImplementation(() => '$5.00')
}
}));
jest.mock('@/helpers/cms-content-helper', () => ({
fetchCmsContentForPage: () => Promise.resolve('content')
}));
function setupMocks({ customMountOptions }) {
const mountOptions = getMountOptions({
...customMountOptions,
route: { query: { issPage: 'page-name' }, params: {} }
});
// set all mock stuff
mountOptions.mixins = [
{
methods: {
getCmsContent: jest.fn().mockImplementation((widgetName, fieldName) => {
if (mockCmsContent[widgetName] && mockCmsContent[widgetName][fieldName]) {
return mockCmsContent[widgetName][fieldName];
}
return undefined;
}),
setCmsContent: jest.fn()
}
}
];
const wrapper = shallowMount(payment, mountOptions);
// act on vm
return wrapper;
}
describe('payment-page.vue', () => {
beforeEach(() => {
useMainStore().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',
phoneNumber: '555-555-5555'
},
damage: {
isRepair: false,
numberOfChips: null,
glassToReplace: [{ location: 'windshield' }]
},
lineItems: {
glassParts: [parts.windshield],
supportingItems: [],
vaps: [parts.frontWipers],
promos: []
},
payment: {
isInsurance: false,
insuranceCoverage: {
isVerified: null,
coverageStatus: null,
coverageVerificationType: null
},
isPayInAdvance: true,
payInAdvanceType: 'Afterpay',
inactivePromos: []
},
schedule: {
date: 'date',
startTime: 'start',
endTime: 'end',
jobMinMinutes: '30',
jobMaxMinutes: '45'
}
};
mockCmsContent = {};
});
afterEach(() => {
jest.clearAllMocks();
});
describe('arePagePrerequisitesValid', () => {
test('Returns true in nominal conditions', () => {
// Arrange
// Store defaults are valid
const wrapper = setupMocks({});
// 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
useMainStore().order.payment.isPayInAdvance = null;
useMainStore().order.payment.payInAdvanceType = null;
const wrapper = setupMocks({});
// Act
const result = wrapper.vm.arePagePrerequisitesValid();
// Assert
expect(result).toBe(false);
});
test('Returns false if pay at time of service', () => {
// Arrange
useMainStore().order.payment.isPayInAdvance = false;
useMainStore().order.payment.payInAdvanceType = null;
const wrapper = setupMocks({});
// Act
const result = wrapper.vm.arePagePrerequisitesValid();
// Assert
expect(result).toBe(false);
});
});
});
describe('beforeRouteEnter', () => {
test('Properly initializes fields and kicks off hop', async () => {
// Arrange
const vmMock = {
setCmsContent: jest.fn(),
$refs: {
cart: {
cartItems: []
}
},
getPiaLineItems: jest.fn(),
fetchSignatureInfo: jest.fn(),
setIFrameListener: jest.fn(),
$nextTick: (f) => {
f();
}
};
const nextF = (f) => {
f(vmMock);
};
// Act
await payment.beforeRouteEnter.call(
vmMock,
{ query: { issPage: 'payment-page' } },
undefined,
nextF
);
// Assert
expect(vmMock.setCmsContent).toBeCalled();
expect(vmMock.fetchSignatureInfo).toBeCalled();
expect(vmMock.setIFrameListener).toBeCalled();
});
});
describe('payment type mapping', () => {
describe('getPaymentType', () => {
test('Maps AFTERPAY -> hopPaymentMethods.AFTERPAY', () => {
// Arrange
useMainStore().order.payment.payInAdvanceType = paymentMethods.AFTERPAY;
const wrapper = setupMocks({});
// Act
const mapped = wrapper.vm.getPaymentType();
// Assert
expect(mapped).toBe(hopPaymentMethods.AFTERPAY);
});
test('Maps CREDIT_CARD -> hopPaymentMethods.CREDIT_CARD', () => {
// Arrange
useMainStore().order.payment.payInAdvanceType = paymentMethods.CREDIT_CARD;
const wrapper = setupMocks({});
// Act
const mapped = wrapper.vm.getPaymentType();
// Assert
expect(mapped).toBe(hopPaymentMethods.CREDIT_CARD);
});
test('Maps PAYPAL -> hopPaymentMethods.PAYPAL', () => {
// Arrange
useMainStore().order.payment.payInAdvanceType = paymentMethods.PAYPAL;
const wrapper = setupMocks({});
// Act
const mapped = wrapper.vm.getPaymentType();
// Assert
expect(mapped).toBe(hopPaymentMethods.PAYPAL);
});
test('Maps other values to self', () => {
// Arrange
useMainStore().order.payment.payInAdvanceType = 'SomeOtherText';
const wrapper = setupMocks({});
// Act
const mapped = wrapper.vm.getPaymentType();
// Assert
expect(mapped).toBe('SomeOtherText');
});
});
describe('isPaypal', () => {
test('Is responsive if initial data changes', async () => {
// Arrange
// note: begins with payment-type = afterpay
const wrapper = setupMocks({});
// 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 = setupMocks({});
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 = setupMocks({});
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 = setupMocks({});
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 = setupMocks({});
// 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 = setupMocks({});
// 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 = setupMocks({});
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();
});
});
});