diff --git a/packages/expect/src/jest-asymmetric-matchers.ts b/packages/expect/src/jest-asymmetric-matchers.ts index da4e899d1e41..bc2f29e62249 100644 --- a/packages/expect/src/jest-asymmetric-matchers.ts +++ b/packages/expect/src/jest-asymmetric-matchers.ts @@ -1,3 +1,4 @@ +import { isObject } from '@vitest/utils' import type { ChaiPlugin, MatcherState } from './types' import { GLOBAL_EXPECT } from './constants' import { getState } from './state' @@ -147,7 +148,7 @@ export class ObjectContaining extends AsymmetricMatcher< const matcherContext = this.getMatcherContext() for (const property in this.sample) { if ( - !this.hasProperty(other, property) + !isObject(other) || !equals( this.sample[property], other[property], diff --git a/packages/expect/src/jest-utils.ts b/packages/expect/src/jest-utils.ts index e0bb88e65e69..6326dff9a40b 100644 --- a/packages/expect/src/jest-utils.ts +++ b/packages/expect/src/jest-utils.ts @@ -565,8 +565,7 @@ export function subsetEquality( seenReferences.set(subset[key], true) } const result - = object != null - && hasPropertyInObject(object, key) + = isObject(object) && equals(object[key], subset[key], [ ...filteredCustomTesters, subsetEqualityWithContext(seenReferences), diff --git a/test/core/test/__snapshots__/jest-expect.test.ts.snap b/test/core/test/__snapshots__/jest-expect.test.ts.snap index 85b30fbf13c6..57a8fbd84d3f 100644 --- a/test/core/test/__snapshots__/jest-expect.test.ts.snap +++ b/test/core/test/__snapshots__/jest-expect.test.ts.snap @@ -311,6 +311,78 @@ exports[`asymmetric matcher error 23`] = ` } `; +exports[`proxy 1`] = ` +{ + "actual": "Object {}", + "diff": "- Expected ++ Received + +- Object { +- "key": "no", +- } ++ Object {}", + "expected": "Object { + "key": "no", +}", + "message": "expected {} to match object { key: 'no' }", +} +`; + +exports[`proxy 2`] = ` +{ + "actual": "Object { + "key": "value", +}", + "diff": "- Expected ++ Received + + Object { +- "key": "no", ++ "key": "value", + }", + "expected": "Object { + "key": "no", +}", + "message": "expected { key: 'value' } to match object { key: 'no' }", +} +`; + +exports[`proxy equality 1`] = ` +{ + "actual": "Object {}", + "diff": "- Expected ++ Received + +- Object { +- "key": "no", +- } ++ Object {}", + "expected": "Object { + "key": "no", +}", + "message": "expected {} to match object { key: 'no' }", +} +`; + +exports[`proxy equality 2`] = ` +{ + "actual": "Object { + "key": "value", +}", + "diff": "- Expected ++ Received + + Object { +- "key": "no", ++ "key": "value", + }", + "expected": "Object { + "key": "no", +}", + "message": "expected { key: 'value' } to match object { key: 'no' }", +} +`; + exports[`toHaveBeenNthCalledWith error 1`] = ` { "actual": "Array [ diff --git a/test/core/test/jest-expect.test.ts b/test/core/test/jest-expect.test.ts index b97d1a7c6297..498301356bb8 100644 --- a/test/core/test/jest-expect.test.ts +++ b/test/core/test/jest-expect.test.ts @@ -1354,3 +1354,53 @@ it('toMatch/toContain diff', () => { }) it('timeout', () => new Promise(resolve => setTimeout(resolve, 500))) + +it('proxy equality', () => { + const proxy = new Proxy( + {}, + { + get(target, prop, receiver) { + if (prop === 'key') { + return 'value' + } + return Reflect.get(target, prop, receiver) + }, + }, + ) + + expect(proxy).toMatchObject({ key: 'value' }) + snapshotError(() => expect(proxy).toMatchObject({ key: 'no' })) + + const proxyWithKeys = new Proxy( + {}, + { + get(target, prop, receiver) { + if (prop === 'key') { + return 'value' + } + return Reflect.get(target, prop, receiver) + }, + // need more handlers to enumerate keys for diff + ownKeys(target) { + return [...Reflect.ownKeys(target), 'key'] + }, + getOwnPropertyDescriptor(target, prop) { + if (prop === 'key') { + return { configurable: true, enumerable: true } + } + return Reflect.getOwnPropertyDescriptor(target, prop) + }, + }, + ) + + expect(proxyWithKeys).toMatchObject({ key: 'value' }) + snapshotError(() => expect(proxyWithKeys).toMatchObject({ key: 'no' })) + + // objectContaining + expect(proxy).toEqual(expect.objectContaining({ key: 'value' })) + expect(proxyWithKeys).toEqual(expect.objectContaining({ key: 'value' })) + + // toEqual + expect(proxy).not.toEqual({ key: 'value' }) + expect(proxyWithKeys).toEqual({ key: 'value' }) // shouldn't succeed? +})