From 740a8d3f325e4e6eeca0e69e26079b4fec669248 Mon Sep 17 00:00:00 2001 From: Josh Kelley Date: Tue, 24 Jan 2023 16:29:13 -0500 Subject: [PATCH 1/2] Include symbol keys when diffing objects As a result of #13639, `expect.toMatchObject` can now compare symbol keys. However, the diffs that it generates if a symbol key doesn't match are misleading. For example, the following assertion: ``` const Foo = Symbol("foo"); test("mismatched symbols", () => { expect({ a: 1, [Foo]: {id: 1} }).toMatchObject({ a: 1, [Foo]: {id: 2} }); }); ``` fails as desired, but it displays the following diff: ``` - Expected - 3 + Received + 0 Object { "a": 1, - Symbol(foo): Object { - "id": 2, - }, } ``` *Note*: In inspecting the code, I wonder if `getObjectSubset` should use the same logic as `subsetEquality` - i.e., if `subsetEquality` does not evaluate an object's inherited string keys when determining equality, then `getObjectSubset` should not evaluate an object's inherited string keys for reporting on inequality? To put it another way - #13639 appears to change `subsetEquality` from considering an object's inherited string keys to not considering inherited string keys, and I'm not sure if that was intentional in a SemVer-minor change. For now, I've preserved the previous behavior, but let me know if this should change. Fixes #13809 --- packages/expect-utils/src/utils.ts | 15 +++++++++++++-- .../__tests__/__snapshots__/matchers.test.js.snap | 4 ++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/expect-utils/src/utils.ts b/packages/expect-utils/src/utils.ts index 32a403ba4874..0c1cc7b243fe 100644 --- a/packages/expect-utils/src/utils.ts +++ b/packages/expect-utils/src/utils.ts @@ -43,6 +43,17 @@ const hasPropertyInObject = (object: object, key: string | symbol): boolean => { ); }; +// Retrieves an object's keys for evaluation by getObjectSubset. This evaluates +// the prototype chain for string keys but not for symbols. (Otherwise, it +// could find values such as a Set or Map's Symbol.toStringTag, with unexpected +// results.) +// +// Compare with subsetEquality's use of Reflect.ownKeys. +const getObjectKeys = (object: object) => [ + ...Object.keys(object), + ...Object.getOwnPropertySymbols(object), +]; + export const getPath = ( object: Record, propertyPath: string | Array, @@ -131,7 +142,7 @@ export const getObjectSubset = ( const trimmed: any = {}; seenReferences.set(object, trimmed); - Object.keys(object) + getObjectKeys(object) .filter(key => hasPropertyInObject(subset, key)) .forEach(key => { trimmed[key] = seenReferences.has(object[key]) @@ -144,7 +155,7 @@ export const getObjectSubset = ( ); }); - if (Object.keys(trimmed).length > 0) { + if (getObjectKeys(trimmed).length > 0) { return trimmed; } } diff --git a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap index f02029f00d96..31c26a85f106 100644 --- a/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap +++ b/packages/expect/src/__tests__/__snapshots__/matchers.test.js.snap @@ -4140,13 +4140,13 @@ exports[`toMatchObject() {pass: false} expect({"a": "a", "c": "d"}).toMatchObjec exports[`toMatchObject() {pass: false} expect({"a": "b", "c": "d", Symbol(jest): "jest"}).toMatchObject({"a": "c", Symbol(jest): Any}) 1`] = ` expect(received).toMatchObject(expected) -- Expected - 2 +- Expected - 1 + Received + 1 Object { - "a": "c", -- Symbol(jest): Any, + "a": "b", + Symbol(jest): Any, } `; From f5942b453aaa45301a4aec98b41852acfbf68a25 Mon Sep 17 00:00:00 2001 From: Josh Kelley Date: Tue, 24 Jan 2023 16:53:11 -0500 Subject: [PATCH 2/2] Update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 933aa3a6d3a6..d874dbd4acee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Fixes +- `[@jest/expect-utils]` `toMatchObject` diffs should include `Symbol` properties ([#13810](https://github.com/facebook/jest/pull/13810)) + ### Chore & Maintenance ### Performance