Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(expect): preserve prototype in toMatchObject diff #6620

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/expect/src/jest-expect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
const { subset: actualSubset, stripped } = getObjectSubset(
actual,
expected,
customTesters,
)
if ((pass && isNot) || (!pass && !isNot)) {
const msg = utils.getMessage(this, [
Expand Down
12 changes: 10 additions & 2 deletions packages/expect/src/jest-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -676,7 +676,7 @@ export function getObjectKeys(object: object): Array<string | symbol> {
export function getObjectSubset(
object: any,
subset: any,
customTesters: Array<Tester> = [],
customTesters: Array<Tester>,
): { subset: any; stripped: number } {
let stripped = 0

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

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

// preserve constructor for toMatchObject diff
if (typeof object.constructor === 'function' && typeof object.constructor.name === 'string') {
Object.defineProperty(trimmed, 'constructor', {
enumerable: false,
value: object.constructor,
})
}

for (const key of getObjectKeys(object)) {
if (hasPropertyInObject(subset, key)) {
trimmed[key] = seenReferences.has(object[key])
Expand Down
122 changes: 121 additions & 1 deletion test/core/test/jest-expect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -927,12 +927,12 @@ function trim(s: string): string {
function getError(f: () => unknown) {
try {
f()
return expect.unreachable()
}
catch (error) {
const processed = processError(error)
return [stripVTControlCharacters(processed.message), stripVTControlCharacters(trim(processed.diff))]
}
return expect.unreachable()
}

it('toMatchObject error diff', () => {
Expand Down Expand Up @@ -1059,6 +1059,126 @@ it('toMatchObject error diff', () => {
}",
]
`)

// https://github.com/vitest-dev/vitest/issues/6543
class Foo {
constructor(public value: any) {}
}

class Bar {
constructor(public value: any) {}
}

expect(new Foo(0)).toMatchObject(new Bar(0))
expect(new Foo(0)).toMatchObject({ value: 0 })
expect({ value: 0 }).toMatchObject(new Bar(0))

expect(getError(() => expect(new Foo(0)).toMatchObject(new Bar(1)))).toMatchInlineSnapshot(`
[
"expected Foo{ value: +0 } to match object Bar{ value: 1 }",
"- Expected
+ Received

- Bar {
- "value": 1,
+ Foo {
+ "value": 0,
}",
]
`)

expect(getError(() => expect(new Foo(0)).toMatchObject({ value: 1 }))).toMatchInlineSnapshot(`
[
"expected Foo{ value: +0 } to match object { value: 1 }",
"- Expected
+ Received

- Object {
- "value": 1,
+ Foo {
+ "value": 0,
}",
]
`)

expect(getError(() => expect({ value: 0 }).toMatchObject(new Bar(1)))).toMatchInlineSnapshot(`
[
"expected { value: +0 } to match object Bar{ value: 1 }",
"- Expected
+ Received

- Bar {
- "value": 1,
+ Object {
+ "value": 0,
}",
]
`)

expect(getError(() =>
expect({
bad: new Foo(1),
good: new Foo(0),
}).toMatchObject({
bad: new Bar(2),
good: new Bar(0),
}),
)).toMatchInlineSnapshot(`
[
"expected { bad: Foo{ value: 1 }, …(1) } to match object { bad: Bar{ value: 2 }, …(1) }",
"- Expected
+ Received

Object {
- "bad": Bar {
- "value": 2,
+ "bad": Foo {
+ "value": 1,
},
"good": Bar {
"value": 0,
},
}",
]
`)

expect(getError(() =>
expect(new Foo(new Foo(1))).toMatchObject(new Bar(new Bar(0))),
)).toMatchInlineSnapshot(`
[
"expected Foo{ value: Foo{ value: 1 } } to match object Bar{ value: Bar{ value: +0 } }",
"- Expected
+ Received

- Bar {
- "value": Bar {
- "value": 0,
+ Foo {
+ "value": Foo {
+ "value": 1,
},
}",
]
`)

expect(new Foo(new Foo(1))).toMatchObject(new Bar(new Foo(1)))
expect(getError(() =>
expect(new Foo(new Foo(1))).toMatchObject(new Bar(new Foo(2))),
)).toMatchInlineSnapshot(`
[
"expected Foo{ value: Foo{ value: 1 } } to match object Bar{ value: Foo{ value: 2 } }",
"- Expected
+ Received

- Bar {
+ Foo {
"value": Foo {
- "value": 2,
+ "value": 1,
},
}",
]
`)
})

it('toHaveProperty error diff', () => {
Expand Down
Loading