/** @jsxImportSource react */ import { expect, test } from "bun:test"; import { act } from "react"; import { useShortcut, type KeyEventLike } from "../../../react/input"; import { WebInputHostProvider } from "./input-host"; import { createDomTestHarness } from "./test-utils"; const { window: testWindow, render: renderDom } = createDomTestHarness({ withUi: false }); function Probe({ onKey }: { onKey: (event: KeyEventLike) => void }) { useShortcut(onKey, { phase: "before" }); return null; } async function mountProbe(onKey?: (event: KeyEventLike) => void): Promise { const seen: KeyEventLike[] = []; await renderDom( { seen.push(event); onKey?.(event); }} /> , ); return seen; } async function pressKey( key: string, options: { target?: unknown; shiftKey?: boolean; metaKey?: boolean; ctrlKey?: boolean } = {}, ): Promise<{ defaultPrevented: boolean }> { const event = new testWindow.KeyboardEvent("keydown", { key, bubbles: true, cancelable: true, shiftKey: options.shiftKey ?? false, metaKey: options.metaKey ?? false, ctrlKey: options.ctrlKey ?? false, }); const target = (options.target ?? testWindow.document.body) as { dispatchEvent: (event: unknown) => void }; await act(async () => { target.dispatchEvent(event); await Promise.resolve(); }); return { defaultPrevented: event.defaultPrevented }; } test("delivers plain Tab and Shift-Tab outside editable controls", async () => { const seen = await mountProbe(); await pressKey("Tab"); await pressKey("Tab", { shiftKey: true }); expect(seen.map((event) => ({ name: event.name, shift: event.shift, editable: event.targetEditable }))) .toEqual([ { name: "tab", shift: false, editable: false }, { name: "tab", shift: true, editable: false }, ]); }); test("leaves Tab to the field when an editable control owns the focus", async () => { const seen = await mountProbe(); const input = testWindow.document.createElement("input"); testWindow.document.body.appendChild(input); const result = await pressKey("Tab", { target: input }); // The event still reaches the registry, but the shared model refuses to // deliver an unmodified key to a handler while a field is focused, and the // host must not swallow the browser's own focus move either. expect(seen).toEqual([]); expect(result.defaultPrevented).toBe(false); input.remove(); }); test("reports Cmd-digit as a meta shortcut so layout switching can claim it", async () => { const seen = await mountProbe(); await pressKey("2", { metaKey: true }); await pressKey("3", { ctrlKey: true }); expect(seen.map((event) => ({ name: event.name, ctrl: event.ctrl, meta: event.meta, super: event.super }))) .toEqual([ { name: "2", ctrl: false, meta: true, super: true }, { name: "3", ctrl: true, meta: false, super: false }, ]); }); // Cmd-digit is a browser tab shortcut, so the app only keeps it if its handler // still sees the key while a field is focused and can cancel the default there. test("delivers Cmd-digit from an editable field and lets a handler cancel the browser default", async () => { const seen = await mountProbe((event) => { if (event.name === "1" && event.meta) event.preventDefault(); }); const input = testWindow.document.createElement("input"); testWindow.document.body.appendChild(input); const result = await pressKey("1", { target: input, metaKey: true }); expect(seen.map((event) => ({ name: event.name, meta: event.meta, editable: event.targetEditable }))) .toEqual([{ name: "1", meta: true, editable: true }]); expect(result.defaultPrevented).toBe(true); input.remove(); });