From b72b21d38a1c3ea6fb5699f38c5f21219c92ea49 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 2 Nov 2023 13:27:29 +1100 Subject: [PATCH 1/2] exclude non-enumerable symbol in object matching --- CHANGELOG.md | 1 + .../expect-utils/src/__tests__/utils.test.ts | 45 +++++++++++++++++++ packages/expect-utils/src/utils.ts | 24 +++++++--- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a66604ccaa6e..52c50f8600a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ - [**BREAKING**] Changes `testPathPattern` configuration option to `testPathPatterns`, which now takes a list of patterns instead of the regex. - [**BREAKING**] `--testPathPattern` is now `--testPathPatterns` - `[jest-reporters, jest-runner]` Unhandled errors without stack get correctly logged to console ([#14619](https://github.com/facebook/jest/pull/14619)) +- `[@jest/expect-utils]` [**BREAKING**] exclude non-enumerable in object matching ([#14670](https://github.com/jestjs/jest/pull/14670)) ### Performance diff --git a/packages/expect-utils/src/__tests__/utils.test.ts b/packages/expect-utils/src/__tests__/utils.test.ts index c4d7893e4298..a563e9d9a860 100644 --- a/packages/expect-utils/src/__tests__/utils.test.ts +++ b/packages/expect-utils/src/__tests__/utils.test.ts @@ -355,6 +355,51 @@ describe('subsetEquality()', () => { ).toBe(false); }); }); + + describe('matching subsets with symbols', () => { + describe('same symbol', () => { + test('objects to not match with value diff', () => { + const symbol = Symbol('foo'); + expect(subsetEquality({[symbol]: 1}, {[symbol]: 2})).toBe(false); + }); + + test('objects to match with non-enumerable symbols', () => { + const symbol = Symbol('foo'); + const foo = {}; + Object.defineProperty(foo, symbol, { + enumerable: false, + value: 1, + }); + const bar = {}; + Object.defineProperty(bar, symbol, { + enumerable: false, + value: 2, + }); + expect(subsetEquality(foo, bar)).toBe(true); + }); + }); + + describe('different symbol', () => { + test('objects to not match with same value', () => { + expect(subsetEquality({[Symbol('foo')]: 1}, {[Symbol('foo')]: 2})).toBe( + false, + ); + }); + test('objects to match with non-enumerable symbols', () => { + const foo = {}; + Object.defineProperty(foo, Symbol('foo'), { + enumerable: false, + value: 1, + }); + const bar = {}; + Object.defineProperty(bar, Symbol('foo'), { + enumerable: false, + value: 2, + }); + expect(subsetEquality(foo, bar)).toBe(true); + }); + }); + }); }); describe('iterableEquality', () => { diff --git a/packages/expect-utils/src/utils.ts b/packages/expect-utils/src/utils.ts index c666e6697d9b..45d341bfdd78 100644 --- a/packages/expect-utils/src/utils.ts +++ b/packages/expect-utils/src/utils.ts @@ -44,13 +44,23 @@ 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.) -export const getObjectKeys = (object: object): Array => [ - ...Object.keys(object), - ...Object.getOwnPropertySymbols(object), -]; +// the prototype chain for string keys but not for non-enumerable symbols. +// (Otherwise, it could find values such as a Set or Map's Symbol.toStringTag, +// with unexpected results.) +// export const getObjectKeys = (object: object): Array => [ +// ...Object.keys(object), +// ...Object.getOwnPropertySymbols(object).filter( +// s => Object.getOwnPropertyDescriptor(object, s)?.enumerable, +// ), +// ]; +export const getObjectKeys = (object: object): Array => { + return [ + ...Object.keys(object), + ...Object.getOwnPropertySymbols(object).filter( + s => Object.getOwnPropertyDescriptor(object, s)?.enumerable, + ), + ]; +}; export const getPath = ( object: Record, From fe4fe1eb5bfa61704e4813155602337740ee7078 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 2 Nov 2023 13:27:50 +0100 Subject: [PATCH 2/2] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52c50f8600a1..dc4bc7cd6bd8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - `[jest-circus, jest-expect, jest-snapshot]` Pass `test.failing` tests when containing failing snapshot matchers ([#14313](https://github.com/jestjs/jest/pull/14313)) - `[jest-config]` Make sure to respect `runInBand` option ([#14578](https://github.com/facebook/jest/pull/14578)) - `[@jest/expect-utils]` Fix comparison of `DataView` ([#14408](https://github.com/jestjs/jest/pull/14408)) +- `[@jest/expect-utils]` [**BREAKING**] exclude non-enumerable in object matching ([#14670](https://github.com/jestjs/jest/pull/14670)) - `[jest-leak-detector]` Make leak-detector more aggressive when running GC ([#14526](https://github.com/jestjs/jest/pull/14526)) - `[jest-runtime]` Properly handle re-exported native modules in ESM via CJS ([#14589](https://github.com/jestjs/jest/pull/14589)) - `[jest-util]` Make sure `isInteractive` works in a browser ([#14552](https://github.com/jestjs/jest/pull/14552)) @@ -32,7 +33,6 @@ - [**BREAKING**] Changes `testPathPattern` configuration option to `testPathPatterns`, which now takes a list of patterns instead of the regex. - [**BREAKING**] `--testPathPattern` is now `--testPathPatterns` - `[jest-reporters, jest-runner]` Unhandled errors without stack get correctly logged to console ([#14619](https://github.com/facebook/jest/pull/14619)) -- `[@jest/expect-utils]` [**BREAKING**] exclude non-enumerable in object matching ([#14670](https://github.com/jestjs/jest/pull/14670)) ### Performance