Skip to content

Commit 7ce3417

Browse files
hi-ogawaclaude
andauthored
fix(expect): fix objectContaining with proxy (#9554)
Co-authored-by: Claude Opus 4.5 <[email protected]>
1 parent 20e00ef commit 7ce3417

File tree

2 files changed

+76
-2
lines changed

2 files changed

+76
-2
lines changed

packages/expect/src/jest-asymmetric-matchers.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -167,8 +167,8 @@ export class ObjectContaining extends AsymmetricMatcher<
167167
result = false
168168
break
169169
}
170-
const value = Object.getOwnPropertyDescriptor(this.sample, property)?.value ?? this.sample[property]
171-
const otherValue = Object.getOwnPropertyDescriptor(other, property)?.value ?? other[property]
170+
const value = this.sample[property]
171+
const otherValue = other[property]
172172
if (!equals(
173173
value,
174174
otherValue,

test/core/test/jest-expect.test.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1365,6 +1365,80 @@ it('toMatchObject', () => {
13651365
expect(new Map()).toMatchObject({})
13661366
})
13671367

1368+
it('proxy equality', () => {
1369+
// { intercepted: 'original', passthrough: 'original' } => { intercepted: 'proxied', passthrough: 'original' }
1370+
const proxyActual = new Proxy({ intercepted: 'original', passthrough: 'original' }, {
1371+
get(target, prop, receiver) {
1372+
if (prop === 'intercepted') {
1373+
return 'proxied'
1374+
}
1375+
return Reflect.get(target, prop, receiver)
1376+
},
1377+
})
1378+
// { intercepted: 'original' } => { intercepted: 'proxied' }
1379+
const proxyExpected = new Proxy({ intercepted: 'original' }, {
1380+
get(target, prop, receiver) {
1381+
if (prop === 'intercepted') {
1382+
return 'proxied'
1383+
}
1384+
return Reflect.get(target, prop, receiver)
1385+
},
1386+
})
1387+
1388+
// objectContaining
1389+
expect(proxyActual).toEqual(expect.objectContaining({ intercepted: 'proxied', passthrough: 'original' }))
1390+
expect(proxyActual).not.toEqual(expect.objectContaining({ intercepted: 'original' }))
1391+
expect({ intercepted: 'proxied', extra: 'ignored' }).toEqual(expect.objectContaining(proxyExpected))
1392+
expect({ intercepted: 'original' }).not.toEqual(expect.objectContaining(proxyExpected))
1393+
1394+
// toMatchObject
1395+
expect(proxyActual).toMatchObject({ intercepted: 'proxied', passthrough: 'original' })
1396+
expect(proxyActual).not.toMatchObject({ intercepted: 'original' })
1397+
expect({ intercepted: 'proxied', extra: 'ignored' }).toMatchObject(proxyExpected)
1398+
expect({ intercepted: 'original' }).not.toMatchObject(proxyExpected)
1399+
1400+
// toEqual
1401+
expect(proxyActual).toEqual({ intercepted: 'proxied', passthrough: 'original' })
1402+
expect(proxyActual).not.toEqual({ intercepted: 'original', passthrough: 'original' })
1403+
expect({ intercepted: 'proxied' }).toEqual(proxyExpected)
1404+
expect({ intercepted: 'original' }).not.toEqual(proxyExpected)
1405+
1406+
// empty target proxy with only `get` trap
1407+
const proxyBad = new Proxy({} as any, {
1408+
get(target, prop, receiver) {
1409+
if (prop === 'virtual') {
1410+
return 'value'
1411+
}
1412+
return Reflect.get(target, prop, receiver)
1413+
},
1414+
})
1415+
expect(proxyBad).not.toEqual(expect.objectContaining({ virtual: 'value' }))
1416+
expect(proxyBad).not.toMatchObject({ virtual: 'value' })
1417+
expect(proxyBad).not.toEqual({ virtual: 'value' })
1418+
expect({ virtual: 'value' }).not.toEqual(proxyBad)
1419+
1420+
// empty target proxy with required traps
1421+
const proxyGood = new Proxy({}, {
1422+
get(target, prop, receiver) {
1423+
if (prop === 'virtual') {
1424+
return 'value'
1425+
}
1426+
return Reflect.get(target, prop, receiver)
1427+
},
1428+
ownKeys: () => ['virtual'],
1429+
getOwnPropertyDescriptor(target, prop) {
1430+
if (prop === 'virtual') {
1431+
return { enumerable: true, configurable: true, value: 'value' }
1432+
}
1433+
return Reflect.getOwnPropertyDescriptor(target, prop)
1434+
},
1435+
})
1436+
expect(proxyGood).toEqual({ virtual: 'value' })
1437+
expect(proxyGood).toMatchObject({ virtual: 'value' })
1438+
expect(proxyGood).toEqual(expect.objectContaining({ virtual: 'value' }))
1439+
expect({ virtual: 'value' }).toEqual(proxyGood)
1440+
})
1441+
13681442
it('toMatchObject error diff', () => {
13691443
// single property on root (3 total properties, 1 expected)
13701444
expect(getError(() => expect({ a: 1, b: 2, c: 3 }).toMatchObject({ c: 4 }))).toMatchInlineSnapshot(`

0 commit comments

Comments
 (0)