Skip to content

Commit

Permalink
Fix subsetEquality: same referenced object on same level node of tree…
Browse files Browse the repository at this point in the history
… is regarded as circular reference
  • Loading branch information
Gianfranco committed Dec 18, 2019
1 parent 72a1447 commit 7a27e77
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
- `[jest-utils]` Allow querying process.domain ([#9136](https://github.com/facebook/jest/pull/9136))
- `[pretty-format]` Correctly detect memoized elements ([#9196](https://github.com/facebook/jest/pull/9196))
- `[jest-fake-timers]` Support `util.promisify` on `setTimeout` ([#9180](https://github.com/facebook/jest/pull/9180))
- `[expect]` Fix subsetEquality: fix circular reference handling logic ([#9322](https://github.com/facebook/jest/pull/9322))

### Chore & Maintenance

Expand Down
13 changes: 13 additions & 0 deletions packages/expect/src/__tests__/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,19 @@ describe('subsetEquality()', () => {
expect(subsetEquality(primitiveInsteadOfRef, circularObjA1)).toBe(false);
});

test('referenced object on same level should not regarded as circular reference', () => {
const referencedObj = {abc: 'def'};
const object = {
a: {abc: 'def'},
b: {abc: 'def', zzz: 'zzz'},
};
const thisIsNotCircular = {
a: referencedObj,
b: referencedObj,
};
expect(subsetEquality(object, thisIsNotCircular)).toBeTruthy();
});

test('transitive circular references', () => {
const transitiveCircularObjA1 = {a: 'hello'};
transitiveCircularObjA1.nestedObj = {parentObj: transitiveCircularObjA1};
Expand Down
16 changes: 10 additions & 6 deletions packages/expect/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@ export const iterableEquality = (
if (a.constructor !== b.constructor) {
return false;
}

let length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
Expand Down Expand Up @@ -290,20 +289,25 @@ export const subsetEquality = (

return Object.keys(subset).every(key => {
if (isObjectWithKeys(subset[key])) {
if (seenReferences.get(subset[key])) {
if (seenReferences.has(subset[key])) {
return equals(object[key], subset[key], [iterableEquality]);
}
seenReferences.set(subset[key], true);
}

return (
const result =
object != null &&
hasOwnProperty(object, key) &&
equals(object[key], subset[key], [
iterableEquality,
subsetEqualityWithContext(seenReferences),
])
);
]);
// The main goal of using seenReference is to avoid circular node on tree.
// It will only happen within a parent and its child, not a node and nodes next to it (same level)
// We should keep the reference for a parent and its child only
// Thus we should delete the reference immediately so that it doesn't interfere
// other nodes within the same level on tree.
seenReferences.delete(subset[key]);
return result;
});
};

Expand Down

0 comments on commit 7a27e77

Please sign in to comment.