Skip to content

Commit 031f02a

Browse files
authored
fix: allow catch/finally for async assertion (#9827)
1 parent 3e9e096 commit 031f02a

File tree

6 files changed

+135
-1
lines changed

6 files changed

+135
-1
lines changed

packages/browser/src/client/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,11 @@ export function ensureAwaited<T>(promise: (error?: Error) => Promise<T>): Promis
5454
return (promiseResult ||= promise(sourceError)).then(onFulfilled, onRejected)
5555
},
5656
catch(onRejected) {
57+
awaited = true
5758
return (promiseResult ||= promise(sourceError)).catch(onRejected)
5859
},
5960
finally(onFinally) {
61+
awaited = true
6062
return (promiseResult ||= promise(sourceError)).finally(onFinally)
6163
},
6264
[Symbol.toStringTag]: 'Promise',

packages/expect/src/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,11 @@ export function recordAsyncExpect(
7171
return promise.then(onFulfilled, onRejected)
7272
},
7373
catch(onRejected) {
74+
resolved = true
7475
return promise.catch(onRejected)
7576
},
7677
finally(onFinally) {
78+
resolved = true
7779
return promise.finally(onFinally)
7880
},
7981
[Symbol.toStringTag]: 'Promise',

packages/vitest/src/integrations/chai/poll.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,9 +152,11 @@ export function createExpectPoll(expect: ExpectStatic): ExpectStatic['poll'] {
152152
return (resultPromise ||= promise()).then(onFulfilled, onRejected)
153153
},
154154
catch(onRejected) {
155+
awaited = true
155156
return (resultPromise ||= promise()).catch(onRejected)
156157
},
157158
finally(onFinally) {
159+
awaited = true
158160
return (resultPromise ||= promise()).finally(onFinally)
159161
},
160162
[Symbol.toStringTag]: 'Promise',

test/browser/fixtures/failing/failing.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,15 @@ it('several locator methods are not awaited', () => {
3838
it('correctly prints error from a bundled file', () => {
3939
index()
4040
})
41+
42+
it('not awaited but with then/catch/finally', async () => {
43+
await page.getByRole('button').click().then()
44+
await page.getByRole('button').click().catch()
45+
await page.getByRole('button').click().finally()
46+
await userEvent.click(page.getByRole('button')).then()
47+
await userEvent.click(page.getByRole('button')).catch()
48+
await userEvent.click(page.getByRole('button')).finally()
49+
page.getByRole('button').click().then()
50+
page.getByRole('button').click().catch()
51+
page.getByRole('button').click().finally()
52+
})

test/browser/specs/runner.test.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ error with a stack
209209
})
210210

211211
test(`stack trace points to correct file in every browser when failed`, async () => {
212-
expect.assertions(29)
212+
expect.assertions(30)
213213
const { stderr } = await runBrowserTests({
214214
root: './fixtures/failing',
215215
reporters: [
@@ -273,6 +273,9 @@ test(`stack trace points to correct file in every browser when failed`, async ()
273273

274274
// index() is called from a bundled file
275275
expect(stderr).toMatch(/failing.test.ts:39:(2|8)/)
276+
277+
// "not awaited but with then/catch/finally" test should not produce warnings
278+
expect(stderr).not.toMatch(/failing.test.ts:4[3-8]/)
276279
})
277280

278281
test('user-event', async () => {

test/cli/test/fails.test.ts

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,119 @@ it('prints a warning if the assertion is not awaited', async () => {
117117
`)
118118
})
119119

120+
it('no async tracking after then/catch/finally', async () => {
121+
// resolve + then/catch/finally
122+
// rejects + then/catch/finally
123+
// expect.poll + then/catch/finally
124+
const { stderr, errorTree } = await runInlineTests({
125+
'await.test.js': ts`
126+
import { expect, test } from 'vitest';
127+
128+
test('resolves + then', async () => {
129+
await expect(Promise.resolve(1)).resolves.toBe(1).then()
130+
})
131+
132+
test('resolves + catch', async () => {
133+
await expect(Promise.resolve(1)).resolves.toBe(1).catch()
134+
})
135+
136+
test('resolves + finally', async () => {
137+
await expect(Promise.resolve(1)).resolves.toBe(1).finally()
138+
})
139+
140+
test('rejects + then', async () => {
141+
await expect(Promise.reject(1)).rejects.toBe(1).then()
142+
})
143+
144+
test('rejects + catch', async () => {
145+
await expect(Promise.reject(1)).rejects.toBe(1).catch()
146+
})
147+
148+
test('rejects + finally', async () => {
149+
await expect(Promise.reject(1)).rejects.toBe(1).finally()
150+
})
151+
152+
test('expect.poll + then', async () => {
153+
await expect.poll(() => 2).toBe(2).then()
154+
})
155+
156+
test('expect.poll + catch', async () => {
157+
await expect.poll(() => 2).toBe(2).catch()
158+
})
159+
160+
test('expect.poll + finally', async () => {
161+
await expect.poll(() => 2).toBe(2).finally()
162+
})
163+
`,
164+
'hang.test.js': ts`
165+
import { expect, test } from 'vitest';
166+
167+
test('resolves + then', async () => {
168+
expect(Promise.resolve(1)).resolves.toBe(1).then()
169+
})
170+
171+
test('resolves + catch', async () => {
172+
expect(Promise.resolve(1)).resolves.toBe(1).catch()
173+
})
174+
175+
test('resolves + finally', async () => {
176+
expect(Promise.resolve(1)).resolves.toBe(1).finally()
177+
})
178+
179+
test('rejects + then', async () => {
180+
expect(Promise.reject(1)).rejects.toBe(1).then()
181+
})
182+
183+
test('rejects + catch', async () => {
184+
expect(Promise.reject(1)).rejects.toBe(1).catch()
185+
})
186+
187+
test('rejects + finally', async () => {
188+
expect(Promise.reject(1)).rejects.toBe(1).finally()
189+
})
190+
191+
test('expect.poll + then', async () => {
192+
expect.poll(() => 2).toBe(2).then()
193+
})
194+
195+
test('expect.poll + catch', async () => {
196+
expect.poll(() => 2).toBe(2).catch()
197+
})
198+
199+
test('expect.poll + finally', async () => {
200+
expect.poll(() => 2).toBe(2).finally()
201+
})
202+
`,
203+
})
204+
expect(stderr).toMatchInlineSnapshot(`""`)
205+
expect(errorTree()).toMatchInlineSnapshot(`
206+
{
207+
"await.test.js": {
208+
"expect.poll + catch": "passed",
209+
"expect.poll + finally": "passed",
210+
"expect.poll + then": "passed",
211+
"rejects + catch": "passed",
212+
"rejects + finally": "passed",
213+
"rejects + then": "passed",
214+
"resolves + catch": "passed",
215+
"resolves + finally": "passed",
216+
"resolves + then": "passed",
217+
},
218+
"hang.test.js": {
219+
"expect.poll + catch": "passed",
220+
"expect.poll + finally": "passed",
221+
"expect.poll + then": "passed",
222+
"rejects + catch": "passed",
223+
"rejects + finally": "passed",
224+
"rejects + then": "passed",
225+
"resolves + catch": "passed",
226+
"resolves + finally": "passed",
227+
"resolves + then": "passed",
228+
},
229+
}
230+
`)
231+
})
232+
120233
it('prints a warning if the assertion is not awaited in the browser mode', async () => {
121234
const { stderr } = await runInlineTests({
122235
'base.test.js': ts`

0 commit comments

Comments
 (0)