1import { describe, expect, it } from "vitest"; 2 3import { 4 SLASH_COMMANDS, 5 fuzzyScore, 6 searchSlashCommands, 7} from "./slash.js"; 8 9describe("slash registry", () => { 10 it("has 30 commands with unique ids", () => { 11 expect(SLASH_COMMANDS).toHaveLength(30); 12 const ids = new Set(SLASH_COMMANDS.map((c) => c.id)); 13 expect(ids.size).toBe(30); 14 }); 15 16 it("every plain command maps to a valid block type", () => { 17 const VALID = new Set([ 18 "paragraph", "heading_1", "heading_2", "heading_3", "quote", "callout", 19 "bulleted_list", "numbered_list", "todo", "toggle", "image", "video", 20 "audio", "file", "bookmark", "embed", "code", "equation", "column", 21 "column_list", "divider", "toc", "spacer", "database", "linked_database", 22 "table", "chart", "form", "synced_block", "child_page", "child_database", 23 ]); 24 for (const c of SLASH_COMMANDS) { 25 if (c.blockType !== undefined) expect(VALID.has(c.blockType)).toBe(true); 26 else expect(c.special).toBeDefined(); 27 } 28 }); 29}); 30 31describe("fuzzyScore", () => { 32 it("scores exact prefix best", () => { 33 expect(fuzzyScore("text", "Text plain paragraph default")).toBe(1); 34 }); 35 36 it("scores subsequence with gap penalty", () => { 37 const s = fuzzyScore("h1", "Heading 1 subtitle medium"); 38 expect(s).not.toBeNull(); 39 expect(s!).toBeGreaterThan(1); 40 }); 41 42 it("returns null for non-subsequence", () => { 43 expect(fuzzyScore("zz", "Heading 1")).toBeNull(); 44 }); 45}); 46 47describe("searchSlashCommands", () => { 48 it("empty query returns all commands in menu order", () => { 49 const hits = searchSlashCommands(""); 50 expect(hits).toHaveLength(30); 51 expect(hits[0]?.command.id).toBe("paragraph"); 52 expect(hits.map((h) => h.command.id)).toEqual(SLASH_COMMANDS.map((c) => c.id)); 53 }); 54 55 it('"h1" ranks Heading 1 first', () => { 56 const hits = searchSlashCommands("h1"); 57 expect(hits.length).toBeGreaterThan(0); 58 expect(hits[0]?.command.id).toBe("heading_1"); 59 }); 60 61 it('"cal" finds Callout in the top 3', () => { 62 const hits = searchSlashCommands("cal"); 63 const ids = hits.slice(0, 3).map((h) => h.command.id); 64 expect(ids).toContain("callout"); 65 }); 66 67 it('"emb" finds Embed', () => { 68 const hits = searchSlashCommands("emb"); 69 expect(hits.map((h) => h.command.id)).toContain("embed"); 70 }); 71 72 it("is deterministic (same query → same order)", () => { 73 const a = searchSlashCommands("list").map((h) => h.command.id); 74 const b = searchSlashCommands("list").map((h) => h.command.id); 75 expect(a).toEqual(b); 76 expect(a.length).toBeGreaterThanOrEqual(2); 77 }); 78}); 79