1import { describe, expect, it } from "vitest"; 2import { diffSnapshots, type PageSnapshot, type SnapshotBlock } from "./version-diff.js"; 3 4const b = (id: string, text: string, extra: Partial<SnapshotBlock> = {}): SnapshotBlock => ({ 5 id, 6 type: "PARAGRAPH", 7 content: { text }, 8 properties: {}, 9 parentId: null, 10 position: id, 11 ...extra 12}); 13 14const snap = (blocks: SnapshotBlock[], page: PageSnapshot["page"] = { title: "T", icon: null, description: null }): PageSnapshot => ({ 15 page, 16 blocks 17}); 18 19describe("version snapshot diff (T-150)", () => { 20 it("identical snapshots → empty diff", () => { 21 const s = snap([b("a", "one"), b("c", "three")]); 22 const d = diffSnapshots(s, snap([...s.blocks]), 1, 2); 23 expect(d.summary).toEqual({ added: 0, deleted: 0, modified: 0, moved: 0, unchanged: 2 }); 24 expect(Object.keys(d.page)).toHaveLength(0); 25 }); 26 27 it("classifies added / deleted / modified / moved", () => { 28 const from = snap([ 29 b("a", "one"), 30 b("b", "two"), 31 b("c", "three", { position: "c0" }), 32 b("gone", "bye") 33 ]); 34 const to = snap([ 35 b("a", "ONE changed"), 36 b("b", "two", { properties: { align: "center" } }), 37 b("c", "three", { position: "z9" }), 38 b("d", "new block") 39 ]); 40 const d = diffSnapshots(from, to, 1, 2); 41 expect(d.summary).toEqual({ added: 1, deleted: 1, modified: 2, moved: 1, unchanged: 0 }); 42 expect(d.added.map((x) => x.id)).toEqual(["d"]); 43 expect(d.deleted.map((x) => x.id)).toEqual(["gone"]); 44 const byId = Object.fromEntries(d.modified.map((m) => [m.id, m.fields])); 45 expect(byId).toEqual({ a: ["content"], b: ["properties"] }); 46 expect(d.moved).toEqual([{ id: "c", from: "c0", to: "z9" }]); 47 }); 48 49 it("detects parent moves as modified (structure change), not moved", () => { 50 const from = snap([b("a", "top")]); 51 const to = snap([b("a", "top", { parentId: "toggle1" })]); 52 const d = diffSnapshots(from, to, 1, 2); 53 expect(d.modified).toEqual([{ id: "a", fields: ["parentId"] }]); 54 expect(d.moved).toHaveLength(0); 55 }); 56 57 it("compares page fields and reports from/to values", () => { 58 const d = diffSnapshots( 59 snap([], { title: "Old", icon: "x", description: null }), 60 snap([], { title: "Old", icon: null, description: "new" }), 61 1, 2 62 ); 63 expect(d.page).toEqual({ 64 icon: { from: "x", to: null }, 65 description: { from: null, to: "new" } 66 }); 67 }); 68 69 it("key order in content/properties objects does NOT count as a change", () => { 70 const from = snap([b("a", "x", { properties: { b: 1, a: 2 } })]); 71 const to = snap([b("a", "x", { properties: { a: 2, b: 1 } })]); 72 expect(diffSnapshots(from, to, 1, 2).summary.modified).toBe(0); 73 }); 74 75 it("type change is flagged", () => { 76 const d = diffSnapshots(snap([b("a", "x")]), snap([b("a", "x", { type: "HEADING_1" })]), 1, 2); 77 expect(d.modified).toEqual([{ id: "a", fields: ["type"] }]); 78 }); 79}); 80