diff options
Diffstat (limited to 'std/testing/asserts.ts')
-rw-r--r-- | std/testing/asserts.ts | 42 |
1 files changed, 42 insertions, 0 deletions
diff --git a/std/testing/asserts.ts b/std/testing/asserts.ts index f93e043dc..1d58f0a00 100644 --- a/std/testing/asserts.ts +++ b/std/testing/asserts.ts @@ -430,6 +430,48 @@ export function assertNotMatch( } /** + * Make an assertion that `actual` object is a subset of `expected` object, deeply. + * If not, then throw. + */ +export function assertObjectMatch( + actual: Record<PropertyKey, unknown>, + expected: Record<PropertyKey, unknown>, +): void { + type loose = Record<PropertyKey, unknown>; + const seen = new WeakMap(); + return assertEquals( + (function filter(a: loose, b: loose): loose { + // Prevent infinite loop with circular references with same filter + if ((seen.has(a)) && (seen.get(a) === b)) { + return a; + } + seen.set(a, b); + // Filter keys and symbols which are present in both actual and expected + const filtered = {} as loose; + const entries = [ + ...Object.getOwnPropertyNames(a), + ...Object.getOwnPropertySymbols(a), + ] + .filter((key) => key in b) + .map((key) => [key, a[key as string]]) as Array<[string, unknown]>; + // Build filtered object and filter recursively on nested objects references + for (const [key, value] of entries) { + if (typeof value === "object") { + const subset = (b as loose)[key]; + if ((typeof subset === "object") && (subset)) { + filtered[key] = filter(value as loose, subset as loose); + continue; + } + } + filtered[key] = value; + } + return filtered; + })(actual, expected), + expected, + ); +} + +/** * Forcefully throws a failed assertion */ export function fail(msg?: string): void { |