DigitalConsumer.ISS/src/layouts/payment-page/payment-page.spec.js
2024-05-20 13:13:40 -05:00

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();
});
});
});