import { describe, it, expect } from "vitest"; import { splitBlocks, diffBlocks } from "../src/trackChangesModel"; describe("splitBlocks", () => { it("splits prose paragraphs on blank lines, dropping empties", () => { const blocks = splitBlocks("# Heading\n\nFirst para.\n\nSecond para.\n"); expect(blocks.map((b) => b.type)).toEqual(["prose", "prose", "prose"]); expect(blocks[0].raw).toBe("# Heading"); expect(blocks[2].raw).toBe("Second para."); }); it("keeps a fenced code block whole and tags it `code`", () => { const text = "Intro.\n\n```ts\nconst a = 1;\n\nconst b = 2;\n```\n\nOutro.\n"; const blocks = splitBlocks(text); expect(blocks.map((b) => b.type)).toEqual(["prose", "code", "prose"]); expect(blocks[1].raw).toBe("```ts\nconst a = 1;\n\nconst b = 2;\n```"); }); it("tags a mermaid fence `mermaid`", () => { const blocks = splitBlocks("```mermaid\nflowchart LR\n a-->b\n```\n"); expect(blocks).toHaveLength(1); expect(blocks[0].type).toBe("mermaid"); }); it("normalizes the key (whitespace-collapsed) for matching", () => { const blocks = splitBlocks("Hello world\n"); expect(blocks[0].key).toBe("hello world"); }); }); describe("diffBlocks", () => { const kinds = (base: string, cur: string) => diffBlocks(base, cur).map((o) => o.kind); it("unchanged doc → all unchanged", () => { const doc = "Alpha.\n\nBravo.\n"; expect(kinds(doc, doc)).toEqual(["unchanged", "unchanged"]); }); it("a pure addition → an added op", () => { const ops = diffBlocks("Alpha.\n", "Alpha.\n\nBravo.\n"); expect(ops.map((o) => o.kind)).toEqual(["unchanged", "added"]); }); it("a pure deletion → a removed op", () => { const ops = diffBlocks("Alpha.\n\nBravo.\n", "Alpha.\n"); expect(ops.map((o) => o.kind)).toEqual(["unchanged", "removed"]); }); it("a prose modification → a non-atomic changed op", () => { const ops = diffBlocks("The quick fox.\n", "The slow fox.\n"); expect(ops).toHaveLength(1); expect(ops[0].kind).toBe("changed"); expect(ops[0].kind === "changed" && ops[0].atomic).toBe(false); }); it("a code-fence change → an ATOMIC changed op (INV-23)", () => { const base = "```ts\nconst a = 1;\n```\n"; const cur = "```ts\nconst a = 2;\n```\n"; const ops = diffBlocks(base, cur); expect(ops).toHaveLength(1); expect(ops[0].kind).toBe("changed"); expect(ops[0].kind === "changed" && ops[0].atomic).toBe(true); }); it("a mermaid-fence change → an ATOMIC changed op", () => { const base = "```mermaid\nflowchart LR\n a-->b\n```\n"; const cur = "```mermaid\nflowchart LR\n a-->c\n```\n"; const ops = diffBlocks(base, cur); expect(ops[0].kind).toBe("changed"); expect(ops[0].kind === "changed" && ops[0].atomic).toBe(true); }); it("a reorder → keeps a matched block unchanged", () => { const ops = diffBlocks("One.\n\nTwo.\n", "Two.\n\nOne.\n"); const k = ops.map((o) => o.kind); expect(k).toContain("unchanged"); }); });