import { describe, expect, it } from "vitest"; import { TOOLS, TOOL_MENU_KEYS, TOOL_SHORTCUTS, canSwitchTo, menuIntentAfterClick, toolButtonFace, visibleToolsFor, } from "@/components/pdf/toolbar-tools"; // spec ตาม PLAN-TOOLBAR.md ขั้น 1 — ค่า expected เป็น literal จาก // .claude/rules/pdf-viewer.md + พฤติกรรม toolbar ของ commit 8571ef9 // (ไม่คัดลอกสูตรจากโค้ดปลายทาง กัน tautological) describe("TOOLS", () => { it("ครบ 10 ตัวตาม union และลำดับปุ่มบนแถบ", () => { expect(TOOLS.map((t) => t.key)).toEqual([ "select", "pan", "highlight", "draw", "comment", "shape", "erase", "redact", "textbox", "stamp", ]); }); it("text สั้นแสดงบนปุ่มครบทุกตัว (คำขอผู้ใช้ 2026-09-21: ปุ่มต้องมีชื่อ ไม่ใช่ icon ล้วน)", () => { expect(Object.fromEntries(TOOLS.map((t) => [t.key, t.text]))).toEqual({ select: "เลือก", pan: "เลื่อนหน้า", highlight: "มาร์กข้อความ", draw: "วาดเส้น", comment: "คอมเมนต์", shape: "รูปทรง", erase: "ยางลบ", redact: "ปิดพราง", textbox: "กล่องข้อความ", stamp: "ตรายาง", }); }); it("label เต็ม (title/aria-label) มีคีย์ลัด H/D/C ตาม rules และทุกตัวมี Icon", () => { const byKey = Object.fromEntries(TOOLS.map((t) => [t.key, t])); expect(byKey.highlight.label).toBe("มาร์กข้อความ (H)"); expect(byKey.draw.label).toBe("วาดเส้น (D)"); expect(byKey.comment.label).toBe("คอมเมนต์ (C)"); for (const t of TOOLS) { expect(t.label.trim().length).toBeGreaterThan(0); expect(t.Icon).toBeTruthy(); } }); }); describe("visibleToolsFor", () => { it("hide=false → ทั้งชุด 10 ตัว", () => { expect(visibleToolsFor(false).map((t) => t.key)).toHaveLength(10); expect(visibleToolsFor(false)[0].key).toBe("select"); expect(visibleToolsFor(false).at(-1)?.key).toBe("stamp"); }); it("hide=true (ไฟล์ภาพ/สแกนล้วน) → ตัดเฉพาะ highlight เหลือ 9 ตัว ลำดับเดิม", () => { expect(visibleToolsFor(true).map((t) => t.key)).toEqual([ "select", "pan", "draw", "comment", "shape", "erase", "redact", "textbox", "stamp", ]); }); }); describe("canSwitchTo", () => { it("highlight บนไฟล์ที่ซ่อนปุ่ม (ภาพ/สแกนล้วน) → false กันคีย์ลัด H", () => { expect(canSwitchTo("highlight", true)).toBe(false); }); it("highlight บนไฟล์ปกติ → true", () => { expect(canSwitchTo("highlight", false)).toBe(true); }); it("tool อื่นทุกตัวสลับได้แม้ไฟล์ภาพ/สแกนล้วน", () => { for (const t of TOOLS.map((x) => x.key)) { if (t === "highlight") continue; expect(canSwitchTo(t, true)).toBe(true); } }); }); describe("toolButtonFace", () => { it("ไม่ active → ชื่อ tool เสมอแม้มีค่าย่อย (คำขอผู้ใช้ 2026-09-29: 'วาดเส้น' ต้องไม่กลายเป็น 'ปากกา')", () => { expect(toolButtonFace(false, { label: "วาดเส้น (D)", text: "วาดเส้น" }, "ปากกา")).toEqual({ text: "วาดเส้น", title: "วาดเส้น (D)", }); }); it("active + มีค่าย่อย → แสดงค่าที่เลือก + title 'label — ค่า' (stamp คำกำหนดเองก็เป็นค่าหนึ่ง)", () => { expect( toolButtonFace(true, { label: "ตรายาง (stamp)", text: "ตรายาง" }, "กำหนดเอง"), ).toEqual({ text: "กำหนดเอง", title: "ตรายาง (stamp) — กำหนดเอง" }); // textbox ไม่มีค่าย่อย — active อยู่ชื่อเดิม ไม่กลายเป็น "กล่องข้อความ — กล่องข้อความ" expect(toolButtonFace(true, { label: "กล่องข้อความ", text: "กล่องข้อความ" })).toEqual({ text: "กล่องข้อความ", title: "กล่องข้อความ", }); }); }); describe("menuIntentAfterClick", () => { it("กดจาก tool อื่น / tool เดิมที่เมนูปิด = เปิดเมนู; tool เดิมที่เมนูเปิด = พับ", () => { expect(menuIntentAfterClick("select", "highlight", false)).toBe(true); expect(menuIntentAfterClick("select", "highlight", true)).toBe(true); expect(menuIntentAfterClick("highlight", "highlight", false)).toBe(true); expect(menuIntentAfterClick("highlight", "highlight", true)).toBe(false); }); }); describe("TOOL_MENU_KEYS", () => { it("ครบเฉพาะ tool ที่ปุ่มเป็น dropdown (มาร์กข้อความ/วาดเส้น/รูปทรง/กล่องข้อความ/ตรายาง) — จุดที่ปิดเมนูทั้งชุดวนจากชุดนี้ชุดเดียว", () => { expect([...TOOL_MENU_KEYS].sort()).toEqual(["draw", "highlight", "shape", "stamp", "textbox"]); // ทุก key เป็นค่าของ Tool จริง (กันชื่อคำนวณพลาด) for (const k of TOOL_MENU_KEYS) { expect(TOOLS.some((t) => t.key === k)).toBe(true); } }); }); describe("TOOL_SHORTCUTS", () => { it("จับคู่ e.code ตำแหน่งปุ่ม (layout-independent) — บน layout ไทย e.key เป็น 'ภ' ก็ยังใช้ได้", () => { expect(TOOL_SHORTCUTS.KeyH).toBe("highlight"); expect(TOOL_SHORTCUTS.KeyD).toBe("draw"); expect(TOOL_SHORTCUTS.KeyC).toBe("comment"); // คีย์อื่น/ตัวอักษร layout ไทยไม่ใช่คีย์ลัด expect(TOOL_SHORTCUTS.KeyZ).toBeUndefined(); expect(TOOL_SHORTCUTS.KeyB).toBeUndefined(); // B = bookmark ไม่ใช่ tool expect(TOOL_SHORTCUTS["ภ"]).toBeUndefined(); }); it("ตรงกับคีย์ลัดที่ label ของ TOOLS ประกาศ ((H)/(D)/(C)) — กัน drift สองฝั่ง", () => { for (const [code, tool] of Object.entries(TOOL_SHORTCUTS)) { const entry = TOOLS.find((t) => t.key === tool); expect(entry, `tool ${tool} ต้องอยู่ใน TOOLS`).toBeTruthy(); expect(entry!.label).toContain(`(${code.replace("Key", "")})`); } }); });