mirror of
https://github.com/denoland/std.git
synced 2024-11-21 12:40:03 +00:00
fix(assert): fix assertObjectMatch()
prints arrays as objects (#5503)
fix(assert): fix ssertObjectMatch prints arrays as objects
This commit is contained in:
parent
b6b0d68381
commit
5cff014b02
@ -49,7 +49,7 @@ function isObject(val: unknown): boolean {
|
||||
return typeof val === "object" && val !== null;
|
||||
}
|
||||
|
||||
function filter(a: loose, b: loose) {
|
||||
function filter(a: loose, b: loose): loose {
|
||||
const seen = new WeakMap();
|
||||
return filterObject(a, b);
|
||||
|
||||
@ -97,7 +97,7 @@ function filter(a: loose, b: loose) {
|
||||
|
||||
// On array references, build a filtered array and filter nested objects inside
|
||||
if (Array.isArray(value) && Array.isArray(subset)) {
|
||||
filtered[key] = filterObject({ ...value }, { ...subset });
|
||||
filtered[key] = filterArray(value, subset);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -135,4 +135,66 @@ function filter(a: loose, b: loose) {
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
function filterArray(a: unknown[], b: unknown[]): unknown[] {
|
||||
// Prevent infinite loop with circular references with same filter
|
||||
if (seen.has(a) && (seen.get(a) === b)) {
|
||||
return a;
|
||||
}
|
||||
|
||||
seen.set(a, b);
|
||||
|
||||
const filtered: unknown[] = [];
|
||||
const count = Math.min(a.length, b.length);
|
||||
|
||||
for (let i = 0; i < count; ++i) {
|
||||
const value = a[i];
|
||||
const subset = b[i];
|
||||
|
||||
// On regexp references, keep value as it to avoid loosing pattern and flags
|
||||
if (value instanceof RegExp) {
|
||||
filtered.push(value);
|
||||
continue;
|
||||
}
|
||||
|
||||
// On array references, build a filtered array and filter nested objects inside
|
||||
if (Array.isArray(value) && Array.isArray(subset)) {
|
||||
filtered.push(filterArray(value, subset));
|
||||
continue;
|
||||
}
|
||||
|
||||
// On nested objects references, build a filtered object recursively
|
||||
if (isObject(value) && isObject(subset)) {
|
||||
// When both operands are maps, build a filtered map with common keys and filter nested objects inside
|
||||
if ((value instanceof Map) && (subset instanceof Map)) {
|
||||
const map = new Map(
|
||||
[...value].filter(([k]) => subset.has(k))
|
||||
.map(([k, v]) => {
|
||||
const v2 = subset.get(k);
|
||||
if (isObject(v) && isObject(v2)) {
|
||||
return [k, filterObject(v as loose, v2 as loose)];
|
||||
}
|
||||
|
||||
return [k, v];
|
||||
}),
|
||||
);
|
||||
filtered.push(map);
|
||||
continue;
|
||||
}
|
||||
|
||||
// When both operands are set, build a filtered set with common values
|
||||
if ((value instanceof Set) && (subset instanceof Set)) {
|
||||
filtered.push(value.intersection(subset));
|
||||
continue;
|
||||
}
|
||||
|
||||
filtered.push(filterObject(value as loose, subset as loose));
|
||||
continue;
|
||||
}
|
||||
|
||||
filtered.push(value);
|
||||
}
|
||||
|
||||
return filtered;
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,11 @@ const n = {
|
||||
["b", b],
|
||||
]),
|
||||
};
|
||||
const o = { foo: [new Map([["bar", n.bar], ["baz", null]])] };
|
||||
const p = { bar: [new Set([1, 2, 3])] };
|
||||
const q = { foo: [1, 2] as unknown[] };
|
||||
q.foo[2] = q.foo;
|
||||
const r = { bar: [[1, [2, [q]]]] };
|
||||
|
||||
Deno.test("assertObjectMatch() matches simple subset", () => {
|
||||
assertObjectMatch(a, {
|
||||
@ -79,6 +84,7 @@ Deno.test("assertObjectMatch() matches subset with circular reference", () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
assertObjectMatch(q, { foo: [1, 2, [1, 2, [1, 2, [1, 2]]]] });
|
||||
});
|
||||
|
||||
Deno.test("assertObjectMatch() matches subset with interface", () => {
|
||||
@ -105,6 +111,7 @@ Deno.test("assertObjectMatch() matches subset with nested array inside", () => {
|
||||
assertObjectMatch(j, { foo: [[1, 2, 3]] });
|
||||
assertObjectMatch(k, { foo: [[1, [2, [3]]]] });
|
||||
assertObjectMatch(l, { foo: [[1, [2, [a, e, j, k]]]] });
|
||||
assertObjectMatch(r, { bar: [[1, [2, [q]]]] });
|
||||
});
|
||||
|
||||
Deno.test("assertObjectMatch() matches subset with regexp", () => {
|
||||
@ -117,6 +124,9 @@ Deno.test("assertObjectMatch() matches subset with built-in data structures", ()
|
||||
assertObjectMatch(n, { bar: new Map([["bar", 2]]) });
|
||||
assertObjectMatch(n, { baz: new Map([["b", b]]) });
|
||||
assertObjectMatch(n, { baz: new Map([["b", { foo: true }]]) });
|
||||
assertObjectMatch(o, { foo: [new Map([["baz", null]])] });
|
||||
assertObjectMatch(o, { foo: [new Map([["bar", n.bar]])] });
|
||||
assertObjectMatch(p, { bar: [new Set([2, 3])] });
|
||||
});
|
||||
|
||||
Deno.test("assertObjectMatch() throws when a key is missing from subset", () => {
|
||||
@ -341,6 +351,10 @@ Deno.test("assertObjectMatch() prints inputs correctly", () => {
|
||||
description: "foo",
|
||||
},
|
||||
name: "somegroup",
|
||||
nodes: [
|
||||
"somenode",
|
||||
"someothernode",
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
@ -356,9 +370,46 @@ Deno.test("assertObjectMatch() prints inputs correctly", () => {
|
||||
+ description: "foo",
|
||||
+ },
|
||||
+ name: "somegroup",
|
||||
+ nodes: [
|
||||
+ "somenode",
|
||||
+ "someothernode",
|
||||
+ ],
|
||||
- message: "NodeNotFound",
|
||||
},
|
||||
protocol: "graph",
|
||||
}`,
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
() => assertObjectMatch({ foo: [] }, { foo: ["bar"] }),
|
||||
AssertionError,
|
||||
` {
|
||||
+ foo: [
|
||||
+ "bar",
|
||||
+ ],
|
||||
- foo: [],
|
||||
}`,
|
||||
);
|
||||
|
||||
const a = {};
|
||||
const b = {};
|
||||
|
||||
Object.defineProperty(a, "hello", {
|
||||
value: "world",
|
||||
enumerable: false,
|
||||
});
|
||||
|
||||
Object.defineProperty(b, "foo", {
|
||||
value: "bar",
|
||||
enumerable: false,
|
||||
});
|
||||
|
||||
assertThrows(
|
||||
() => assertObjectMatch(a, b),
|
||||
AssertionError,
|
||||
` {
|
||||
- hello: "world",
|
||||
+ foo: "bar",
|
||||
}`,
|
||||
);
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user