import { describe, expect, it } from "bun:test"; import { diff } from "../diff"; import { applyExtendedPatch } from "../extended"; it("replaces object property", () => { const a = { foo: 0 }; const b = { foo: 1 }; const [d, i] = diff(a, b); expect(d).toEqual([ { op: "replace", path: ["foo"], value: 1, }, ]); expect(i).toEqual([ { op: "replace", path: ["foo"], value: 0, }, ]); expect(applyExtendedPatch(a, d)).toEqual(b); expect(applyExtendedPatch(b, i)).toEqual(a); }); it("adds object property", () => { const a = {}; const b = { foo: 1 }; const [d, i] = diff(a, b); expect(d).toEqual([ { op: "add", path: ["foo"], value: 1, }, ]); expect(i).toEqual([ { op: "remove", path: ["foo"], }, ]); expect(applyExtendedPatch(a, d)).toEqual(b); expect(applyExtendedPatch(b, i)).toEqual(a as any); }); it("removes object property", () => { const a = { foo: 1 }; const b = {}; const [d, i] = diff(a, b); expect(d).toEqual([ { op: "remove", path: ["foo"], }, ]); expect(i).toEqual([ { op: "add", path: ["foo"], value: 1, }, ]); expect(applyExtendedPatch(a, d)).toEqual(b as any); expect(applyExtendedPatch(b, i)).toEqual(a); }); describe("simple patches", () => { it("adds array item", () => { const a = { items: [] }; const b = { items: [{ id: "a" }] }; const [d, i] = diff(a, b); expect(d).toEqual([ { op: "add", path: ["items", 0], value: { id: "a" }, }, ]); expect(i).toEqual([ { op: "remove", path: ["items", 0], }, ]); expect(applyExtendedPatch(a, d)).toEqual(b as any); expect(applyExtendedPatch(b, i)).toEqual(a); }); it("removes array item", () => { const a = { items: [{ id: "a" }] }; const b = { items: [] }; const [d, i] = diff(a, b); expect(d).toEqual([ { op: "remove", path: ["items", 0], }, ]); expect(i).toEqual([ { op: "add", path: ["items", 0], value: { id: "a" }, }, ]); expect(applyExtendedPatch(a, d)).toEqual(b); expect(applyExtendedPatch(b, i)).toEqual(a as any); }); it("updates array item", () => { const a = { items: [{ id: "a", count: 0 }] }; const b = { items: [{ id: "a", count: 1 }] }; const [d, i] = diff(a, b); expect(d).toEqual([ { op: "replace", path: ["items", 0, "count"], value: 1, }, ]); expect(i).toEqual([ { op: "replace", path: ["items", 0, "count"], value: 0, }, ]); expect(applyExtendedPatch(a, d)).toEqual(b); expect(applyExtendedPatch(b, i)).toEqual(a); }); it("shifts array item", () => { const objA = { id: "a", count: 1 }; const objB = { id: "b", count: 2 }; const objC = { id: "c", count: 3 }; const a = { items: [objA, objB] }; const b = { items: [objC, objA, objB] }; const [d, i] = diff(a, b); expect(d).toEqual([ { op: "add", path: ["items", 0], value: { id: "c", count: 3 }, }, ]); expect(i).toEqual([ { op: "remove", path: ["items", 0], }, ]); expect(applyExtendedPatch(a, d)).toEqual(b); expect(applyExtendedPatch(b, i)).toEqual(a); }); }); describe("extended patches", () => { it("adds array item", () => { const a = { items: [] }; const b = { items: [{ id: "a" }] }; const [d, i] = diff(a, b, { useExtendedPatches: true }); expect(d).toEqual([ { op: "add", path: ["items", 0], value: { id: "a" }, }, ]); expect(i).toEqual([ { op: "remove", path: ["items", { id: "a" }], }, ]); expect(applyExtendedPatch(a, d)).toEqual(b as any); expect(applyExtendedPatch(b, i)).toEqual(a); }); it("removes array item", () => { const a = { items: [{ id: "a" }] }; const b = { items: [] }; const [d, i] = diff(a, b, { useExtendedPatches: true }); expect(d).toEqual([ { op: "remove", path: ["items", { id: "a" }], }, ]); expect(i).toEqual([ { op: "add", path: ["items", 0], value: { id: "a" }, }, ]); expect(applyExtendedPatch(a, d)).toEqual(b); expect(applyExtendedPatch(b, i)).toEqual(a as any); }); it("updates array item", () => { const a = { items: [{ id: "a", count: 0 }] }; const b = { items: [{ id: "a", count: 1 }] }; const [d, i] = diff(a, b, { useExtendedPatches: true }); expect(d).toEqual([ { op: "replace", path: ["items", { id: "a" }, "count"], value: 1, }, ]); expect(i).toEqual([ { op: "replace", path: ["items", { id: "a" }, "count"], value: 0, }, ]); expect(applyExtendedPatch(a, d)).toEqual(b); expect(applyExtendedPatch(b, i)).toEqual(a); }); it("shifts array item", () => { const objA = { id: "a", count: 1 }; const objB = { id: "b", count: 2 }; const objC = { id: "c", count: 3 }; const a = { items: [objA, objB] }; const b = { items: [objC, objA, objB] }; const [d, i] = diff(a, b, { useExtendedPatches: true }); expect(d).toEqual([ { op: "add", path: ["items", 0], value: { id: "c", count: 3 }, }, ]); expect(i).toEqual([ { op: "remove", path: ["items", { id: "c" }], }, ]); expect(applyExtendedPatch(a, d)).toEqual(b); expect(applyExtendedPatch(b, i)).toEqual(a); }); }); it("replaces nested object with null", () => { const a = { nested: { foo: 1 } }; const b = { nested: null } as const; const [d, i] = diff(a, b); expect(d).toEqual([ { op: "replace", path: ["nested"], value: null, }, ]); expect(i).toEqual([ { op: "replace", path: ["nested"], value: { foo: 1 }, }, ]); expect(applyExtendedPatch(a, d)).toEqual(b as unknown as typeof a); expect(applyExtendedPatch(b, i)).toEqual(a as unknown as typeof b); }); it("replaces nested object with undefined", () => { const a = { nested: { foo: 1 } } as const; const b: any = { nested: undefined }; const [d, i] = diff(a, b); expect(d).toEqual([ { op: "replace", path: ["nested"], value: undefined, }, ]); expect(i).toEqual([ { op: "replace", path: ["nested"], value: { foo: 1 }, }, ]); expect(applyExtendedPatch(a, d)).toEqual(b); expect(applyExtendedPatch(b, i)).toEqual(a); });