mountSpawned now fires a CLOSE_DRAWER_EVENT so the sidebar gets out of the way once the new session lands. Covers fast-forward, modal, and command-bar spawn paths. Closes #261 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
168 lines
4.9 KiB
TypeScript
168 lines
4.9 KiB
TypeScript
// @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(
|
|
<Drawer sidebar={<div className="tree-leaf">row</div>}>
|
|
<div>content</div>
|
|
</Drawer>,
|
|
);
|
|
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(
|
|
<Drawer sidebar={<div className="tree-leaf">row</div>}>
|
|
<div>content</div>
|
|
</Drawer>,
|
|
);
|
|
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(
|
|
<Drawer sidebar={<div className="tree-leaf">row</div>}>
|
|
<div>content</div>
|
|
</Drawer>,
|
|
);
|
|
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(
|
|
<Drawer sidebar={<div className="tree-leaf">row</div>}>
|
|
<div>content</div>
|
|
</Drawer>,
|
|
);
|
|
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(
|
|
<Drawer sidebar={<div className="tree-leaf">row</div>}>
|
|
<div>content</div>
|
|
</Drawer>,
|
|
);
|
|
openViaToggle();
|
|
act(() => {
|
|
document.dispatchEvent(new CustomEvent(CLOSE_DRAWER_EVENT));
|
|
});
|
|
expect(shell().classList.contains("drawer-open")).toBe(false);
|
|
});
|
|
|
|
it("closes on a backdrop click", () => {
|
|
render(
|
|
<Drawer sidebar={<div className="tree-leaf">row</div>}>
|
|
<div>content</div>
|
|
</Drawer>,
|
|
);
|
|
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(
|
|
<Drawer sidebar={<div className="tree-leaf">row</div>}>
|
|
<div>content</div>
|
|
</Drawer>,
|
|
);
|
|
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(
|
|
<Drawer sidebar={<div className="tree-leaf">row</div>}>
|
|
<div>content</div>
|
|
</Drawer>,
|
|
);
|
|
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(
|
|
<Drawer sidebar={<div className="tree-leaf">row</div>}>
|
|
<div>content</div>
|
|
</Drawer>,
|
|
);
|
|
touch("touchstart", 0, 0);
|
|
touch("touchend", 10, 100);
|
|
expect(shell().classList.contains("drawer-open")).toBe(false);
|
|
});
|
|
});
|