// Components import schedule from "@/layouts/schedule/schedule.vue"; // Supporting Files import { shallowMount } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; import store from "@/store"; import router from "@/router"; import baseMixin from "../../mixins/base-mixin"; // Mock basemixin jest.mock("@/mixins/base-mixin.js", () => ({ methods: { dispatchStoreAction: jest.fn().mockImplementation((storeAction) => { if (storeAction === "getShopTimeSlots" || storeAction === "getMobileTimeSlots") { return { 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, }, ], }, ], }, }; } if (storeAction === "getMobilePremiumFee") { return Promise.resolve({ data: { partNumber: "EARLY BIRD", description: null, partType: "EARLY BIRD", laborAmount: 0, sellingPrice: 14.99, kitPrice: 0, }, }); } if (storeAction === "priceOrderItemsAndSaveServerData") { return Promise.resolve([ { partNumber: "EARLY BIRD", description: null, partType: "EARLY BIRD", laborAmount: 0, sellingPrice: 14.99, kitPrice: 0, }, ]); } if (storeAction === "saveSupportingItemsSuppressingStateResetting") { return Promise.resolve([ { partNumber: "EARLY BIRD", description: null, partType: "EARLY BIRD", laborAmount: 0, sellingPrice: 14.99, kitPrice: 0, }, ]); } }), dispatchStoreActionWithLogging: jest.fn().mockImplementation((storeAction) => { if (storeAction === "getShopTimeSlots" || storeAction === "getMobileTimeSlots") { return { 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, }, ], }, ], }, }; } if (storeAction === "getMobilePremiumFee") { return Promise.resolve({ data: { partNumber: "EARLY BIRD", description: null, partType: "EARLY BIRD", laborAmount: 0, sellingPrice: 14.99, kitPrice: 0, }, }); } if (storeAction === "priceOrderItemsAndSaveServerData") { return Promise.resolve([ { partNumber: "EARLY BIRD", description: null, partType: "EARLY BIRD", laborAmount: 0, sellingPrice: 14.99, kitPrice: 0, }, ]); } if (storeAction === "saveSupportingItemsSuppressingStateResetting") { return Promise.resolve([ { partNumber: "EARLY BIRD", description: null, partType: "EARLY BIRD", laborAmount: 0, sellingPrice: 14.99, kitPrice: 0, }, ]); } }), filterOutCertainPartTypesOrNumbers: jest.fn(), hasSubmittedOrder: jest.fn(), getTotalPriceOfAllLineItemsAndChildParts: jest.fn(), getTotalLineItemPrice: jest.fn(), }, })); // Mock fetchCmsContentForPage jest.mock("@/helpers/cms-content-helper", () => ({ fetchCmsContentForPage: () => Promise.resolve("content"), splitCopyOnCMSPlaceHolder: jest.fn(() => ["A", "B"]), })); beforeEach(() => { jest.restoreAllMocks(); jest.clearAllMocks(); store.getters = { applicationUser: { experiments: [], }, 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: [], }, experimentSettings: {}, }; }); afterEach(() => { store.getters = {}; jest.restoreAllMocks(); jest.clearAllMocks(); }); describe("schedule.vue...", () => { describe("initial load", () => { test("should pass arePagePrerequisitesValid with a mobile order and no providerNumber", () => { // Arrange const { wrapper } = setupMocks({}); store.getters.order.serviceLocation.appointmentType = "Mobile"; store.getters.order.serviceLocation.provider = null; // Act const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); // Assert expect(arePagePrerequisitesValid).toBe(true); }); test("should pass arePagePrerequisitesValid with a inshop order and providerNumber", () => { // Arrange const { wrapper } = setupMocks({}); // Act const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); // Assert expect(arePagePrerequisitesValid).toBe(true); }); test("should fail arePagePrerequisitesValid with a replace with no glass parts", async () => { // Arrange const { wrapper } = setupMocks({}); store.getters.order.lineItems.glassParts = []; // Act const arePagePrerequisitesValid2 = await wrapper.vm.arePagePrerequisitesValid(); // Assert expect(arePagePrerequisitesValid2).toBe(false); }); test("should fail arePagePrerequisitesValid without isInsurance", () => { // Arrange const { wrapper } = setupMocks({}); store.getters.payment.isInsurance = null; // Act const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); // Assert expect(arePagePrerequisitesValid).toBe(false); }); test("should return timeslots when getMoreScheduleData is called", async () => { // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesInshop = { days: [], }; wrapper.vm.selectableDatesMobile = { days: [], }; // Act const newShopTimeSlots = await wrapper.vm.getMoreScheduleData( "2023-01-01", "2023-01-31" ); // Assert expect(newShopTimeSlots).toStrictEqual({ inshopTimeSlotsData: { 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, }, mobileTimeSlotsData: { 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 day ranges of 34 or less when getMoreScheduleData is called with large date ranges", async () => { // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesInshop = { days: [], }; wrapper.vm.selectableDatesMobile = { days: [], }; // Act await wrapper.vm.getMoreScheduleData.call( wrapper.vm, "2023-01-01", "2023-03-31", "Inshop", "123" ); // Assert expect(baseMixin.methods.dispatchStoreActionWithLogging).toHaveBeenCalledTimes(6); expect(baseMixin.methods.dispatchStoreActionWithLogging).toHaveBeenCalledWith( "getShopTimeSlots", expect.anything(), expect.anything(), expect.anything() ); }); describe("beforeRouteEnter function... ", () => { test("should call next() and call all functions within next", async () => { // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesInshop = { days: [], }; wrapper.vm.updateFooterButtonText = jest.fn(); wrapper.vm.setDisplayWaitList = jest.fn(); const nextFunction = jest.fn((c) => { c(wrapper.vm); }); // Act await schedule.beforeRouteEnter.call( wrapper.vm, { query: { fmgPage: "schedule" } }, undefined, nextFunction ); // Assert expect(nextFunction).toHaveBeenCalled(); expect(wrapper.vm.setCmsContent).toHaveBeenCalledWith("content"); expect(wrapper.vm.$refs.datePicker.initializeComponent).toHaveBeenCalledWith( expect.objectContaining({ calendarViewDirection: "future", }) ); expect(wrapper.vm.$refs.locationAlerts.initializeComponent).toHaveBeenCalled(); expect(wrapper.vm.selectableDatesInshop).toStrictEqual( expect.objectContaining({ days: expect.any(Array), estimatedServiceMinutesMaximum: expect.any(Number), estimatedServiceMinutesMinimum: expect.any(Number), }) ); expect(wrapper.vm.mobilePremiumAppointmentFee).toStrictEqual( expect.objectContaining({ partNumber: expect.any(String), }) ); expect(wrapper.vm.updateFooterButtonText).toHaveBeenCalled(); expect(wrapper.vm.setDisplayWaitList).toHaveBeenCalled(); }); }); describe("computed properties...", () => { test("timeSlotsForSelectedDate should return timeslots if selected date is available", () => { // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesInshop = { days: [ { date: "2022-11-11", timeSlots: [ { id: "1820I-01820-M-I*20425*AM", startTime: "08:00", endTime: "12:00", offerPremium: true, }, { id: "1820I-01820-M-I*20425*PM", startTime: "12:00", endTime: "17:00", offerPremium: false, }, ], }, ], }; wrapper.setData({ selectedDate: "2022-11-11", }); // Act const testValue = wrapper.vm.timeSlotsForSelectedDate; // Assert expect(testValue).toStrictEqual( expect.objectContaining({ date: "2022-11-11", }) ); }); test("timeSlotsForSelectedDate should be null if no date has been selected", () => { // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesInshop = { days: [ { date: "2022-11-11", timeSlots: [ { id: "1820I-01820-M-I*20425*AM", startTime: "08:00", endTime: "12:00", offerPremium: true, }, { id: "1820I-01820-M-I*20425*PM", startTime: "12:00", endTime: "17:00", offerPremium: false, }, ], }, ], }; wrapper.setData({ selectedDate: undefined, }); // Act const testValue = wrapper.vm.timeSlotsForSelectedDate; // Assert expect(testValue).toBe(null); }); }); }); describe("schedule page methods...", () => { test("getServiceZipCtuCodeFromStore should return zipCodeCtu", () => { // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesInshop = { days: [], }; // Act const testValue = wrapper.vm.getServiceZipCtuCodeFromStore(); // Assert expect(testValue).toStrictEqual("01234"); }); test("getDisplayTextForMilitaryTime should return the correctly formatted string", () => { // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesInshop = { days: [], }; const timeInput1 = "15:00"; const timeInput2 = "15:30"; // Act const testOutput1 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput1); const testOutput2 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput2); const testOutput3 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput1, true); const testOutput4 = wrapper.vm.getDisplayTextForMilitaryTime(timeInput2, true); // Assert expect(testOutput1).toBe("3:00 PM"); expect(testOutput2).toBe("3:30 PM"); expect(testOutput3).toBe("3 PM"); expect(testOutput4).toBe("3:30 PM"); }); test("Clicking back should fire correct navigation", () => { // Arrange const { wrapper } = setupMocks({}); wrapper.vm.selectableDatesInshop = { days: [], }; wrapper.vm.$router.navigateWithoutSaving = jest.fn(); // Act wrapper.vm.backButtonAction(); // Assert expect(wrapper.vm.$router.navigateWithoutSaving).toBeCalledWith( "CLICKED_BACK", "schedule" ); }); }); test("forwardButtonAction should call route method navigateWithoutSaving", async () => { // Arrange const { wrapper } = setupMocks({}); wrapper.vm.dispatchStoreAction = jest.fn(() => { return { data: [], }; }); wrapper.vm.$router.navigateWithSaving = jest.fn(() => { return {}; }); // Act await wrapper.vm.forwardButtonAction(); // Assert expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled(); }); test("for Inshop appts, updateSupportingItems should call store action to save supporting items WITHOUT the EARLY BIRD supporting item", async () => { // Arrange store.getters.order.serviceLocation.appointmentType = "Inshop"; store.getters.lineItems.supportingItems = [ { partNumber: "EARLY BIRD", description: null, partType: "EARLY BIRD", laborAmount: 0, sellingPrice: 0, kitPrice: 0, }, ]; const { wrapper } = setupMocks({}); wrapper.vm.dispatchStoreAction = jest.fn(() => { return { data: [], }; }); wrapper.vm.mobilePremiumAppointmentFee = 14.99; wrapper.setData({ selectedTimeSlot: { date: "2019-01-01", startTime: "09:00", endTime: "10:00", routeCode: null, isPremiumAppointment: true, }, }); // Act await wrapper.vm.updateSupportingItems(); // Assert expect(wrapper.vm.dispatchStoreAction).toBeCalledWith( "saveSupportingItemsSuppressingStateResetting", expect.not.arrayContaining([ expect.objectContaining({ partType: "EARLY BIRD", }), ]), expect.anything() ); }); }); const mockCmsContent = {}; function setupMocks({ customMountOptions }) { const mountOptions = getMountOptions({ ...customMountOptions, route: { name: "schedule" }, }); mountOptions.global.mocks["$store"] = store; mountOptions.global.mocks["$router"] = router; mountOptions["attachTo"] = document.body; mountOptions.mixins = [ { methods: { getCmsContent: jest.fn().mockImplementation((widgetName, fieldName) => { if (mockCmsContent[widgetName] && mockCmsContent[widgetName][fieldName]) return mockCmsContent[widgetName][fieldName]; }), }, }, ]; mountOptions.global.mocks.pageName = "schedule"; const wrapper = shallowMount(schedule, mountOptions); wrapper.vm.setCmsContent = jest.fn(); wrapper.vm.$refs.datePicker.initializeComponent = jest.fn(); wrapper.vm.$refs.locationAlerts.initializeComponent = jest.fn(); wrapper.vm.$refs.navbar.updateButtonText = jest.fn(); return { wrapper }; }