committed by
philon-
34 changed files with 695 additions and 159 deletions
@ -0,0 +1,60 @@ |
|||
import { describe, expect, it } from "vitest"; |
|||
import { deepCompareConfig } from "./deepCompareConfig"; |
|||
|
|||
describe("deepCompareConfig", () => { |
|||
it("returns true for identical primitives", () => { |
|||
expect(deepCompareConfig(5, 5)).toBe(true); |
|||
expect(deepCompareConfig("foo", "foo")).toBe(true); |
|||
expect(deepCompareConfig(true, true)).toBe(true); |
|||
}); |
|||
|
|||
it("returns false for different primitives", () => { |
|||
expect(deepCompareConfig(5, 6)).toBe(false); |
|||
expect(deepCompareConfig("foo", "bar")).toBe(false); |
|||
expect(deepCompareConfig(true, false)).toBe(false); |
|||
}); |
|||
|
|||
it("handles nulls correctly", () => { |
|||
expect(deepCompareConfig(null, null)).toBe(true); |
|||
expect(deepCompareConfig(null, undefined)).toBe(false); |
|||
expect(deepCompareConfig(null, {})).toBe(false); |
|||
}); |
|||
|
|||
it("allows undefined in working when allowUndefined is true", () => { |
|||
expect(deepCompareConfig({ a: 1 }, { a: undefined }, true)).toBe(true); |
|||
expect(deepCompareConfig([1, 2, 3], [1, undefined, 3], true)).toBe(true); |
|||
}); |
|||
|
|||
it("rejects undefined in working when allowUndefined is false", () => { |
|||
expect(deepCompareConfig({ a: 1 }, { a: undefined }, false)).toBe(false); |
|||
}); |
|||
|
|||
it("compares arrays deeply", () => { |
|||
expect(deepCompareConfig([1, [2, 3]], [1, [2, 3]])).toBe(true); |
|||
expect(deepCompareConfig([1, [2, 3]], [1, [2, 4]])).toBe(false); |
|||
}); |
|||
|
|||
it("compares objects deeply", () => { |
|||
const existing = { x: 10, y: { z: 20 } }; |
|||
const workingEqual = { x: 10, y: { z: 20 } }; |
|||
const workingDiff = { x: 10, y: { z: 21 } }; |
|||
|
|||
expect(deepCompareConfig(existing, workingEqual)).toBe(true); |
|||
expect(deepCompareConfig(existing, workingDiff)).toBe(false); |
|||
}); |
|||
|
|||
it("ignores $typeName key in existing", () => { |
|||
const existing = { $typeName: "Test", a: 1 }; |
|||
const working = { a: 1 }; |
|||
expect(deepCompareConfig(existing, working)).toBe(true); |
|||
}); |
|||
|
|||
it("fails when working has extra keys", () => { |
|||
expect(deepCompareConfig({ a: 1 }, { a: 1, b: 2 })).toBe(false); |
|||
}); |
|||
|
|||
it("allows working arrays to be shorter if allowUndefined is true", () => { |
|||
expect(deepCompareConfig([1, 2, 3, 4], [1, 2], true)).toBe(true); |
|||
expect(deepCompareConfig([1, 2, 3, 4], [1, 2], false)).toBe(false); |
|||
}); |
|||
}); |
|||
@ -0,0 +1,68 @@ |
|||
export function deepCompareConfig( |
|||
existing: unknown, |
|||
working: unknown, |
|||
allowUndefined = false, |
|||
): boolean { |
|||
if (existing === working) return true; |
|||
|
|||
if ( |
|||
allowUndefined && |
|||
(typeof existing === "undefined" || typeof working === "undefined") |
|||
) return true; |
|||
|
|||
if (typeof existing !== typeof working) { |
|||
return false; |
|||
} |
|||
|
|||
if (existing === null || working === null) { |
|||
if (existing !== working) { |
|||
return false; |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
if (Array.isArray(existing) && Array.isArray(working)) { |
|||
if (existing.length !== working.length && !allowUndefined) { |
|||
return false; |
|||
} |
|||
for (let i = 0; i < existing.length; i++) { |
|||
if ( |
|||
!deepCompareConfig(existing[i], working[i], allowUndefined) |
|||
) { |
|||
return false; |
|||
} |
|||
} |
|||
return true; |
|||
} |
|||
|
|||
if (typeof existing === "object" && typeof working === "object") { |
|||
const exObj = existing as Record<string, unknown>; |
|||
const woObj = working as Record<string, unknown>; |
|||
const keys = new Set<string>([ |
|||
...Object.keys(exObj), |
|||
...Object.keys(woObj), |
|||
]); |
|||
|
|||
for (const key of keys) { |
|||
if (key === "$typeName") continue; |
|||
const hasExisting = Object.prototype.hasOwnProperty.call(exObj, key); |
|||
const hasWorking = Object.prototype.hasOwnProperty.call(woObj, key); |
|||
const valExisting = exObj[key]; |
|||
const valWorking = woObj[key]; |
|||
|
|||
if (!hasWorking && allowUndefined && hasExisting) { |
|||
continue; |
|||
} |
|||
|
|||
if ( |
|||
!deepCompareConfig(valExisting, valWorking, allowUndefined) |
|||
) { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
Loading…
Reference in new issue