// @vitest-environment happy-dom import { act, cleanup, fireEvent, render } from "@testing-library/react"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { CLOSE_DRAWER_EVENT, OPEN_SPAWN_EVENT, SPAWN_SIMILAR_EVENT } from "../../uiEvents.js"; import { TOGGLE_DRAWER_EVENT } from "../../uiEvents.js"; import { Drawer } from "./Drawer.js"; /** Force the mobile media query on, whatever happy-dom's viewport says * (mirrors ../layout/MultiView's own `forceMobile` in multiview.test.ts). */ function forceMobile(matches: boolean): void { vi.spyOn(window, "matchMedia").mockImplementation( (query: string) => ({ matches: query.includes("859") && matches, media: query, onchange: null, addEventListener() {}, removeEventListener() {}, addListener() {}, removeListener() {}, dispatchEvent: () => false, }) as unknown as MediaQueryList, ); } function shell(): HTMLElement { return document.querySelector(".app-shell") as HTMLElement; } function touch(type: "touchstart" | "touchend", x: number, y: number): void { const touches = [{ clientX: x, clientY: y }]; const event = new Event(type, { bubbles: true }) as unknown as { touches: typeof touches; changedTouches: typeof touches; }; event.touches = touches; event.changedTouches = touches; act(() => { document.dispatchEvent(event as unknown as Event); }); } beforeEach(() => forceMobile(true)); afterEach(() => { cleanup(); vi.restoreAllMocks(); }); function openViaToggle(): void { act(() => { document.dispatchEvent(new CustomEvent(TOGGLE_DRAWER_EVENT)); }); } describe("Drawer (#35)", () => { it("toggles open on the toggle event, off on desktop by class alone", () => { render( row}>
content
, ); expect(shell().classList.contains("drawer-open")).toBe(false); openViaToggle(); expect(shell().classList.contains("drawer-open")).toBe(true); }); it("closes when a session row is tapped", () => { render( row}>
content
, ); openViaToggle(); fireEvent.click(document.querySelector(".tree-leaf") as HTMLElement); expect(shell().classList.contains("drawer-open")).toBe(false); }); it("closes when a spawn is requested", () => { render( row}>
content
, ); openViaToggle(); act(() => { document.dispatchEvent(new CustomEvent(OPEN_SPAWN_EVENT)); }); expect(shell().classList.contains("drawer-open")).toBe(false); }); it("closes when spawn similar is requested", () => { render( row}>
content
, ); openViaToggle(); act(() => { document.dispatchEvent(new CustomEvent(SPAWN_SIMILAR_EVENT, { detail: {} })); }); expect(shell().classList.contains("drawer-open")).toBe(false); }); it("closes on the close-drawer event (after spawn lands)", () => { render( row}>
content
, ); openViaToggle(); act(() => { document.dispatchEvent(new CustomEvent(CLOSE_DRAWER_EVENT)); }); expect(shell().classList.contains("drawer-open")).toBe(false); }); it("closes on a backdrop click", () => { render( row}>
content
, ); openViaToggle(); fireEvent.click(document.querySelector(".mv-backdrop") as HTMLElement); expect(shell().classList.contains("drawer-open")).toBe(false); }); it("opens on a rightward swipe and closes on a leftward one, only on mobile", () => { render( row}>
content
, ); touch("touchstart", 0, 100); touch("touchend", 100, 100); expect(shell().classList.contains("drawer-open")).toBe(true); touch("touchstart", 100, 100); touch("touchend", 0, 100); expect(shell().classList.contains("drawer-open")).toBe(false); }); it("ignores swipes on desktop", () => { forceMobile(false); render( row}>
content
, ); touch("touchstart", 0, 100); touch("touchend", 100, 100); expect(shell().classList.contains("drawer-open")).toBe(false); }); it("ignores a mostly-vertical or too-short gesture", () => { render( row}>
content
, ); touch("touchstart", 0, 0); touch("touchend", 10, 100); expect(shell().classList.contains("drawer-open")).toBe(false); }); });