Skip to content

Commit 82c2e24

Browse files
authoredDec 5, 2024··
feat!: pass down context to test hooks (#7034)
1 parent 407f10e commit 82c2e24

File tree

5 files changed

+41
-16
lines changed

5 files changed

+41
-16
lines changed
 

‎docs/api/index.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1126,7 +1126,7 @@ These hooks will throw an error if they are called outside of the test body.
11261126

11271127
### onTestFinished {#ontestfinished}
11281128

1129-
This hook is always called after the test has finished running. It is called after `afterEach` hooks since they can influence the test result. It receives a `TaskResult` object with the current test result.
1129+
This hook is always called after the test has finished running. It is called after `afterEach` hooks since they can influence the test result. It receives an `ExtendedContext` object like `beforeEach` and `afterEach`.
11301130

11311131
```ts {1,5}
11321132
import { onTestFinished, test } from 'vitest'
@@ -1183,7 +1183,7 @@ This hook is always called in reverse order and is not affected by [`sequence.ho
11831183

11841184
### onTestFailed
11851185

1186-
This hook is called only after the test has failed. It is called after `afterEach` hooks since they can influence the test result. It receives a `TaskResult` object with the current test result. This hook is useful for debugging.
1186+
This hook is called only after the test has failed. It is called after `afterEach` hooks since they can influence the test result. It receives an `ExtendedContext` object like `beforeEach` and `afterEach`. This hook is useful for debugging.
11871187

11881188
```ts {1,5-7}
11891189
import { onTestFailed, test } from 'vitest'

‎docs/guide/migration.md

+4
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ import {
4242

4343
If you are using `getCurrentSuite().custom()`, the `type` of the returned task is now is equal to `'test'`. The `Custom` type will be removed in Vitest 4.
4444

45+
### `onTestFinished` and `onTestFailed` Now Receive a Context
46+
47+
The [`onTestFinished`](/api/#ontestfinished) and [`onTestFailed`](/api/#ontestfailed) hooks previously received a test result as the first argument. Now, they receive a test context, like `beforeEach` and `afterEach`.
48+
4549
## Migrating to Vitest 2.0
4650

4751
### Default Pool is `forks`

‎packages/runner/src/context.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,18 @@ export function createTestContext<T extends Test>(
7171
throw new PendingError('test is skipped; abort execution', test, note)
7272
}
7373

74-
context.onTestFailed = (fn) => {
74+
context.onTestFailed = (handler, timeout) => {
7575
test.onFailed ||= []
76-
test.onFailed.push(fn)
76+
test.onFailed.push(
77+
withTimeout(handler, timeout ?? runner.config.hookTimeout, true),
78+
)
7779
}
7880

79-
context.onTestFinished = (fn) => {
81+
context.onTestFinished = (handler, timeout) => {
8082
test.onFinished ||= []
81-
test.onFinished.push(fn)
83+
test.onFinished.push(
84+
withTimeout(handler, timeout ?? runner.config.hookTimeout, true),
85+
)
8286
}
8387

8488
return (runner.extendTaskContext?.(context) as ExtendedContext<T>) || context

‎packages/runner/src/run.ts

+23-6
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { Awaitable } from '@vitest/utils'
22
import type { DiffOptions } from '@vitest/utils/diff'
33
import type { FileSpec, VitestRunner } from './types/runner'
44
import type {
5+
ExtendedContext,
56
File,
67
HookCleanupCallback,
78
HookListener,
@@ -62,32 +63,48 @@ function getSuiteHooks(
6263

6364
async function callTestHooks(
6465
runner: VitestRunner,
65-
task: Task,
66-
hooks: ((result: TaskResult) => Awaitable<void>)[],
66+
test: Test,
67+
hooks: ((context: ExtendedContext<Test>) => Awaitable<void>)[],
6768
sequence: SequenceHooks,
6869
) {
6970
if (sequence === 'stack') {
7071
hooks = hooks.slice().reverse()
7172
}
7273

74+
if (!hooks.length) {
75+
return
76+
}
77+
78+
const onTestFailed = test.context.onTestFailed
79+
const onTestFinished = test.context.onTestFinished
80+
test.context.onTestFailed = () => {
81+
throw new Error(`Cannot call "onTestFailed" inside a test hook.`)
82+
}
83+
test.context.onTestFinished = () => {
84+
throw new Error(`Cannot call "onTestFinished" inside a test hook.`)
85+
}
86+
7387
if (sequence === 'parallel') {
7488
try {
75-
await Promise.all(hooks.map(fn => fn(task.result!)))
89+
await Promise.all(hooks.map(fn => fn(test.context)))
7690
}
7791
catch (e) {
78-
failTask(task.result!, e, runner.config.diffOptions)
92+
failTask(test.result!, e, runner.config.diffOptions)
7993
}
8094
}
8195
else {
8296
for (const fn of hooks) {
8397
try {
84-
await fn(task.result!)
98+
await fn(test.context)
8599
}
86100
catch (e) {
87-
failTask(task.result!, e, runner.config.diffOptions)
101+
failTask(test.result!, e, runner.config.diffOptions)
88102
}
89103
}
90104
}
105+
106+
test.context.onTestFailed = onTestFailed
107+
test.context.onTestFinished = onTestFinished
91108
}
92109

93110
export async function callSuiteHook<T extends keyof SuiteHooks>(

‎packages/runner/src/types/tasks.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -608,12 +608,12 @@ export interface TaskContext<Task extends Test = Test> {
608608
/**
609609
* Extract hooks on test failed
610610
*/
611-
onTestFailed: (fn: OnTestFailedHandler) => void
611+
onTestFailed: (fn: OnTestFailedHandler, timeout?: number) => void
612612

613613
/**
614614
* Extract hooks on test failed
615615
*/
616-
onTestFinished: (fn: OnTestFinishedHandler) => void
616+
onTestFinished: (fn: OnTestFinishedHandler, timeout?: number) => void
617617

618618
/**
619619
* Mark tests as skipped. All execution after this call will be skipped.
@@ -625,8 +625,8 @@ export interface TaskContext<Task extends Test = Test> {
625625
export type ExtendedContext<T extends Test> = TaskContext<T> &
626626
TestContext
627627

628-
export type OnTestFailedHandler = (result: TaskResult) => Awaitable<void>
629-
export type OnTestFinishedHandler = (result: TaskResult) => Awaitable<void>
628+
export type OnTestFailedHandler = (context: ExtendedContext<Test>) => Awaitable<void>
629+
export type OnTestFinishedHandler = (context: ExtendedContext<Test>) => Awaitable<void>
630630

631631
export interface TaskHook<HookListener> {
632632
(fn: HookListener, timeout?: number): void

0 commit comments

Comments
 (0)
Please sign in to comment.