Closes #250: kill confirmation/force-kill (dialogs/KillDialog), the ? shortcuts overlay (dialogs/HelpOverlay), the general-purpose right-click menu (common/ContextMenu, closes on scroll/resize unlike the sidebar's own), the mobile drawer with swipe support (layout/Drawer), and the visual-viewport hook (hooks/useViewport). Each is a standalone, tested port of its vanilla-DOM source; wiring into the shell is a later issue.
69 lines
2.4 KiB
TypeScript
69 lines
2.4 KiB
TypeScript
// @vitest-environment happy-dom
|
|
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
|
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
import { ContextMenu } from "./ContextMenu.js";
|
|
|
|
afterEach(() => {
|
|
cleanup();
|
|
});
|
|
|
|
function labels(): string[] {
|
|
return [...document.querySelectorAll(".context-menu-item")].map((b) => b.textContent ?? "");
|
|
}
|
|
|
|
describe("ContextMenu (#125)", () => {
|
|
it("renders one item per entry, in order", () => {
|
|
render(
|
|
<ContextMenu
|
|
x={10}
|
|
y={20}
|
|
items={[
|
|
{ label: "Rename…", onSelect: () => {} },
|
|
{ label: "Kill session", onSelect: () => {}, danger: true },
|
|
]}
|
|
onClose={vi.fn()}
|
|
/>,
|
|
);
|
|
expect(labels()).toEqual(["Rename…", "Kill session"]);
|
|
expect(document.querySelector(".context-menu-item.is-danger")?.textContent).toBe("Kill session");
|
|
});
|
|
|
|
it("runs the selected entry and closes", () => {
|
|
const chosen = vi.fn();
|
|
const onClose = vi.fn();
|
|
render(<ContextMenu x={0} y={0} items={[{ label: "Rename…", onSelect: chosen }]} onClose={onClose} />);
|
|
fireEvent.click(screen.getByRole("menuitem"));
|
|
expect(chosen).toHaveBeenCalledOnce();
|
|
expect(onClose).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("closes on Escape and on a press outside it", () => {
|
|
const onClose = vi.fn();
|
|
render(<ContextMenu x={0} y={0} items={[{ label: "Rename…", onSelect: () => {} }]} onClose={onClose} />);
|
|
fireEvent.keyDown(document, { key: "Escape" });
|
|
expect(onClose).toHaveBeenCalledTimes(1);
|
|
|
|
fireEvent(document.body, new Event("pointerdown", { bubbles: true }));
|
|
expect(onClose).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it("stays open for a press inside itself", () => {
|
|
const onClose = vi.fn();
|
|
render(<ContextMenu x={0} y={0} items={[{ label: "Rename…", onSelect: () => {} }]} onClose={onClose} />);
|
|
const menu = document.querySelector(".context-menu") as HTMLElement;
|
|
fireEvent(menu, new Event("pointerdown", { bubbles: true }));
|
|
expect(onClose).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("closes on scroll and on resize", () => {
|
|
const onClose = vi.fn();
|
|
render(<ContextMenu x={0} y={0} items={[{ label: "Rename…", onSelect: () => {} }]} onClose={onClose} />);
|
|
fireEvent.scroll(window);
|
|
expect(onClose).toHaveBeenCalledTimes(1);
|
|
|
|
fireEvent.resize(window);
|
|
expect(onClose).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|