// @vitest-environment jsdom import { defineComponent, h } from "vue"; import { mount } from "@vue/test-utils"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; const mocks = vi.hoisted(() => ({ authenticatedRequest: vi.fn(), toastSuccess: vi.fn(), toastError: vi.fn(), })); vi.mock("vue-router", async () => { const { ref } = await import("vue"); const currentRoute = ref({ params: { departmentId: "12", }, }); const push = vi.fn(); return { useRouter: () => ({ currentRoute, push, }), __routeRef: currentRoute, __pushMock: push, }; }); vi.mock("@/components/session/authenticatedRequest.vue", () => ({ authenticatedRequest: mocks.authenticatedRequest, })); vi.mock("vue-toast-notification", () => ({ useToast: () => ({ success: mocks.toastSuccess, error: mocks.toastError, }), })); vi.mock("vue-i18n", () => ({ useI18n: () => ({ t: (key) => key, }), })); vi.mock("buefy", () => ({ BSwitch: defineComponent({ name: "MockSwitch", props: { modelValue: { type: Boolean, default: false, }, disabled: { type: Boolean, default: false, }, }, emits: ["update:model-value"], setup(props, { emit }) { return () => h("button", { "data-testid": "self-serve-switch", "data-state": props.modelValue ? "on" : "off", "disabled": props.disabled, onClick: () => emit("update:model-value", !props.modelValue), }); }, }), })); import DepartmentModulesDisplay from "@/components/displays/department/moduleNavigation/DepartmentModulesDisplay.vue"; import { __pushMock, __routeRef } from "vue-router"; const flushMicrotasks = async () => { await Promise.resolve(); await Promise.resolve(); }; describe("DepartmentModulesDisplay self-serve management", () => { let setIntervalSpy; let consoleErrorSpy; beforeEach(() => { mocks.authenticatedRequest.mockReset(); mocks.toastSuccess.mockReset(); mocks.toastError.mockReset(); __routeRef.value = { params: { departmentId: "12" } }; setIntervalSpy = vi.spyOn(globalThis, "setInterval").mockImplementation(() => 1); consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {}); mocks.authenticatedRequest.mockImplementation(async (url, method) => { if (String(url).startsWith("/admin/bookings/department/count?department_id=12")) { return { data: { data: { message: 0, }, }, }; } if (url === "/departments/self-serve/enabled?id=12" && method === "GET") { return { data: { data: { enabled: false, }, }, }; } if (url === "/departments/self-serve/enabled?id=12&enabled=true" && method === "PUT") { return { data: { data: { enabled: true, }, }, }; } if (url === "/departments/self-serve/enabled?id=12&enabled=false" && method === "PUT") { return { data: { data: { enabled: false, }, }, }; } throw new Error(`Unexpected request: ${method} ${url}`); }); }); afterEach(() => { setIntervalSpy.mockRestore(); consoleErrorSpy.mockRestore(); }); it("loads the current self-serve status on mount", async () => { const wrapper = mount(DepartmentModulesDisplay); await flushMicrotasks(); expect(mocks.authenticatedRequest).toHaveBeenCalledWith("/departments/self-serve/enabled?id=12", "GET"); expect(wrapper.get("[data-testid='self-serve-switch']").attributes("data-state")).toBe("off"); }); it("renders responsive overview columns for each module card", async () => { const wrapper = mount(DepartmentModulesDisplay); await flushMicrotasks(); const firstColumn = wrapper.find(".column"); expect(firstColumn.classes()).toEqual(expect.arrayContaining([ "is-12-mobile", "is-6-tablet", "is-4-desktop", "is-3-widescreen", ])); }); it("updates self-serve status and keeps UI in sync on success", async () => { const wrapper = mount(DepartmentModulesDisplay); await flushMicrotasks(); await wrapper.get("[data-testid='self-serve-switch']").trigger("click"); await flushMicrotasks(); expect(mocks.authenticatedRequest).toHaveBeenCalledWith( "/departments/self-serve/enabled?id=12&enabled=true", "PUT" ); expect(wrapper.get("[data-testid='self-serve-switch']").attributes("data-state")).toBe("on"); expect(mocks.toastSuccess).toHaveBeenCalledWith("admin.department_modules.self_wash.enabled"); }); it("keeps the self-serve switch isolated from card navigation", async () => { const wrapper = mount(DepartmentModulesDisplay); await flushMicrotasks(); __pushMock.mockClear(); await wrapper.get("[data-testid='self-serve-switch']").trigger("click"); await flushMicrotasks(); expect(__pushMock).not.toHaveBeenCalled(); }); it("keeps the bookings count badge isolated from the card click handler", async () => { mocks.authenticatedRequest.mockImplementation(async (url, method) => { if (String(url).startsWith("/admin/bookings/department/count?department_id=12")) { return { data: { data: { message: 5, }, }, }; } if (url === "/departments/self-serve/enabled?id=12" && method === "GET") { return { data: { data: { enabled: false, }, }, }; } throw new Error(`Unexpected request: ${method} ${url}`); }); const wrapper = mount(DepartmentModulesDisplay); await flushMicrotasks(); __pushMock.mockClear(); await wrapper.get(".department-module-card__count").trigger("click"); expect(__pushMock).toHaveBeenCalledTimes(1); expect(__pushMock).toHaveBeenCalledWith("/admin/12/modules/bookings?search=pending"); }); it("reverts self-serve status and shows an error when update fails", async () => { mocks.authenticatedRequest.mockImplementation(async (url, method) => { if (String(url).startsWith("/admin/bookings/department/count?department_id=12")) { return { data: { data: { message: 0, }, }, }; } if (url === "/departments/self-serve/enabled?id=12" && method === "GET") { return { data: { data: { enabled: false, }, }, }; } if (url === "/departments/self-serve/enabled?id=12&enabled=true" && method === "PUT") { throw new Error("save failed"); } throw new Error(`Unexpected request: ${method} ${url}`); }); const wrapper = mount(DepartmentModulesDisplay); await flushMicrotasks(); await wrapper.get("[data-testid='self-serve-switch']").trigger("click"); await flushMicrotasks(); expect(wrapper.get("[data-testid='self-serve-switch']").attributes("data-state")).toBe("off"); expect(mocks.toastError).toHaveBeenCalledWith("admin.department_modules.self_wash.update_error"); }); });