diff --git a/src/layouts/scheduling/inshop-scheduling-card/inshop-scheduling-card.spec.js b/src/layouts/scheduling/inshop-scheduling-card/inshop-scheduling-card.spec.js index bfbade779..09647f4ca 100644 --- a/src/layouts/scheduling/inshop-scheduling-card/inshop-scheduling-card.spec.js +++ b/src/layouts/scheduling/inshop-scheduling-card/inshop-scheduling-card.spec.js @@ -243,4 +243,27 @@ describe("inshop-scheduling-card.vue", () => { expect(wrapper.find(".inshop-scheduling-card__body").isVisible()).toBe(false); wrapper.unmount(); }); + + describe("isLoading prop", () => { + test("shows the skeleton loader and hides real content when isLoading is true", () => { + const wrapper = mountComponent({ isLoading: true }); + expect(wrapper.find("scheduling-card-loader-stub").exists()).toBe(true); + expect(wrapper.find(".inshop-scheduling-card__header").exists()).toBe(false); + wrapper.unmount(); + }); + + test("hides the skeleton loader and shows real content when isLoading is false", () => { + const wrapper = mountComponent({ isLoading: false }); + expect(wrapper.find("scheduling-card-loader-stub").exists()).toBe(false); + expect(wrapper.find(".inshop-scheduling-card__header").exists()).toBe(true); + wrapper.unmount(); + }); + + test("defaults isLoading to false", () => { + const wrapper = mountComponent(); + expect(wrapper.props("isLoading")).toBe(false); + expect(wrapper.find("scheduling-card-loader-stub").exists()).toBe(false); + wrapper.unmount(); + }); + }); }); diff --git a/src/layouts/scheduling/mobile-scheduling-card/mobile-scheduling-card.spec.js b/src/layouts/scheduling/mobile-scheduling-card/mobile-scheduling-card.spec.js index 6200deea3..c771b6e9a 100644 --- a/src/layouts/scheduling/mobile-scheduling-card/mobile-scheduling-card.spec.js +++ b/src/layouts/scheduling/mobile-scheduling-card/mobile-scheduling-card.spec.js @@ -167,4 +167,33 @@ describe("mobile-scheduling-card.vue", () => { expect(wrapper.find(".mobile-scheduling-card__body").isVisible()).toBe(true); wrapper.unmount(); }); + + describe("isLoading prop", () => { + test("shows the skeleton loader and hides real content when isLoading is true", () => { + const wrapper = mountComponent({ isLoading: true }); + expect(wrapper.find("scheduling-card-loader-stub").exists()).toBe(true); + expect(wrapper.find(".mobile-scheduling-card__header").exists()).toBe(false); + wrapper.unmount(); + }); + + test("hides the skeleton loader and shows real content when isLoading is false", () => { + const wrapper = mountComponent({ isLoading: false }); + expect(wrapper.find("scheduling-card-loader-stub").exists()).toBe(false); + expect(wrapper.find(".mobile-scheduling-card__header").exists()).toBe(true); + wrapper.unmount(); + }); + + test("hides the free flag when isLoading is true even if showFreeFlag is true", () => { + const wrapper = mountComponent({ isLoading: true, showFreeFlag: true }); + expect(wrapper.find(".mobile-scheduling-card__free-flag").exists()).toBe(false); + wrapper.unmount(); + }); + + test("defaults isLoading to false", () => { + const wrapper = mountComponent(); + expect(wrapper.props("isLoading")).toBe(false); + expect(wrapper.find("scheduling-card-loader-stub").exists()).toBe(false); + wrapper.unmount(); + }); + }); }); diff --git a/src/layouts/scheduling/scheduling-card-loader/scheduling-card-loader.spec.js b/src/layouts/scheduling/scheduling-card-loader/scheduling-card-loader.spec.js new file mode 100644 index 000000000..2af43dcb3 --- /dev/null +++ b/src/layouts/scheduling/scheduling-card-loader/scheduling-card-loader.spec.js @@ -0,0 +1,25 @@ +import { shallowMount } from "@vue/test-utils"; +import schedulingCardLoader from "./scheduling-card-loader"; + +describe("scheduling-card-loader.vue", () => { + test("renders the loader container with aria-hidden", () => { + const wrapper = shallowMount(schedulingCardLoader); + expect(wrapper.find(".scheduling-card-loader").exists()).toBe(true); + expect(wrapper.find(".scheduling-card-loader").attributes("aria-hidden")).toBe("true"); + wrapper.unmount(); + }); + + test("renders exactly two skeleton rows", () => { + const wrapper = shallowMount(schedulingCardLoader); + expect(wrapper.findAll(".scheduling-card-loader__row").length).toBe(2); + wrapper.unmount(); + }); + + test("each row contains a wide shimmer bar", () => { + const wrapper = shallowMount(schedulingCardLoader); + wrapper.findAll(".scheduling-card-loader__row").forEach((row) => { + expect(row.find(".scheduling-card-loader__bar--wide").exists()).toBe(true); + }); + wrapper.unmount(); + }); +}); diff --git a/src/layouts/scheduling/scheduling.spec.js b/src/layouts/scheduling/scheduling.spec.js new file mode 100644 index 000000000..a7a97a3a2 --- /dev/null +++ b/src/layouts/scheduling/scheduling.spec.js @@ -0,0 +1,110 @@ +import { shallowMount } from "@vue/test-utils"; +import scheduling from "./scheduling"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import { settleAllPromises } from "@/helpers/layout-helper"; + +jest.mock("@/store", () => ({ + dispatch: jest.fn().mockResolvedValue(null), + getters: { + order: { + serviceLocation: { zipCode: "43235", appointmentType: null }, + payment: { isInsurance: false, insuranceCoverage: { isVerified: false } }, + referralNumber: "", + damage: { isRepair: false }, + lineItems: { glassParts: [] }, + policy: { isItac: false, isNoComp: false }, + }, + }, +})); + +jest.mock("@/helpers/layout-helper", () => ({ + settleAllPromises: jest.fn().mockResolvedValue({}), +})); + +jest.mock("@/helpers/cms-content-helper", () => ({ + fetchCmsContentForPage: jest.fn().mockResolvedValue({}), +})); + +jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({ + navigateToHeritageFunnel: jest.fn(), +})); + +jest.mock("@/helpers/debug-log-helper", () => ({ + debugLog: jest.fn(), +})); + +jest.mock("@/helpers/page-prerequisites-helper.js", () => ({ + flushPagePrereqsLogs: jest.fn(), + hasServiceZipInfo: jest.fn(() => true), + hasGlassPartsOrRepairInfo: jest.fn(() => true), + hasInsuranceInfo: jest.fn(() => true), +})); + +function setupMocks() { + const baseMixin = { + methods: { + getCmsContent: jest.fn(() => ""), + setCmsContent: jest.fn(), + getTotalLineItemPrice: jest.fn(() => 0), + }, + }; + const mountOptions = getMountOptions({ + route: { name: "scheduling" }, + router: { + navigate: jest.fn(), + navigateWithSaving: jest.fn(), + navigateWithoutSaving: jest.fn(), + }, + }); + mountOptions.global.mixins = [baseMixin]; + const wrapper = shallowMount(scheduling, mountOptions); + return { wrapper }; +} + +describe("scheduling.vue", () => { + describe("intercept overlay", () => { + test("does not render interceptOverlay when isLoadingDates is false", () => { + const { wrapper } = setupMocks(); + expect(wrapper.find("intercept-overlay-stub").exists()).toBe(false); + wrapper.unmount(); + }); + + test("renders interceptOverlay when isLoadingDates is true", async () => { + const { wrapper } = setupMocks(); + await wrapper.setData({ isLoadingDates: true }); + expect(wrapper.find("intercept-overlay-stub").exists()).toBe(true); + wrapper.unmount(); + }); + }); + + describe("handleRequestMoreDates", () => { + test("sets isLoadingDates to true while fetching and false after resolving", async () => { + let resolve; + settleAllPromises.mockReturnValueOnce( + new Promise((r) => { + resolve = r; + }) + ); + + const { wrapper } = setupMocks(); + const fetchPromise = wrapper.vm.handleRequestMoreDates(); + + expect(wrapper.vm.isLoadingDates).toBe(true); + + resolve({}); + await fetchPromise; + + expect(wrapper.vm.isLoadingDates).toBe(false); + wrapper.unmount(); + }); + + test("completes without invoking loadingModal methods (stub has none)", async () => { + // The loadingModal stub rendered by shallowMount has no showModal/hideModal. + // If those calls were still in handleRequestMoreDates they would throw here. + settleAllPromises.mockResolvedValueOnce({}); + const { wrapper } = setupMocks(); + await expect(wrapper.vm.handleRequestMoreDates()).resolves.toBeUndefined(); + wrapper.unmount(); + }); + }); +}); diff --git a/src/ux-components/intercept-overlay/intercept-overlay.spec.js b/src/ux-components/intercept-overlay/intercept-overlay.spec.js new file mode 100644 index 000000000..f3854a47a --- /dev/null +++ b/src/ux-components/intercept-overlay/intercept-overlay.spec.js @@ -0,0 +1,17 @@ +import { shallowMount } from "@vue/test-utils"; +import interceptOverlay from "./intercept-overlay"; + +describe("intercept-overlay.vue", () => { + test("renders the overlay container with aria-hidden", () => { + const wrapper = shallowMount(interceptOverlay); + expect(wrapper.find(".intercept-overlay").exists()).toBe(true); + expect(wrapper.find(".intercept-overlay").attributes("aria-hidden")).toBe("true"); + wrapper.unmount(); + }); + + test("renders as a single root element with no visible content", () => { + const wrapper = shallowMount(interceptOverlay); + expect(wrapper.text()).toBe(""); + wrapper.unmount(); + }); +});