- Split composer into AI zone (primary accent) and data actions zone (neutral) - Move Attach/Generate to labeled footer bar below textarea (left/right aligned) - Add info icon with HoverCard (hover preview) + Popover (pinned click) showing identical keyboard shortcuts content using shadcn HoverCard to fix theme inconsistency vs Tooltip - Expose imperative triggerRef on ImagePicker for keyboard shortcut access - Wire TooltipProvider in root layout; install shadcn kbd and hover-card - Unauthenticated state shows locked CTA with real sign-in button weight - Add behavioral contract tests for footer bar, info trigger, and zone layout
138 lines
4.3 KiB
TypeScript
138 lines
4.3 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
||
import {
|
||
SHORTCUT_DEFINITIONS,
|
||
resolveKeys,
|
||
} from "@/lib/keyboard-shortcuts";
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Keyboard shortcuts – OS-aware key resolution
|
||
//
|
||
// Public interface under test: resolveKeys(modifiers, os) — a pure function
|
||
// that maps abstract modifier tokens to display glyphs based on the detected
|
||
// operating system.
|
||
//
|
||
// We test the pure function directly, no browser or DOM required.
|
||
// The React hook (useOs) is just a thin browser wrapper around the same
|
||
// detection logic and doesn't need separate unit tests.
|
||
// ---------------------------------------------------------------------------
|
||
|
||
describe("resolveKeys – Mac", () => {
|
||
test("mod resolves to ⌘ on Mac", () => {
|
||
expect(resolveKeys(["mod"], "mac")).toEqual(["⌘"]);
|
||
});
|
||
|
||
test("shift resolves to ⇧ on Mac", () => {
|
||
expect(resolveKeys(["shift"], "mac")).toEqual(["⇧"]);
|
||
});
|
||
|
||
test("alt resolves to ⌥ on Mac", () => {
|
||
expect(resolveKeys(["alt"], "mac")).toEqual(["⌥"]);
|
||
});
|
||
|
||
test("enter resolves to ↵ on Mac", () => {
|
||
expect(resolveKeys(["enter"], "mac")).toEqual(["↵"]);
|
||
});
|
||
|
||
test("esc resolves to Esc on Mac", () => {
|
||
expect(resolveKeys(["esc"], "mac")).toEqual(["Esc"]);
|
||
});
|
||
|
||
test("combined mod+enter resolves correctly on Mac", () => {
|
||
expect(resolveKeys(["mod", "enter"], "mac")).toEqual(["⌘", "↵"]);
|
||
});
|
||
|
||
test("combined mod+shift+A resolves correctly on Mac", () => {
|
||
expect(resolveKeys(["mod", "shift", "A"], "mac")).toEqual(["⌘", "⇧", "A"]);
|
||
});
|
||
});
|
||
|
||
describe("resolveKeys – Windows / Linux", () => {
|
||
test("mod resolves to Ctrl on non-Mac", () => {
|
||
expect(resolveKeys(["mod"], "other")).toEqual(["Ctrl"]);
|
||
});
|
||
|
||
test("shift resolves to Shift on non-Mac", () => {
|
||
expect(resolveKeys(["shift"], "other")).toEqual(["Shift"]);
|
||
});
|
||
|
||
test("alt resolves to Alt on non-Mac", () => {
|
||
expect(resolveKeys(["alt"], "other")).toEqual(["Alt"]);
|
||
});
|
||
|
||
test("enter resolves to Enter on non-Mac", () => {
|
||
expect(resolveKeys(["enter"], "other")).toEqual(["Enter"]);
|
||
});
|
||
|
||
test("esc resolves to Esc on non-Mac (same as Mac)", () => {
|
||
expect(resolveKeys(["esc"], "other")).toEqual(["Esc"]);
|
||
});
|
||
|
||
test("combined mod+enter resolves correctly on non-Mac", () => {
|
||
expect(resolveKeys(["mod", "enter"], "other")).toEqual(["Ctrl", "Enter"]);
|
||
});
|
||
|
||
test("combined mod+shift+A resolves correctly on non-Mac", () => {
|
||
expect(resolveKeys(["mod", "shift", "A"], "other")).toEqual(["Ctrl", "Shift", "A"]);
|
||
});
|
||
});
|
||
|
||
describe("resolveKeys – unknown OS (SSR fallback)", () => {
|
||
test("unknown falls back to Mac glyphs (most users are Mac)", () => {
|
||
expect(resolveKeys(["mod"], "unknown")).toEqual(["⌘"]);
|
||
});
|
||
|
||
test("unknown enter fallback", () => {
|
||
expect(resolveKeys(["enter"], "unknown")).toEqual(["↵"]);
|
||
});
|
||
});
|
||
|
||
describe("resolveKeys – passthrough for plain keys", () => {
|
||
test("plain letter A passes through unchanged on Mac", () => {
|
||
expect(resolveKeys(["A"], "mac")).toEqual(["A"]);
|
||
});
|
||
|
||
test("plain letter A passes through unchanged on non-Mac", () => {
|
||
expect(resolveKeys(["A"], "other")).toEqual(["A"]);
|
||
});
|
||
});
|
||
|
||
describe("SHORTCUT_DEFINITIONS – schema contract", () => {
|
||
test("every definition has a non-empty modifiers array", () => {
|
||
for (const def of SHORTCUT_DEFINITIONS) {
|
||
expect(def.modifiers.length).toBeGreaterThan(0);
|
||
}
|
||
});
|
||
|
||
test("every definition has a non-empty label", () => {
|
||
for (const def of SHORTCUT_DEFINITIONS) {
|
||
expect(def.label.length).toBeGreaterThan(0);
|
||
}
|
||
});
|
||
|
||
test("Generate event shortcut exists and uses mod+enter", () => {
|
||
const gen = SHORTCUT_DEFINITIONS.find((d) =>
|
||
d.label.toLowerCase().includes("generate"),
|
||
);
|
||
expect(gen).toBeDefined();
|
||
expect(gen!.modifiers).toContain("mod");
|
||
expect(gen!.modifiers).toContain("enter");
|
||
});
|
||
|
||
test("Attach image shortcut exists and uses mod+shift", () => {
|
||
const attach = SHORTCUT_DEFINITIONS.find((d) =>
|
||
d.label.toLowerCase().includes("attach"),
|
||
);
|
||
expect(attach).toBeDefined();
|
||
expect(attach!.modifiers).toContain("mod");
|
||
expect(attach!.modifiers).toContain("shift");
|
||
});
|
||
|
||
test("Clear prompt shortcut exists and uses esc", () => {
|
||
const clear = SHORTCUT_DEFINITIONS.find((d) =>
|
||
d.label.toLowerCase().includes("clear"),
|
||
);
|
||
expect(clear).toBeDefined();
|
||
expect(clear!.modifiers).toContain("esc");
|
||
});
|
||
});
|