diff --git a/packages/expect/src/jest-asymmetric-matchers.ts b/packages/expect/src/jest-asymmetric-matchers.ts index 9525ed22db78..524b345f036e 100644 --- a/packages/expect/src/jest-asymmetric-matchers.ts +++ b/packages/expect/src/jest-asymmetric-matchers.ts @@ -167,8 +167,8 @@ export class ObjectContaining extends AsymmetricMatcher< result = false break } - const value = Object.getOwnPropertyDescriptor(this.sample, property)?.value ?? this.sample[property] - const otherValue = Object.getOwnPropertyDescriptor(other, property)?.value ?? other[property] + const value = this.sample[property] + const otherValue = other[property] if (!equals( value, otherValue, diff --git a/test/core/test/jest-expect.test.ts b/test/core/test/jest-expect.test.ts index 903ec9e8f9e1..81a4ed20daed 100644 --- a/test/core/test/jest-expect.test.ts +++ b/test/core/test/jest-expect.test.ts @@ -1365,6 +1365,80 @@ it('toMatchObject', () => { expect(new Map()).toMatchObject({}) }) +it('proxy equality', () => { + // { intercepted: 'original', passthrough: 'original' } => { intercepted: 'proxied', passthrough: 'original' } + const proxyActual = new Proxy({ intercepted: 'original', passthrough: 'original' }, { + get(target, prop, receiver) { + if (prop === 'intercepted') { + return 'proxied' + } + return Reflect.get(target, prop, receiver) + }, + }) + // { intercepted: 'original' } => { intercepted: 'proxied' } + const proxyExpected = new Proxy({ intercepted: 'original' }, { + get(target, prop, receiver) { + if (prop === 'intercepted') { + return 'proxied' + } + return Reflect.get(target, prop, receiver) + }, + }) + + // objectContaining + expect(proxyActual).toEqual(expect.objectContaining({ intercepted: 'proxied', passthrough: 'original' })) + expect(proxyActual).not.toEqual(expect.objectContaining({ intercepted: 'original' })) + expect({ intercepted: 'proxied', extra: 'ignored' }).toEqual(expect.objectContaining(proxyExpected)) + expect({ intercepted: 'original' }).not.toEqual(expect.objectContaining(proxyExpected)) + + // toMatchObject + expect(proxyActual).toMatchObject({ intercepted: 'proxied', passthrough: 'original' }) + expect(proxyActual).not.toMatchObject({ intercepted: 'original' }) + expect({ intercepted: 'proxied', extra: 'ignored' }).toMatchObject(proxyExpected) + expect({ intercepted: 'original' }).not.toMatchObject(proxyExpected) + + // toEqual + expect(proxyActual).toEqual({ intercepted: 'proxied', passthrough: 'original' }) + expect(proxyActual).not.toEqual({ intercepted: 'original', passthrough: 'original' }) + expect({ intercepted: 'proxied' }).toEqual(proxyExpected) + expect({ intercepted: 'original' }).not.toEqual(proxyExpected) + + // empty target proxy with only `get` trap + const proxyBad = new Proxy({} as any, { + get(target, prop, receiver) { + if (prop === 'virtual') { + return 'value' + } + return Reflect.get(target, prop, receiver) + }, + }) + expect(proxyBad).not.toEqual(expect.objectContaining({ virtual: 'value' })) + expect(proxyBad).not.toMatchObject({ virtual: 'value' }) + expect(proxyBad).not.toEqual({ virtual: 'value' }) + expect({ virtual: 'value' }).not.toEqual(proxyBad) + + // empty target proxy with required traps + const proxyGood = new Proxy({}, { + get(target, prop, receiver) { + if (prop === 'virtual') { + return 'value' + } + return Reflect.get(target, prop, receiver) + }, + ownKeys: () => ['virtual'], + getOwnPropertyDescriptor(target, prop) { + if (prop === 'virtual') { + return { enumerable: true, configurable: true, value: 'value' } + } + return Reflect.getOwnPropertyDescriptor(target, prop) + }, + }) + expect(proxyGood).toEqual({ virtual: 'value' }) + expect(proxyGood).toMatchObject({ virtual: 'value' }) + expect(proxyGood).toEqual(expect.objectContaining({ virtual: 'value' })) + expect({ virtual: 'value' }).toEqual(proxyGood) +}) + it('toMatchObject error diff', () => { // single property on root (3 total properties, 1 expected) expect(getError(() => expect({ a: 1, b: 2, c: 3 }).toMatchObject({ c: 4 }))).toMatchInlineSnapshot(`