Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit afddeec

Browse files
committed
maybe address comments
1 parent 61ed623 commit afddeec

3 files changed

Lines changed: 117 additions & 79 deletions

File tree

packages/expect-utils/src/__tests__/utils.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,10 @@ describe('iterableEquality', () => {
660660
};
661661

662662
expect(() => iterableEquality(badIterable, badIterable)).not.toThrow();
663-
expect(iterableEquality(badIterable, badIterable)).toBe(false);
663+
// Returns undefined so equals() can fall through to Object.is / property checks.
664+
expect(iterableEquality(badIterable, badIterable)).toBeUndefined();
665+
// Same reference must still be considered equal via equals().
666+
expect(equals(badIterable, badIterable, [iterableEquality])).toBe(true);
664667
});
665668
});
666669

packages/expect-utils/src/utils.ts

Lines changed: 103 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -234,104 +234,129 @@ export const iterableEquality = (
234234
iterableEqualityWithStack,
235235
];
236236

237-
try {
238-
if (a.size !== undefined) {
239-
if (a.size !== b.size) {
240-
return false;
241-
} else if (isA<Set<unknown>>('Set', a) || isImmutableUnorderedSet(a)) {
242-
let allFound = true;
243-
for (const aValue of a) {
244-
if (!b.has(aValue)) {
245-
let has = false;
246-
for (const bValue of b) {
247-
const isEqual = equals(aValue, bValue, filteredCustomTesters);
248-
if (isEqual === true) {
249-
has = true;
250-
}
237+
if (a.size !== undefined) {
238+
if (a.size !== b.size) {
239+
aStack.pop();
240+
bStack.pop();
241+
return false;
242+
} else if (isA<Set<unknown>>('Set', a) || isImmutableUnorderedSet(a)) {
243+
let allFound = true;
244+
for (const aValue of a) {
245+
if (!b.has(aValue)) {
246+
let has = false;
247+
for (const bValue of b) {
248+
const isEqual = equals(aValue, bValue, filteredCustomTesters);
249+
if (isEqual === true) {
250+
has = true;
251251
}
252+
}
252253

253-
if (has === false) {
254-
allFound = false;
255-
break;
256-
}
254+
if (has === false) {
255+
allFound = false;
256+
break;
257257
}
258258
}
259-
return allFound;
260-
} else if (
261-
isA<Map<unknown, unknown>>('Map', a) ||
262-
isImmutableUnorderedKeyed(a)
263-
) {
264-
let allFound = true;
265-
for (const aEntry of a) {
266-
if (
267-
!b.has(aEntry[0]) ||
268-
!equals(aEntry[1], b.get(aEntry[0]), filteredCustomTesters)
269-
) {
270-
let has = false;
271-
for (const bEntry of b) {
272-
const matchedKey = equals(
273-
aEntry[0],
274-
bEntry[0],
259+
}
260+
// Remove the first value from the stack of traversed values.
261+
aStack.pop();
262+
bStack.pop();
263+
return allFound;
264+
} else if (
265+
isA<Map<unknown, unknown>>('Map', a) ||
266+
isImmutableUnorderedKeyed(a)
267+
) {
268+
let allFound = true;
269+
for (const aEntry of a) {
270+
if (
271+
!b.has(aEntry[0]) ||
272+
!equals(aEntry[1], b.get(aEntry[0]), filteredCustomTesters)
273+
) {
274+
let has = false;
275+
for (const bEntry of b) {
276+
const matchedKey = equals(
277+
aEntry[0],
278+
bEntry[0],
279+
filteredCustomTesters,
280+
);
281+
282+
let matchedValue = false;
283+
if (matchedKey === true) {
284+
matchedValue = equals(
285+
aEntry[1],
286+
bEntry[1],
275287
filteredCustomTesters,
276288
);
277-
278-
let matchedValue = false;
279-
if (matchedKey === true) {
280-
matchedValue = equals(
281-
aEntry[1],
282-
bEntry[1],
283-
filteredCustomTesters,
284-
);
285-
}
286-
if (matchedValue === true) {
287-
has = true;
288-
}
289289
}
290-
291-
if (has === false) {
292-
allFound = false;
293-
break;
290+
if (matchedValue === true) {
291+
has = true;
294292
}
295293
}
294+
295+
if (has === false) {
296+
allFound = false;
297+
break;
298+
}
296299
}
297-
return allFound;
298300
}
301+
// Remove the first value from the stack of traversed values.
302+
aStack.pop();
303+
bStack.pop();
304+
return allFound;
299305
}
306+
}
300307

301-
const bIterator = b[IteratorSymbol]();
302-
303-
for (const aValue of a) {
304-
const nextB = bIterator.next();
305-
if (nextB.done || !equals(aValue, nextB.value, filteredCustomTesters)) {
306-
return false;
307-
}
308-
}
309-
if (!bIterator.next().done) {
310-
return false;
311-
}
308+
let aIterator: Iterator<unknown>;
309+
let bIterator: Iterator<unknown>;
310+
try {
311+
aIterator = a[IteratorSymbol]();
312+
bIterator = b[IteratorSymbol]();
313+
} catch {
314+
// If the iterator factory itself throws (e.g. a TypedArray method used as
315+
// [Symbol.iterator] on a plain object), we cannot compare as iterables.
316+
// Return undefined so equals() falls through to Object.is / property checks.
317+
aStack.pop();
318+
bStack.pop();
319+
return undefined;
320+
}
312321

322+
let aStep = aIterator.next();
323+
while (!aStep.done) {
324+
const bStep = bIterator.next();
313325
if (
314-
!isImmutableList(a) &&
315-
!isImmutableOrderedKeyed(a) &&
316-
!isImmutableOrderedSet(a) &&
317-
!isImmutableRecord(a)
326+
bStep.done ||
327+
!equals(aStep.value, bStep.value, filteredCustomTesters)
318328
) {
319-
const aEntries = entries(a);
320-
const bEntries = entries(b);
321-
if (!equals(aEntries, bEntries)) {
322-
return false;
323-
}
329+
aStack.pop();
330+
bStack.pop();
331+
return false;
324332
}
325-
326-
return true;
327-
} catch {
328-
// If an exotic iterator/getter throws (DOM objects, proxies, host objects),
329-
// treat it as "not equal" rather than crashing the matcher.
330-
return false;
331-
} finally {
333+
aStep = aIterator.next();
334+
}
335+
if (!bIterator.next().done) {
332336
aStack.pop();
333337
bStack.pop();
338+
return false;
334339
}
340+
341+
if (
342+
!isImmutableList(a) &&
343+
!isImmutableOrderedKeyed(a) &&
344+
!isImmutableOrderedSet(a) &&
345+
!isImmutableRecord(a)
346+
) {
347+
const aEntries = entries(a);
348+
const bEntries = entries(b);
349+
if (!equals(aEntries, bEntries)) {
350+
aStack.pop();
351+
bStack.pop();
352+
return false;
353+
}
354+
}
355+
356+
// Remove the first value from the stack of traversed values.
357+
aStack.pop();
358+
bStack.pop();
359+
return true;
335360
};
336361

337362
const entries = (obj: any) => {

packages/expect/src/__tests__/matchers.test.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2389,4 +2389,14 @@ describe('toMatchObject()', () => {
23892389
[sym]: true,
23902390
});
23912391
});
2392+
2393+
test('does not throw on exotic iterables (e.g. TypedArray iterator on plain object)', () => {
2394+
const badIterable = {
2395+
[Symbol.iterator]: Uint8Array.prototype[Symbol.iterator],
2396+
};
2397+
expect(() =>
2398+
jestExpect([badIterable]).toMatchObject([badIterable]),
2399+
).not.toThrow();
2400+
jestExpect([badIterable]).toMatchObject([badIterable]);
2401+
});
23922402
});

0 commit comments

Comments
 (0)