import { describe, expect, it } from "vitest"; import { diffSnapshots, type PageSnapshot, type SnapshotBlock } from "./version-diff.js"; const b = (id: string, text: string, extra: Partial = {}): SnapshotBlock => ({ id, type: "PARAGRAPH", content: { text }, properties: {}, parentId: null, position: id, ...extra }); const snap = (blocks: SnapshotBlock[], page: PageSnapshot["page"] = { title: "T", icon: null, description: null }): PageSnapshot => ({ page, blocks }); describe("version snapshot diff (T-150)", () => { it("identical snapshots → empty diff", () => { const s = snap([b("a", "one"), b("c", "three")]); const d = diffSnapshots(s, snap([...s.blocks]), 1, 2); expect(d.summary).toEqual({ added: 0, deleted: 0, modified: 0, moved: 0, unchanged: 2 }); expect(Object.keys(d.page)).toHaveLength(0); }); it("classifies added / deleted / modified / moved", () => { const from = snap([ b("a", "one"), b("b", "two"), b("c", "three", { position: "c0" }), b("gone", "bye") ]); const to = snap([ b("a", "ONE changed"), b("b", "two", { properties: { align: "center" } }), b("c", "three", { position: "z9" }), b("d", "new block") ]); const d = diffSnapshots(from, to, 1, 2); expect(d.summary).toEqual({ added: 1, deleted: 1, modified: 2, moved: 1, unchanged: 0 }); expect(d.added.map((x) => x.id)).toEqual(["d"]); expect(d.deleted.map((x) => x.id)).toEqual(["gone"]); const byId = Object.fromEntries(d.modified.map((m) => [m.id, m.fields])); expect(byId).toEqual({ a: ["content"], b: ["properties"] }); expect(d.moved).toEqual([{ id: "c", from: "c0", to: "z9" }]); }); it("detects parent moves as modified (structure change), not moved", () => { const from = snap([b("a", "top")]); const to = snap([b("a", "top", { parentId: "toggle1" })]); const d = diffSnapshots(from, to, 1, 2); expect(d.modified).toEqual([{ id: "a", fields: ["parentId"] }]); expect(d.moved).toHaveLength(0); }); it("compares page fields and reports from/to values", () => { const d = diffSnapshots( snap([], { title: "Old", icon: "x", description: null }), snap([], { title: "Old", icon: null, description: "new" }), 1, 2 ); expect(d.page).toEqual({ icon: { from: "x", to: null }, description: { from: null, to: "new" } }); }); it("key order in content/properties objects does NOT count as a change", () => { const from = snap([b("a", "x", { properties: { b: 1, a: 2 } })]); const to = snap([b("a", "x", { properties: { a: 2, b: 1 } })]); expect(diffSnapshots(from, to, 1, 2).summary.modified).toBe(0); }); it("type change is flagged", () => { const d = diffSnapshots(snap([b("a", "x")]), snap([b("a", "x", { type: "HEADING_1" })]), 1, 2); expect(d.modified).toEqual([{ id: "a", fields: ["type"] }]); }); });