// @vitest-environment happy-dom import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { fireEvent, screen } from "@testing-library/react"; import { defineCommands, keyCaps, publishCommands, publishedCommands } from "./commands.js"; import { closeHelp, groupedCommands, installHelpKeybind, isHelpOpen, openHelp } from "./help.js"; beforeEach(() => { installHelpKeybind(); }); afterEach(() => { closeHelp(); document.body.replaceChildren(); }); function press(key: string, init: KeyboardEventInit = {}): void { fireEvent.keyDown(window, { key, ...init }); } function overlay(): HTMLElement { return screen.getByRole("dialog", { name: "Keyboard shortcuts" }); } function rows(root: HTMLElement): string[] { return [...root.querySelectorAll(".help-row")].map((r) => [...r.querySelectorAll("kbd")].map((k) => k.textContent).join(" "), ); } describe("help overlay", () => { it("renders exactly the commands the registry has bound, grouped", () => { openHelp(); const dialog = overlay(); const groups = groupedCommands(); expect([...dialog.querySelectorAll(".help-group-title")].map((h) => h.textContent)).toEqual( groups.map((g) => g.title), ); expect(rows(dialog)).toEqual(groups.flatMap((g) => g.items.map((c) => keyCaps(c.keys[0]).join(" ")))); expect([...dialog.querySelectorAll(".help-desc")].map((d) => d.textContent)).toEqual( groups.flatMap((g) => g.items.map((c) => c.label)), ); }); it("names each row by its command id, so a row can't drift from its command", () => { openHelp(); const dialog = overlay(); expect( [...dialog.querySelectorAll(".help-row")].map((r) => r.dataset.commandId), ).toEqual(publishedCommands().map((c) => c.id)); }); // #159: the leader comes from the daemon, so the overlay must render the // configured chord — a hardcoded `Ctrl A` is the visible half of the bug. it("renders the configured leader's chord, not a hardcoded Ctrl A", () => { const bound = defineCommands( { "pane.literalCtrlK": () => {}, "pane.literalLeader": () => {} }, "ctrl-b", ); publishCommands(bound); try { openHelp(); const dialog = overlay(); const keysOf = (id: string) => [...dialog.querySelectorAll(`.help-row[data-command-id="${id}"] kbd`)] .map((k) => k.textContent) .join(" "); expect(keysOf("pane.literalCtrlK")).toBe("Ctrl B then Ctrl K"); expect(keysOf("pane.literalLeader")).toBe("Ctrl B then Ctrl B"); } finally { // Leave the registry as the rest of the suite expects it. publishCommands( defineCommands({ "pane.literalCtrlK": () => {}, "pane.literalLeader": () => {} }), ); } }); it("opens on ? and closes on a second ?", () => { press("?", { shiftKey: true }); expect(isHelpOpen()).toBe(true); press("?", { shiftKey: true }); expect(isHelpOpen()).toBe(false); }); it("closes on Escape, on the close button, and on a click outside the panel", () => { openHelp(); press("Escape"); expect(isHelpOpen()).toBe(false); openHelp(); fireEvent.click(screen.getByRole("button", { name: "Close" })); expect(isHelpOpen()).toBe(false); openHelp(); fireEvent.click(overlay()); expect(isHelpOpen()).toBe(false); }); it("stays open for a click inside the panel", () => { openHelp(); fireEvent.click(screen.getByRole("heading", { name: "Keyboard shortcuts", level: 2 })); expect(isHelpOpen()).toBe(true); }); it("opening twice leaves one overlay", () => { openHelp(); openHelp(); expect(screen.getAllByRole("dialog")).toHaveLength(1); }); it("ignores ? while typing and when a non-shift modifier is held", () => { const input = document.createElement("input"); document.body.appendChild(input); fireEvent.keyDown(input, { key: "?", shiftKey: true }); expect(isHelpOpen()).toBe(false); press("?", { ctrlKey: true }); expect(isHelpOpen()).toBe(false); }); it("installing the keybind twice still toggles once", () => { installHelpKeybind(); press("?", { shiftKey: true }); expect(isHelpOpen()).toBe(true); expect(screen.getAllByRole("dialog")).toHaveLength(1); }); });