| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- import { describe, it, expect } from "vitest";
- import { diffToPatches } from "./diff";
- describe("diffToPatches", () => {
- it("returns empty array for identical objects", () => {
- const obj = { a: 1, b: "hello" };
- expect(diffToPatches(obj, obj)).toEqual([]);
- });
- it("detects added keys", () => {
- const patches = diffToPatches({}, { name: "Alice" });
- expect(patches).toEqual([{ op: "add", path: "/name", value: "Alice" }]);
- });
- it("detects removed keys", () => {
- const patches = diffToPatches({ name: "Alice" }, {});
- expect(patches).toEqual([{ op: "remove", path: "/name" }]);
- });
- it("detects changed scalar values", () => {
- const patches = diffToPatches({ name: "Alice" }, { name: "Bob" });
- expect(patches).toEqual([{ op: "replace", path: "/name", value: "Bob" }]);
- });
- it("recurses into nested objects", () => {
- const oldObj = { user: { name: "Alice", age: 30 } };
- const newObj = { user: { name: "Alice", age: 31 } };
- const patches = diffToPatches(oldObj, newObj);
- expect(patches).toEqual([{ op: "replace", path: "/user/age", value: 31 }]);
- });
- it("replaces arrays atomically", () => {
- const oldObj = { items: ["a", "b"] };
- const newObj = { items: ["a", "b", "c"] };
- const patches = diffToPatches(oldObj, newObj);
- expect(patches).toEqual([
- { op: "replace", path: "/items", value: ["a", "b", "c"] },
- ]);
- });
- it("does not emit patch for identical arrays", () => {
- const oldObj = { items: [1, 2, 3] };
- const newObj = { items: [1, 2, 3] };
- expect(diffToPatches(oldObj, newObj)).toEqual([]);
- });
- it("handles type changes (object → scalar)", () => {
- const oldObj = { data: { nested: true } };
- const newObj = { data: "flat" };
- const patches = diffToPatches(
- oldObj as Record<string, unknown>,
- newObj as Record<string, unknown>,
- );
- expect(patches).toEqual([{ op: "replace", path: "/data", value: "flat" }]);
- });
- it("handles a complex spec diff", () => {
- const oldSpec = {
- root: "main",
- elements: {
- main: { type: "Card", props: { title: "Hello" }, children: [] },
- },
- };
- const newSpec = {
- root: "main",
- elements: {
- main: {
- type: "Card",
- props: { title: "Hello" },
- children: ["child-1"],
- },
- "child-1": {
- type: "Text",
- props: { content: "World" },
- children: [],
- },
- },
- };
- const patches = diffToPatches(oldSpec, newSpec);
- expect(patches).toContainEqual({
- op: "replace",
- path: "/elements/main/children",
- value: ["child-1"],
- });
- expect(patches).toContainEqual({
- op: "add",
- path: "/elements/child-1",
- value: {
- type: "Text",
- props: { content: "World" },
- children: [],
- },
- });
- });
- it("escapes JSON Pointer tokens (~ and /)", () => {
- const patches = diffToPatches({}, { "a/b": 1, "c~d": 2 });
- expect(patches).toContainEqual({
- op: "add",
- path: "/a~1b",
- value: 1,
- });
- expect(patches).toContainEqual({
- op: "add",
- path: "/c~0d",
- value: 2,
- });
- });
- });
|