Skip to content

Commit e649d78

Browse files
authored
fix: print value shape when .resolves and .rejects fails (#4137)
1 parent a084cea commit e649d78

File tree

3 files changed

+66
-28
lines changed

3 files changed

+66
-28
lines changed

examples/mocks/test/factory.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ describe('mocking with factory', () => {
7575
expect((example as any).then).toBe('a then export')
7676
expect((example as any).mocked).toBe(true)
7777
expect(example.square(2, 3)).toBe(5)
78-
expect(example.asyncSquare(2, 3)).resolves.toBe(5)
78+
await expect(example.asyncSquare(2, 3)).resolves.toBe(5)
7979
})
8080

8181
test('successfully with actual', () => {

packages/expect/src/jest-expect.ts

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { AssertionError } from 'chai'
21
import { assertTypes, getColors } from '@vitest/utils'
32
import type { Constructable } from '@vitest/utils'
43
import type { EnhancedSpy } from '@vitest/spy'
@@ -13,6 +12,7 @@ import { recordAsyncExpect, wrapSoft } from './utils'
1312

1413
// Jest Expect Compact
1514
export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
15+
const { AssertionError } = chai
1616
const c = () => getColors()
1717

1818
function def(name: keyof Assertion | (keyof Assertion)[], fn: ((this: Chai.AssertionStatic & Assertion, ...args: any[]) => any)) {
@@ -436,19 +436,16 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
436436
if (called && isNot)
437437
msg = formatCalls(spy, msg)
438438

439-
if ((called && isNot) || (!called && !isNot)) {
440-
const err = new Error(msg)
441-
err.name = 'AssertionError'
442-
throw err
443-
}
439+
if ((called && isNot) || (!called && !isNot))
440+
throw new AssertionError(msg)
444441
})
445442
def(['toHaveBeenCalledWith', 'toBeCalledWith'], function (...args) {
446443
const spy = getSpy(this)
447444
const spyName = spy.getMockName()
448445
const pass = spy.mock.calls.some(callArg => jestEquals(callArg, args, [iterableEquality]))
449446
const isNot = utils.flag(this, 'negate') as boolean
450447

451-
let msg = utils.getMessage(
448+
const msg = utils.getMessage(
452449
this,
453450
[
454451
pass,
@@ -458,12 +455,8 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
458455
],
459456
)
460457

461-
if ((pass && isNot) || (!pass && !isNot)) {
462-
msg = formatCalls(spy, msg, args)
463-
const err = new Error(msg)
464-
err.name = 'AssertionError'
465-
throw err
466-
}
458+
if ((pass && isNot) || (!pass && !isNot))
459+
throw new AssertionError(formatCalls(spy, msg, args))
467460
})
468461
def(['toHaveBeenNthCalledWith', 'nthCalledWith'], function (times: number, ...args: any[]) {
469462
const spy = getSpy(this)
@@ -595,7 +588,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
595588
const pass = spy.mock.results.some(({ type, value: result }) => type === 'return' && jestEquals(value, result))
596589
const isNot = utils.flag(this, 'negate') as boolean
597590

598-
let msg = utils.getMessage(
591+
const msg = utils.getMessage(
599592
this,
600593
[
601594
pass,
@@ -605,12 +598,8 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
605598
],
606599
)
607600

608-
if ((pass && isNot) || (!pass && !isNot)) {
609-
msg = formatReturns(spy, msg, value)
610-
const err = new Error(msg)
611-
err.name = 'AssertionError'
612-
throw err
613-
}
601+
if ((pass && isNot) || (!pass && !isNot))
602+
throw new AssertionError(formatReturns(spy, msg, value))
614603
})
615604
def(['toHaveLastReturnedWith', 'lastReturnedWith'], function (value: any) {
616605
const spy = getSpy(this)
@@ -650,8 +639,9 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
650639
})
651640

652641
utils.addProperty(chai.Assertion.prototype, 'resolves', function __VITEST_RESOLVES__(this: any) {
642+
const error = new Error('resolves')
653643
utils.flag(this, 'promise', 'resolves')
654-
utils.flag(this, 'error', new Error('resolves'))
644+
utils.flag(this, 'error', error)
655645
const test: Test = utils.flag(this, 'vitest-test')
656646
const obj = utils.flag(this, 'object')
657647

@@ -672,7 +662,12 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
672662
return result.call(this, ...args)
673663
},
674664
(err: any) => {
675-
throw new Error(`promise rejected "${String(err)}" instead of resolving`)
665+
const _error = new AssertionError(
666+
`promise rejected "${utils.inspect(err)}" instead of resolving`,
667+
{ showDiff: false },
668+
)
669+
_error.stack = (error.stack as string).replace(error.message, _error.message)
670+
throw _error
676671
},
677672
)
678673

@@ -685,8 +680,9 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
685680
})
686681

687682
utils.addProperty(chai.Assertion.prototype, 'rejects', function __VITEST_REJECTS__(this: any) {
683+
const error = new Error('rejects')
688684
utils.flag(this, 'promise', 'rejects')
689-
utils.flag(this, 'error', new Error('rejects'))
685+
utils.flag(this, 'error', error)
690686
const test: Test = utils.flag(this, 'vitest-test')
691687
const obj = utils.flag(this, 'object')
692688
const wrapper = typeof obj === 'function' ? obj() : obj // for jest compat
@@ -704,7 +700,12 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
704700
return async (...args: any[]) => {
705701
const promise = wrapper.then(
706702
(value: any) => {
707-
throw new Error(`promise resolved "${String(value)}" instead of rejecting`)
703+
const _error = new AssertionError(
704+
`promise resolved "${utils.inspect(value)}" instead of rejecting`,
705+
{ showDiff: false },
706+
)
707+
_error.stack = (error.stack as string).replace(error.message, _error.message)
708+
throw _error
708709
},
709710
(err: any) => {
710711
utils.flag(this, 'object', err)

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

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
/* eslint-disable no-sparse-arrays */
22
import { AssertionError } from 'node:assert'
3+
import { fileURLToPath } from 'node:url'
4+
import { resolve } from 'node:path'
35
import { describe, expect, it, vi } from 'vitest'
46
import { generateToBeMessage, setupColors } from '@vitest/expect'
57
import { processError } from '@vitest/utils/error'
@@ -604,6 +606,7 @@ describe('async expect', () => {
604606

605607
try {
606608
expect(1).resolves.toEqual(2)
609+
expect.unreachable()
607610
}
608611
catch (error) {
609612
expect(error).toEqual(expectedError)
@@ -658,13 +661,15 @@ describe('async expect', () => {
658661

659662
try {
660663
expect(1).rejects.toEqual(2)
664+
expect.unreachable()
661665
}
662666
catch (error) {
663667
expect(error).toEqual(expectedError)
664668
}
665669

666670
try {
667671
expect(() => 1).rejects.toEqual(2)
672+
expect.unreachable()
668673
}
669674
catch (error) {
670675
expect(error).toEqual(expectedError)
@@ -686,6 +691,7 @@ describe('async expect', () => {
686691
const toStrictEqualError1 = generatedToBeMessage('toStrictEqual', '{ key: \'value\' }', '{ key: \'value\' }')
687692
try {
688693
expect(actual).toBe({ ...actual })
694+
expect.unreachable()
689695
}
690696
catch (error: any) {
691697
expect(error.message).toBe(toStrictEqualError1.message)
@@ -694,6 +700,7 @@ describe('async expect', () => {
694700
const toStrictEqualError2 = generatedToBeMessage('toStrictEqual', 'FakeClass{}', 'FakeClass{}')
695701
try {
696702
expect(new FakeClass()).toBe(new FakeClass())
703+
expect.unreachable()
697704
}
698705
catch (error: any) {
699706
expect(error.message).toBe(toStrictEqualError2.message)
@@ -702,15 +709,16 @@ describe('async expect', () => {
702709
const toEqualError1 = generatedToBeMessage('toEqual', '{}', 'FakeClass{}')
703710
try {
704711
expect({}).toBe(new FakeClass())
712+
expect.unreachable()
705713
}
706714
catch (error: any) {
707715
expect(error.message).toBe(toEqualError1.message)
708-
// expect(error).toEqual('1234')
709716
}
710717

711718
const toEqualError2 = generatedToBeMessage('toEqual', 'FakeClass{}', '{}')
712719
try {
713720
expect(new FakeClass()).toBe({})
721+
expect.unreachable()
714722
}
715723
catch (error: any) {
716724
expect(error.message).toBe(toEqualError2.message)
@@ -742,22 +750,51 @@ describe('async expect', () => {
742750
})
743751
})
744752

753+
it('printing error message', async () => {
754+
const root = resolve(fileURLToPath(import.meta.url), '../../../../')
755+
// have "\" on windows, and "/" on unix
756+
const filename = fileURLToPath(import.meta.url).replace(root, '<root>')
757+
try {
758+
await expect(Promise.resolve({ foo: { bar: 42 } })).rejects.toThrow()
759+
expect.unreachable()
760+
}
761+
catch (err: any) {
762+
const stack = err.stack.replace(new RegExp(root, 'g'), '<root>')
763+
expect(err.message).toMatchInlineSnapshot('"promise resolved \\"{ foo: { bar: 42 } }\\" instead of rejecting"')
764+
expect(stack).toContain(`at ${filename}`)
765+
}
766+
767+
try {
768+
const error = new Error('some error')
769+
Object.assign(error, { foo: { bar: 42 } })
770+
await expect(Promise.reject(error)).resolves.toBe(1)
771+
expect.unreachable()
772+
}
773+
catch (err: any) {
774+
const stack = err.stack.replace(new RegExp(root, 'g'), '<root>')
775+
expect(err.message).toMatchInlineSnapshot('"promise rejected \\"Error: some error { foo: { bar: 42 } }\\" instead of resolving"')
776+
expect(stack).toContain(`at ${filename}`)
777+
}
778+
})
779+
745780
it('handle thenable objects', async () => {
746781
await expect({ then: (resolve: any) => resolve(0) }).resolves.toBe(0)
747782
await expect({ then: (_: any, reject: any) => reject(0) }).rejects.toBe(0)
748783

749784
try {
750785
await expect({ then: (resolve: any) => resolve(0) }).rejects.toBe(0)
786+
expect.unreachable()
751787
}
752788
catch (error) {
753-
expect(error).toEqual(new Error('promise resolved "0" instead of rejecting'))
789+
expect(error).toEqual(new Error('promise resolved "+0" instead of rejecting'))
754790
}
755791

756792
try {
757793
await expect({ then: (_: any, reject: any) => reject(0) }).resolves.toBe(0)
794+
expect.unreachable()
758795
}
759796
catch (error) {
760-
expect(error).toEqual(new Error('promise rejected "0" instead of resolving'))
797+
expect(error).toEqual(new Error('promise rejected "+0" instead of resolving'))
761798
}
762799
})
763800
})

0 commit comments

Comments
 (0)