import { describe, it, expect, vi, afterEach } from "vitest"; import { act, render, screen } from "@testing-library/react"; import { useMediaQuery, useIsMobile } from "../../src/hooks/useMediaQuery"; // A controllable matchMedia mock: lets a test flip `matches` and fire the change event the hook // subscribes to, so we can assert the hook re-renders with the new value. function installMatchMedia(initialMatches: boolean) { let matches = initialMatches; const listeners = new Set<(e: MediaQueryListEvent) => void>(); const mql = { get matches() { return matches; }, media: "", onchange: null, addEventListener: vi.fn((_type: string, cb: (e: MediaQueryListEvent) => void) => { listeners.add(cb); }), removeEventListener: vi.fn((_type: string, cb: (e: MediaQueryListEvent) => void) => { listeners.delete(cb); }), addListener: vi.fn(), removeListener: vi.fn(), dispatchEvent: vi.fn(), }; window.matchMedia = vi.fn().mockReturnValue(mql) as unknown as typeof window.matchMedia; return { mql, set(next: boolean) { matches = next; listeners.forEach((cb) => cb({ matches } as MediaQueryListEvent)); }, }; } function Probe({ query }: { query: string }) { const value = useMediaQuery(query); return {value ? "match" : "no-match"}; } afterEach(() => { vi.restoreAllMocks(); }); describe("useMediaQuery", () => { it("reflects the initial matches value", () => { installMatchMedia(true); render(); expect(screen.getByText("match")).toBeInTheDocument(); }); it("updates when the media query changes", () => { const ctl = installMatchMedia(false); render(); expect(screen.getByText("no-match")).toBeInTheDocument(); act(() => ctl.set(true)); expect(screen.getByText("match")).toBeInTheDocument(); }); it("removes its listener on unmount", () => { const ctl = installMatchMedia(false); const { unmount } = render(); expect(ctl.mql.addEventListener).toHaveBeenCalledTimes(1); unmount(); expect(ctl.mql.removeEventListener).toHaveBeenCalledTimes(1); }); it("treats a missing matchMedia as desktop (no match)", () => { // @ts-expect-error simulate an environment without matchMedia window.matchMedia = undefined; render(); expect(screen.getByText("no-match")).toBeInTheDocument(); }); }); describe("useIsMobile", () => { function MobileProbe() { return {useIsMobile() ? "mobile" : "desktop"}; } it("is mobile when the viewport is at or below the breakpoint", () => { installMatchMedia(true); render(); expect(screen.getByText("mobile")).toBeInTheDocument(); }); it("is desktop when the viewport is above the breakpoint", () => { installMatchMedia(false); render(); expect(screen.getByText("desktop")).toBeInTheDocument(); }); });