diff --git a/CHANGELOG.md b/CHANGELOG.md index a66604ccaa6e..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)) 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,