// Components import schedule from '@/layouts/schedule-page/schedule-page.vue'; // Supporting Files import { createTestingPinia } from '@pinia/testing'; import { shallowMount } from '@vue/test-utils'; import { getMountOptions } from '@/helpers/unit-test-helper.js'; import { useMainStore } from '@/store/index.js'; // Mock fetchCmsContentForPage jest.mock('@/helpers/cms-content-helper', () => ({ fetchCmsContentForPage: jest.fn() })); const RECAL_ACK_MODAL_REF_NAME = 'recalAckModal'; const mockMixin = { methods: { getCmsContent: jest.fn().mockImplementation(() => ''), setCmsContent: jest.fn(), dispatchStoreAction: jest.fn().mockImplementation((storeAction) => { if (storeAction === 'saveSupportingItemsSuppressingStateResetting') { return Promise.resolve([ { partNumber: 'EARLY BIRD', description: null, partType: 'EARLY BIRD', laborAmount: 0, sellingPrice: 14.99, kitPrice: 0 } ]); } return { data: { estimatedServiceMinutesMinimum: 90, estimatedServiceMinutesMaximum: 120, days: [] } }; }) } }; const footerStub = { render: () => {}, methods: { updateButtonText: jest.fn() } }; const loadingModalStub = { render: () => {}, methods: { showModal: jest.fn(), hideModal: jest.fn() } }; const serviceLocationStub = { render: () => {}, methods: { forwardButtonAction: jest.fn(), initializeComponent: jest.fn() } }; const recalAckModalStub = { template: '
', methods: { openModal: jest.fn(), footerButtonClick: jest.fn() } }; function getShallowMountedComponent(initialData = {}, methodToRun = () => {}) { const mountOptions = getMountOptions({ router: { navigate: jest.fn() } }); mountOptions.global.stubs = { siteFooter: footerStub, loadingModal: loadingModalStub, serviceLocation: serviceLocationStub, recalAckModal: recalAckModalStub }; methodToRun(); mountOptions.mixins = [mockMixin]; mountOptions.data = () => ( initialData ); const wrapper = shallowMount(schedule, mountOptions); // Manually create the ref for recalAckModal since shallowMount doesn't populate it Object.defineProperty(wrapper.vm.$refs, RECAL_ACK_MODAL_REF_NAME, { value: { openModal: jest.fn(), footerButtonClick: jest.fn() }, writable: false, configurable: true }); // Manually create the ref for serviceLocation since shallowMount doesn't populate it Object.defineProperty(wrapper.vm.$refs, 'serviceLocation', { value: { forwardButtonAction: jest.fn(), initializeComponent: jest.fn() }, writable: false, configurable: true }); return { wrapper }; } beforeEach(() => { const testingPinia = createTestingPinia({ initialState: { main: { order: { schedule: { date: '2019-01-01', startTime: '09:00', endTime: '10:00', routeCode: '000' }, lineItems: { glassParts: [ { partNumber: 'ABC123' } ], supportingItems: [] }, serviceLocation: { appointmentType: 'Inshop', zipCode: '12345', zipCodeCtu: '01234', provider: { providerNumber: '123' } }, damage: { isRepair: false }, referralNumber: '1234567' }, payment: { isInsurance: true }, lineItems: { glassParts: [], supportingItems: [] } } } }); useMainStore(testingPinia); }); afterEach(() => { jest.clearAllMocks(); }); beforeAll(() => { Object.defineProperty(window, 'matchMedia', { value: jest.fn().mockImplementation((query) => ({ matches: false, media: query, onchange: null, addEventListener: jest.fn(), removeEventListener: jest.fn(), dispatchEvent: jest.fn() })), writable: true }); }); describe('Initial Load', () => { test('Should pass arePagePrerequisitesValid with a serviceLocation zipcode [in beforeEach]', () => { // Arrange const { wrapper } = getShallowMountedComponent(); // Act const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); // Assert expect(arePagePrerequisitesValid).toBe(true); }); test('Should fail arePagePrerequisitesValid with no serviceLocation zipcode', () => { // Arrange const { wrapper } = getShallowMountedComponent(); wrapper.vm.mainStore.order.serviceLocation.zipCode = null; // Act const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); // Assert expect(arePagePrerequisitesValid).toBeFalsy(); }); test('should return newShopTimeSlots when getAvailableDatesMethod is called', async () => { // Arrange const { wrapper } = getShallowMountedComponent(); wrapper.vm.selectableDatesData = { days: [] }; const store = useMainStore(); store.getShopTimeSlots.mockImplementation(() => Promise.resolve({ data: { estimatedServiceMinutesMinimum: 90, estimatedServiceMinutesMaximum: 120, days: [ { date: '2023-12-01', timeSlots: [ { id: '06747-01820-S-B*20424*7 AM', startTime: '07:00', endTime: '08:00', offerPremium: false } ] } ] } })); // Act const newShopTimeSlots = await wrapper.vm.getAvailableDatesMethod( '2023-01-01', '2023-01-15' ); // Assert expect(newShopTimeSlots).toStrictEqual({ days: [ { date: '2023-12-01', timeSlots: [ { endTime: '08:00', id: '06747-01820-S-B*20424*7 AM', offerPremium: false, startTime: '07:00' } ] } ], estimatedServiceMinutesMinimum: 90, estimatedServiceMinutesMaximum: 120 }); }); test('Should call API service in days of 15 or less when getAvailableDatesMethod is called with large date ranges', async () => { // Arrange const { wrapper } = getShallowMountedComponent(); wrapper.vm.selectableDatesData = { days: [] }; const store = useMainStore(); store.getShopTimeSlots.mockImplementation(() => Promise.resolve({ data: { estimatedServiceMinutesMinimum: 90, estimatedServiceMinutesMaximum: 120, days: [] } })); // Act await wrapper.vm.getAvailableDates.call( wrapper.vm, '2023-01-01', '2023-03-31', 'Inshop', '123', '43228' ); // Assert // 2023-01-01 --> 2023-02-05 // 2023-02-06 --> 2023-03-12 // 2023-03-13 --> 2023-03-31 expect(store.getShopTimeSlots).toHaveBeenCalledTimes(6); }); }); describe('Rendering', () => { test('Schedule page loads', () => { // Arrange const { wrapper } = getShallowMountedComponent(); // Assert expect(wrapper).toBeTruthy(); }); }); describe('schedule page methods...', () => { test('getServiceZipCtuCodeFromStore should return zipCodeCtu', () => { // Arrange const { wrapper } = getShallowMountedComponent(); wrapper.vm.selectableDatesData = { days: [] }; // Act const testValue = wrapper.vm.getServiceZipCtuCodeFromStore(); // Assert expect(testValue).toStrictEqual('01234'); }); test('forwardButtonAction should call route method navigate', async () => { // Arrange const { wrapper } = getShallowMountedComponent(); wrapper.vm.$router.navigate = jest.fn(() => ({})); wrapper.vm.mainStore.saveSchedule = jest.fn(); wrapper.vm.navigationScenarios = { CLICKED_FORWARD: 'CLICKED_FORWARD' }; // Mock the serviceLocation ref using Object.defineProperty to bypass readonly Object.defineProperty(wrapper.vm.$refs, 'serviceLocation', { value: { forwardButtonAction: jest.fn() }, configurable: true }); wrapper.vm.selectedTimeSlotInfo = { timeSlot: { routeCode: 'test-id' } }; // Act await wrapper.vm.forwardButtonAction(); // Assert expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); }); test('recalAckModal should open when part requires recal, but recal not added to order', async () => { // Arrange const { wrapper } = getShallowMountedComponent(); wrapper.vm.mainStore.lineItems.glassParts = [ { partNumber: 'TEST123', requiresRecalibration: true } ]; // Act await wrapper.vm.forwardButtonAction(); // Assert expect(wrapper.vm.$refs[RECAL_ACK_MODAL_REF_NAME].openModal).toHaveBeenCalled(); }); test('navigation forward is blocked when recalAckModal is displayed, but checkbox has not been acknowledged', async () => { // Arrange const { wrapper } = getShallowMountedComponent(); wrapper.vm.mainStore.lineItems.glassParts = [ { partNumber: 'TEST123', requiresRecalibration: true } ]; // Act await wrapper.vm.forwardButtonAction(); await wrapper.vm.$refs[RECAL_ACK_MODAL_REF_NAME].openModal(); await wrapper.vm.$refs[RECAL_ACK_MODAL_REF_NAME].footerButtonClick(); // Assert expect(wrapper.vm.$router.navigate).not.toHaveBeenCalled(); }); test('navigation forward is allowed when checkbox has been acknowledged on recalAckModal', async () => { // Arrange const { wrapper } = getShallowMountedComponent(); wrapper.vm.mainStore.lineItems.glassParts = [ { partNumber: 'TEST123', requiresRecalibration: true } ]; // Act await wrapper.vm.$refs[RECAL_ACK_MODAL_REF_NAME].openModal(); wrapper.vm.updateIsRecalAcknowledged(true); await wrapper.vm.$refs[RECAL_ACK_MODAL_REF_NAME].footerButtonClick(); await wrapper.vm.forwardButtonAction(); // Assert expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); }); });