Skip to content

Commit d289e7e

Browse files
authoredOct 3, 2024··
fix(expect): preserve prototype in toMatchObject diff (#6620)
1 parent 0ce26a4 commit d289e7e

File tree

3 files changed

+132
-3
lines changed

3 files changed

+132
-3
lines changed
 

‎packages/expect/src/jest-expect.ts

+1
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
195195
const { subset: actualSubset, stripped } = getObjectSubset(
196196
actual,
197197
expected,
198+
customTesters,
198199
)
199200
if ((pass && isNot) || (!pass && !isNot)) {
200201
const msg = utils.getMessage(this, [

‎packages/expect/src/jest-utils.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -676,7 +676,7 @@ export function getObjectKeys(object: object): Array<string | symbol> {
676676
export function getObjectSubset(
677677
object: any,
678678
subset: any,
679-
customTesters: Array<Tester> = [],
679+
customTesters: Array<Tester>,
680680
): { subset: any; stripped: number } {
681681
let stripped = 0
682682

@@ -702,13 +702,21 @@ export function getObjectSubset(
702702
subsetEquality,
703703
])
704704
) {
705-
// Avoid unnecessary copy which might return Object instead of subclass.
705+
// return "expected" subset to avoid showing irrelavant toMatchObject diff
706706
return subset
707707
}
708708

709709
const trimmed: any = {}
710710
seenReferences.set(object, trimmed)
711711

712+
// preserve constructor for toMatchObject diff
713+
if (typeof object.constructor === 'function' && typeof object.constructor.name === 'string') {
714+
Object.defineProperty(trimmed, 'constructor', {
715+
enumerable: false,
716+
value: object.constructor,
717+
})
718+
}
719+
712720
for (const key of getObjectKeys(object)) {
713721
if (hasPropertyInObject(subset, key)) {
714722
trimmed[key] = seenReferences.has(object[key])

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

+121-1
Original file line numberDiff line numberDiff line change
@@ -927,12 +927,12 @@ function trim(s: string): string {
927927
function getError(f: () => unknown) {
928928
try {
929929
f()
930-
return expect.unreachable()
931930
}
932931
catch (error) {
933932
const processed = processError(error)
934933
return [stripVTControlCharacters(processed.message), stripVTControlCharacters(trim(processed.diff))]
935934
}
935+
return expect.unreachable()
936936
}
937937

938938
it('toMatchObject error diff', () => {
@@ -1059,6 +1059,126 @@ it('toMatchObject error diff', () => {
10591059
}",
10601060
]
10611061
`)
1062+
1063+
// https://github.com/vitest-dev/vitest/issues/6543
1064+
class Foo {
1065+
constructor(public value: any) {}
1066+
}
1067+
1068+
class Bar {
1069+
constructor(public value: any) {}
1070+
}
1071+
1072+
expect(new Foo(0)).toMatchObject(new Bar(0))
1073+
expect(new Foo(0)).toMatchObject({ value: 0 })
1074+
expect({ value: 0 }).toMatchObject(new Bar(0))
1075+
1076+
expect(getError(() => expect(new Foo(0)).toMatchObject(new Bar(1)))).toMatchInlineSnapshot(`
1077+
[
1078+
"expected Foo{ value: +0 } to match object Bar{ value: 1 }",
1079+
"- Expected
1080+
+ Received
1081+
1082+
- Bar {
1083+
- "value": 1,
1084+
+ Foo {
1085+
+ "value": 0,
1086+
}",
1087+
]
1088+
`)
1089+
1090+
expect(getError(() => expect(new Foo(0)).toMatchObject({ value: 1 }))).toMatchInlineSnapshot(`
1091+
[
1092+
"expected Foo{ value: +0 } to match object { value: 1 }",
1093+
"- Expected
1094+
+ Received
1095+
1096+
- Object {
1097+
- "value": 1,
1098+
+ Foo {
1099+
+ "value": 0,
1100+
}",
1101+
]
1102+
`)
1103+
1104+
expect(getError(() => expect({ value: 0 }).toMatchObject(new Bar(1)))).toMatchInlineSnapshot(`
1105+
[
1106+
"expected { value: +0 } to match object Bar{ value: 1 }",
1107+
"- Expected
1108+
+ Received
1109+
1110+
- Bar {
1111+
- "value": 1,
1112+
+ Object {
1113+
+ "value": 0,
1114+
}",
1115+
]
1116+
`)
1117+
1118+
expect(getError(() =>
1119+
expect({
1120+
bad: new Foo(1),
1121+
good: new Foo(0),
1122+
}).toMatchObject({
1123+
bad: new Bar(2),
1124+
good: new Bar(0),
1125+
}),
1126+
)).toMatchInlineSnapshot(`
1127+
[
1128+
"expected { bad: Foo{ value: 1 }, …(1) } to match object { bad: Bar{ value: 2 }, …(1) }",
1129+
"- Expected
1130+
+ Received
1131+
1132+
Object {
1133+
- "bad": Bar {
1134+
- "value": 2,
1135+
+ "bad": Foo {
1136+
+ "value": 1,
1137+
},
1138+
"good": Bar {
1139+
"value": 0,
1140+
},
1141+
}",
1142+
]
1143+
`)
1144+
1145+
expect(getError(() =>
1146+
expect(new Foo(new Foo(1))).toMatchObject(new Bar(new Bar(0))),
1147+
)).toMatchInlineSnapshot(`
1148+
[
1149+
"expected Foo{ value: Foo{ value: 1 } } to match object Bar{ value: Bar{ value: +0 } }",
1150+
"- Expected
1151+
+ Received
1152+
1153+
- Bar {
1154+
- "value": Bar {
1155+
- "value": 0,
1156+
+ Foo {
1157+
+ "value": Foo {
1158+
+ "value": 1,
1159+
},
1160+
}",
1161+
]
1162+
`)
1163+
1164+
expect(new Foo(new Foo(1))).toMatchObject(new Bar(new Foo(1)))
1165+
expect(getError(() =>
1166+
expect(new Foo(new Foo(1))).toMatchObject(new Bar(new Foo(2))),
1167+
)).toMatchInlineSnapshot(`
1168+
[
1169+
"expected Foo{ value: Foo{ value: 1 } } to match object Bar{ value: Foo{ value: 2 } }",
1170+
"- Expected
1171+
+ Received
1172+
1173+
- Bar {
1174+
+ Foo {
1175+
"value": Foo {
1176+
- "value": 2,
1177+
+ "value": 1,
1178+
},
1179+
}",
1180+
]
1181+
`)
10621182
})
10631183

10641184
it('toHaveProperty error diff', () => {

0 commit comments

Comments
 (0)
Please sign in to comment.