// @vitest-environment jsdom import { act } from "react"; import { createRoot } from "react-dom/client"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { useKeyboardShortcuts } from "./useKeyboardShortcuts"; // eslint-disable-next-line @typescript-eslint/no-explicit-any (globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; function TestHarness({ onNewIssue, onSearch, onToggleCollapse, onGoToInbox, }: { onNewIssue: () => void; onSearch?: () => void; onToggleCollapse?: () => void; onGoToInbox?: () => void; }) { useKeyboardShortcuts({ enabled: true, onNewIssue, onSearch, onToggleCollapse, onGoToInbox, }); return
keyboard shortcuts test
; } describe("useKeyboardShortcuts", () => { let container: HTMLDivElement; beforeEach(() => { container = document.createElement("div"); document.body.appendChild(container); }); afterEach(() => { container.remove(); }); it("ignores events already claimed by another handler", () => { const root = createRoot(container); const onNewIssue = vi.fn(); act(() => { root.render(); }); const event = new KeyboardEvent("keydown", { key: "c", bubbles: true, cancelable: true, }); event.preventDefault(); document.dispatchEvent(event); expect(onNewIssue).not.toHaveBeenCalled(); act(() => { root.unmount(); }); }); it("focuses the current page search target on slash", () => { const root = createRoot(container); const onSearch = vi.fn(); const input = document.createElement("input"); input.setAttribute("data-page-search-target", "true"); vi.spyOn(input, "getClientRects").mockReturnValue([{}] as unknown as DOMRectList); document.body.appendChild(input); act(() => { root.render(); }); document.dispatchEvent(new KeyboardEvent("keydown", { key: "/", bubbles: true, cancelable: true, })); expect(document.activeElement).toBe(input); expect(onSearch).not.toHaveBeenCalled(); act(() => { root.unmount(); }); input.remove(); }); it("falls back to quick search when the page has no search target", () => { const root = createRoot(container); const onSearch = vi.fn(); act(() => { root.render(); }); document.dispatchEvent(new KeyboardEvent("keydown", { key: "/", bubbles: true, cancelable: true, })); expect(onSearch).toHaveBeenCalledTimes(1); act(() => { root.unmount(); }); }); it("fires onToggleCollapse on Cmd/Ctrl+B", () => { const root = createRoot(container); const onToggleCollapse = vi.fn(); act(() => { root.render(); }); document.dispatchEvent(new KeyboardEvent("keydown", { key: "b", metaKey: true, bubbles: true, cancelable: true, })); expect(onToggleCollapse).toHaveBeenCalledTimes(1); document.dispatchEvent(new KeyboardEvent("keydown", { key: "b", ctrlKey: true, bubbles: true, cancelable: true, })); expect(onToggleCollapse).toHaveBeenCalledTimes(2); act(() => { root.unmount(); }); }); const pressKey = (key: string) => { const event = new KeyboardEvent("keydown", { key, bubbles: true, cancelable: true }); document.dispatchEvent(event); return event; }; it("navigates to the inbox on the g \u2192 i chord", () => { const root = createRoot(container); const onGoToInbox = vi.fn(); const onNewIssue = vi.fn(); act(() => { root.render(); }); // Bare "i" does nothing. pressKey("i"); expect(onGoToInbox).not.toHaveBeenCalled(); pressKey("g"); const chordEvent = pressKey("i"); expect(onGoToInbox).toHaveBeenCalledTimes(1); expect(chordEvent.defaultPrevented).toBe(true); // Chord disarms after firing. pressKey("i"); expect(onGoToInbox).toHaveBeenCalledTimes(1); expect(onNewIssue).not.toHaveBeenCalled(); act(() => { root.unmount(); }); }); it("swallows armed chord keys instead of firing bare shortcuts", () => { const root = createRoot(container); const onGoToInbox = vi.fn(); const onNewIssue = vi.fn(); act(() => { root.render(); }); // g \u2192 c is the issue-detail focus-comment chord; globally it must not // open the new-issue dialog. pressKey("g"); pressKey("c"); expect(onNewIssue).not.toHaveBeenCalled(); expect(onGoToInbox).not.toHaveBeenCalled(); // Bare "c" still creates. pressKey("c"); expect(onNewIssue).toHaveBeenCalledTimes(1); act(() => { root.unmount(); }); }); it("does not fire onToggleCollapse for a bare 'b' keypress", () => { const root = createRoot(container); const onToggleCollapse = vi.fn(); act(() => { root.render(); }); document.dispatchEvent(new KeyboardEvent("keydown", { key: "b", bubbles: true, cancelable: true, })); expect(onToggleCollapse).not.toHaveBeenCalled(); act(() => { root.unmount(); }); }); });