Skip to content

Commit 6869969

Browse files
authored
fix(expect): handle async errors in expect.soft (#8145)
1 parent 790bc31 commit 6869969

File tree

4 files changed

+43
-10
lines changed

4 files changed

+43
-10
lines changed

packages/expect/src/jest-extend.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,16 +80,17 @@ function JestExtendPlugin(
8080
if (
8181
result
8282
&& typeof result === 'object'
83-
&& result instanceof Promise
83+
&& typeof (result as any).then === 'function'
8484
) {
85-
return result.then(({ pass, message, actual, expected }) => {
85+
const thenable = result as PromiseLike<SyncExpectationResult>
86+
return thenable.then(({ pass, message, actual, expected }) => {
8687
if ((pass && isNot) || (!pass && !isNot)) {
8788
throw new JestExtendError(message(), actual, expected)
8889
}
8990
})
9091
}
9192

92-
const { pass, message, actual, expected } = result
93+
const { pass, message, actual, expected } = result as SyncExpectationResult
9394

9495
if ((pass && isNot) || (!pass && !isNot)) {
9596
throw new JestExtendError(message(), actual, expected)

packages/expect/src/utils.ts

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { Test } from '@vitest/runner/types'
22
import type { Assertion } from './types'
3+
import { noop } from '@vitest/utils'
34
import { processError } from '@vitest/utils/error'
45

56
export function createAssertionMessage(
@@ -73,12 +74,19 @@ export function recordAsyncExpect(
7374
return promise
7475
}
7576

77+
function handleTestError(test: Test, err: unknown) {
78+
test.result ||= { state: 'fail' }
79+
test.result.state = 'fail'
80+
test.result.errors ||= []
81+
test.result.errors.push(processError(err))
82+
}
83+
7684
export function wrapAssertion(
7785
utils: Chai.ChaiUtils,
7886
name: string,
79-
fn: (this: Chai.AssertionStatic & Assertion, ...args: any[]) => void,
87+
fn: (this: Chai.AssertionStatic & Assertion, ...args: any[]) => void | PromiseLike<void>,
8088
) {
81-
return function (this: Chai.AssertionStatic & Assertion, ...args: any[]): void {
89+
return function (this: Chai.AssertionStatic & Assertion, ...args: any[]): void | PromiseLike<void> {
8290
// private
8391
if (name !== 'withTest') {
8492
utils.flag(this, '_name', name)
@@ -95,13 +103,18 @@ export function wrapAssertion(
95103
}
96104

97105
try {
98-
return fn.apply(this, args)
106+
const result = fn.apply(this, args)
107+
108+
if (result && typeof result === 'object' && typeof result.then === 'function') {
109+
return result.then(noop, (err) => {
110+
handleTestError(test, err)
111+
})
112+
}
113+
114+
return result
99115
}
100116
catch (err) {
101-
test.result ||= { state: 'fail' }
102-
test.result.state = 'fail'
103-
test.result.errors ||= []
104-
test.result.errors.push(processError(err))
117+
handleTestError(test, err)
105118
}
106119
}
107120
}

test/cli/fixtures/expect-soft/expects/soft.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { expect, test } from 'vitest'
22

33
interface CustomMatchers<R = unknown> {
4+
toBeAsync: (expected: unknown) => Promise<R>;
45
toBeDividedBy(divisor: number): R
56
}
67

@@ -9,6 +10,12 @@ declare module 'vitest' {
910
}
1011

1112
expect.extend({
13+
toBeAsync: async function (received, expected) {
14+
return {
15+
pass: received === expected,
16+
message: () => `expected ${received} to be ${expected} (asynchronously)`,
17+
};
18+
},
1219
toBeDividedBy(received, divisor) {
1320
const pass = received % divisor === 0
1421
if (pass) {
@@ -62,6 +69,12 @@ test('with expect.extend', () => {
6269
expect(5).toEqual(6)
6370
})
6471

72+
test('promise with expect.extend', async () => {
73+
await expect.soft(1 + 1).toBeAsync(3);
74+
await expect.soft(1 + 2).toBeAsync(3);
75+
await expect.soft(2 + 2).toBeAsync(3);
76+
});
77+
6578
test('passed', () => {
6679
expect.soft(1).toEqual(1)
6780
expect(10).toEqual(10)

test/cli/test/expect-soft.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ describe('expect.soft', () => {
4040
expect.soft(stderr).toContain('AssertionError: expected 5 to deeply equal 6')
4141
})
4242

43+
test('promise with expect.extend', async () => {
44+
const { stderr } = await run()
45+
expect.soft(stderr).toContain('Error: expected 2 to be 3')
46+
expect.soft(stderr).toContain('Error: expected 4 to be 3')
47+
})
48+
4349
test('passed', async () => {
4450
const { stdout } = await run()
4551
expect.soft(stdout).toContain('soft.test.ts > passed')

0 commit comments

Comments
 (0)