import { describe, expect, test } from "bun:test"; import { renderToStaticMarkup } from "react-dom/server"; import { Hand, MousePointer2 } from "lucide-react"; import { FloatingDock } from "@/components/ui/aceternity/floating-dock"; import { CANVAS_MODE_TOOL_ID, defaultToolbarPrefs, migrateToolbarPrefs, resolveToolbarEntries, type ToolContext, type ToolbarHandlers } from "@/lib/canvas/tool-registry"; function createMainContext(canvasTool: "move" | "box-select" = "box-select"): ToolContext { return { selectedCount: 0, selectedNodeTypes: new Set(), selectedVideoCount: 0, canvasTool, workspaceMode: "professional", isProjectLinked: false, canUndo: false, canRedo: false, extractingVideoFrames: false, extractingAudio: false, trimmingVideo: false, mergingVideos: false, addPanelOpen: false, appearancePanelOpen: false, settingsPanelOpen: false, handlers: {} as ToolbarHandlers, }; } describe("canvas toolbar mode switch", () => { test.each(["default", "compact"] as const)("renders %s canvas modes as an icon-only sliding switch", (size) => { const entries = resolveToolbarEntries("main", createMainContext(), null); const modeSwitch = entries.find((entry) => entry.kind === "switch"); if (!modeSwitch) throw new Error("Missing canvas mode switch"); const html = renderToStaticMarkup(); const diameter = size === "compact" ? 24 : 26; expect(html).toContain(`width:${diameter}px;height:${diameter}px`); expect(html.match(/aceternity-dock-switch-thumb/g)).toHaveLength(1); expect(html).toContain("absolute top-1/2 rounded-full"); expect(html).toContain("grid size-4 shrink-0 place-items-center"); expect(html).not.toContain("is-labeled"); expect(html).not.toContain("选择"); expect(html).not.toContain("移动"); expect(html).toContain('aria-label="区域选择"'); expect(html).toContain('aria-label="抓手工具"'); }); test("renders grab and box-select as one dock switch", () => { const entries = resolveToolbarEntries("main", createMainContext("box-select"), null); const modeSwitch = entries.find((entry) => entry.kind === "switch" && entry.id === CANVAS_MODE_TOOL_ID); expect(modeSwitch?.kind).toBe("switch"); if (modeSwitch?.kind !== "switch") return; expect(modeSwitch.value).toBe("box-select"); expect(modeSwitch.options.map((option) => option.value)).toEqual(["box-select", "move"]); expect(entries.some((entry) => entry.id === "tool-move" || entry.id === "tool-box-select")).toBe(false); }); test("reflects the active canvas tool on the switch", () => { const entries = resolveToolbarEntries("main", createMainContext("move"), null); const modeSwitch = entries.find((entry) => entry.kind === "switch"); expect(modeSwitch?.kind === "switch" && modeSwitch.value).toBe("move"); }); test("migrates legacy separate tool prefs into the switch item", () => { const migrated = migrateToolbarPrefs("main", { order: ["tool-move", "tool-box-select", "tool-undo", "tool-add"], hidden: [], }); expect(migrated.order[0]).toBe(CANVAS_MODE_TOOL_ID); expect(migrated.order).not.toContain("tool-move"); expect(migrated.order).not.toContain("tool-box-select"); expect(migrated.hidden).toEqual([]); }); test("hides the switch only when both legacy tools were hidden", () => { const oneHidden = migrateToolbarPrefs("main", { order: ["tool-move", "tool-box-select", "tool-undo"], hidden: ["tool-move"], }); expect(oneHidden.hidden).toEqual([]); const bothHidden = migrateToolbarPrefs("main", { order: ["tool-undo", "tool-move", "tool-box-select"], hidden: ["tool-move", "tool-box-select"], }); expect(bothHidden.order[1]).toBe(CANVAS_MODE_TOOL_ID); expect(bothHidden.hidden).toEqual([CANVAS_MODE_TOOL_ID]); }); test("leaves non-main toolbar prefs unchanged", () => { const prefs = { order: ["tool-move"], hidden: ["tool-box-select"] }; expect(migrateToolbarPrefs("selection", prefs)).toEqual(prefs); }); test("keeps the switch visible in default main toolbar prefs", () => { expect(defaultToolbarPrefs("main").order[0]).toBe(CANVAS_MODE_TOOL_ID); expect(defaultToolbarPrefs("main").hidden).toEqual([]); }); test("hides rarely used arrange tools from the selection toolbar by default", () => { const prefs = defaultToolbarPrefs("selection"); expect(prefs.hidden).toEqual(expect.arrayContaining([ "selection-arrange-row", "selection-arrange-column", "selection-arrange-grid", "selection-arrange-flow", "selection-create-reference-group", ])); const entries = resolveToolbarEntries("selection", createMainContext(), null); expect(entries.some((entry) => entry.id === "selection-align-left")).toBe(true); expect(entries.some((entry) => entry.id === "selection-distribute-x")).toBe(true); expect(entries.some((entry) => entry.id === "selection-batch-connect")).toBe(true); expect(entries.some((entry) => entry.id === "selection-arrange-grid")).toBe(false); expect(entries.some((entry) => entry.id === "selection-create-reference-group")).toBe(false); }); test("renders the pill switch with an active circular thumb", () => { const html = renderToStaticMarkup( , value: "box-select" }, { id: "move", label: "抓手工具", icon: , value: "move" }, ], onChange: () => {}, }]} />, ); expect(html).toContain('role="radiogroup"'); expect(html).toContain("aceternity-dock-switch-track"); expect(html).toContain("aceternity-dock-switch-thumb"); expect(html).toContain('aria-label="区域选择"'); expect(html).toContain('aria-label="抓手工具"'); expect(html).toContain('aria-checked="true"'); expect(html).toContain('aria-checked="false"'); }); });