diff --git a/packages/expect/src/jest-expect.ts b/packages/expect/src/jest-expect.ts index 0c8fa719863f..0434c3705233 100644 --- a/packages/expect/src/jest-expect.ts +++ b/packages/expect/src/jest-expect.ts @@ -330,7 +330,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { if (Array.isArray(args[0])) args[0] = args[0].map(key => String(key).replace(/([.[\]])/g, '\\$1')).join('.') - const actual = this._obj + const actual = this._obj as any const [propertyName, expected] = args const getValue = () => { const hasOwn = Object.prototype.hasOwnProperty.call(actual, propertyName) @@ -347,7 +347,8 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { pass, `expected #{this} to have property "${propertyName}"${valueString}`, `expected #{this} to not have property "${propertyName}"${valueString}`, - actual, + expected, + exists ? value : undefined, ) }) def('toBeCloseTo', function (received: number, precision = 2) { diff --git a/test/core/test/jest-expect.test.ts b/test/core/test/jest-expect.test.ts index 4f78f9b8e1cd..492678750e72 100644 --- a/test/core/test/jest-expect.test.ts +++ b/test/core/test/jest-expect.test.ts @@ -903,4 +903,96 @@ it('correctly prints diff with asymmetric matchers', () => { } }) +it('toHaveProperty error diff', () => { + setupColors(getDefaultColors()) + + // make it easy for dev who trims trailing whitespace on IDE + function trim(s: string): string { + return s.replaceAll(/ *$/gm, '') + } + + function getError(f: () => unknown) { + try { + f() + return expect.unreachable() + } + catch (error) { + const processed = processError(error) + return [processed.message, trim(processed.diff)] + } + } + + // non match value + expect(getError(() => expect({ name: 'foo' }).toHaveProperty('name', 'bar'))).toMatchInlineSnapshot(` + [ + "expected { name: 'foo' } to have property "name" with value 'bar'", + "- Expected + + Received + + - bar + + foo", + ] + `) + + // non match key + expect(getError(() => expect({ noName: 'foo' }).toHaveProperty('name', 'bar'))).toMatchInlineSnapshot(` + [ + "expected { noName: 'foo' } to have property "name" with value 'bar'", + "- Expected: + "bar" + + + Received: + undefined", + ] + `) + + // non match value (with asymmetric matcher) + expect(getError(() => expect({ name: 'foo' }).toHaveProperty('name', expect.any(Number)))).toMatchInlineSnapshot(` + [ + "expected { name: 'foo' } to have property "name" with value Any{ …(3) }", + "- Expected: + Any + + + Received: + "foo"", + ] + `) + + // non match key (with asymmetric matcher) + expect(getError(() => expect({ noName: 'foo' }).toHaveProperty('name', expect.any(Number)))).toMatchInlineSnapshot(` + [ + "expected { noName: 'foo' } to have property "name" with value Any{ …(3) }", + "- Expected: + Any + + + Received: + undefined", + ] + `) + + // non match value (deep key) + expect(getError(() => expect({ parent: { name: 'foo' } }).toHaveProperty('parent.name', 'bar'))).toMatchInlineSnapshot(` + [ + "expected { parent: { name: 'foo' } } to have property "parent.name" with value 'bar'", + "- Expected + + Received + + - bar + + foo", + ] + `) + + // non match key (deep key) + expect(getError(() => expect({ parent: { noName: 'foo' } }).toHaveProperty('parent.name', 'bar'))).toMatchInlineSnapshot(` + [ + "expected { parent: { noName: 'foo' } } to have property "parent.name" with value 'bar'", + "- Expected: + "bar" + + + Received: + undefined", + ] + `) +}) + it('timeout', () => new Promise(resolve => setTimeout(resolve, 500)))