Skip to content

Commit 766624a

Browse files
feat: introduce the new reporter API (#7069)
Co-authored-by: Ari Perkkiö <ari.perkkio@gmail.com>
1 parent faca4de commit 766624a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+2467
-944
lines changed

docs/.vitepress/config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,10 @@ export default ({ mode }: { mode: string }) => {
339339
text: 'Runner API',
340340
link: '/advanced/runner',
341341
},
342+
{
343+
text: 'Reporters API',
344+
link: '/advanced/api/reporters',
345+
},
342346
{
343347
text: 'Task Metadata',
344348
link: '/advanced/metadata',

docs/advanced/api/reporters.md

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
# Reporters
2+
3+
::: warning
4+
This is an advanced API. If you just want to configure built-in reporters, read the ["Reporters"](/guide/reporters) guide.
5+
:::
6+
7+
Vitest has its own test run lifecycle. These are represented by reporter's methods:
8+
9+
- [`onInit`](#oninit)
10+
- [`onTestRunStart`](#ontestrunstart)
11+
- [`onTestModuleQueued`](#ontestmodulequeued)
12+
- [`onTestModuleCollected`](#ontestmodulecollected)
13+
- [`onTestModuleStart`](#ontestmodulestart)
14+
- [`onTestSuiteReady`](#ontestsuiteready)
15+
- [`onHookStart(beforeAll)`](#onhookstart)
16+
- [`onHookEnd(beforeAll)`](#onhookend)
17+
- [`onTestCaseReady`](#ontestcaseready)
18+
- [`onHookStart(beforeEach)`](#onhookstart)
19+
- [`onHookEnd(beforeEach)`](#onhookend)
20+
- [`onHookStart(afterEach)`](#onhookstart)
21+
- [`onHookEnd(afterEach)`](#onhookend)
22+
- [`onTestCaseResult`](#ontestcaseresult)
23+
- [`onHookStart(afterAll)`](#onhookstart)
24+
- [`onHookEnd(afterAll)`](#onhookend)
25+
- [`onTestSuiteResult`](#ontestsuiteresult)
26+
- [`onTestModuleEnd`](#ontestmoduleend)
27+
- [`onTestRunEnd`](#ontestrunend)
28+
29+
Tests and suites within a single module will be reported in order unless they were skipped. All skipped tests are reported at the end of suite/module.
30+
31+
Note that since test modules can run in parallel, Vitest will report them in parallel.
32+
33+
This guide lists all supported reporter methods. However, don't forget that instead of creating your own reporter, you can [extend existing one](/advanced/reporters) instead:
34+
35+
```ts [custom-reporter.js]
36+
import { BaseReporter } from 'vitest/reporters'
37+
38+
export default class CustomReporter extends BaseReporter {
39+
onTestRunEnd(testModules, errors) {
40+
console.log(testModule.length, 'tests finished running')
41+
super.onTestRunEnd(testModules, errors)
42+
}
43+
}
44+
```
45+
46+
## onInit
47+
48+
```ts
49+
function onInit(vitest: Vitest): Awaitable<void>
50+
```
51+
52+
This method is called when [Vitest](/advanced/api/vitest) was initiated or started, but before the tests were filtered.
53+
54+
::: info
55+
Internally this method is called inside [`vitest.start`](/advanced/api/vitest#start), [`vitest.init`](/advanced/api/vitest#init) or [`vitest.mergeReports`](/advanced/api/vitest#mergereports). If you are using programmatic API, make sure to call either one dependning on your needs before calling [`vitest.runTestSpecifications`](/advanced/api/vitest#runtestspecifications), for example. Built-in CLI will always run methods in correct order.
56+
:::
57+
58+
Note that you can also get access to `vitest` instance from test cases, suites and test modules via a [`project`](/advanced/api/test-project) property, but it might also be useful to store a reference to `vitest` in this method.
59+
60+
::: details Example
61+
```ts
62+
import type { Reporter, TestSpecification, Vitest } from 'vitest/node'
63+
64+
class MyReporter implements Reporter {
65+
private vitest!: Vitest
66+
67+
onInit(vitest: Vitest) {
68+
this.vitest = vitest
69+
}
70+
71+
onTestRunStart(specifications: TestSpecification[]) {
72+
console.log(
73+
specifications.length,
74+
'test files will run in',
75+
this.vitest.config.root,
76+
)
77+
}
78+
}
79+
80+
export default new MyReporter()
81+
```
82+
:::
83+
84+
## onTestRunStart
85+
86+
```ts
87+
function onTestRunStart(
88+
specifications: TestSpecification[]
89+
): Awaitable<void>
90+
```
91+
92+
This method is called when a new test run has started. It receives an array of [test specifications](/advanced/api/test-specification) scheduled to run. This array is readonly and available only for information purposes.
93+
94+
If Vitest didn't find any test files to run, this event will be invoked with an empty array, and then [`onTestRunEnd`](#ontestrunend) will be called immediately after.
95+
96+
::: details Example
97+
```ts
98+
import type { Reporter, TestSpecification } from 'vitest/node'
99+
100+
class MyReporter implements Reporter {
101+
onTestRunStart(specifications: TestSpecification[]) {
102+
console.log(specifications.length, 'test files will run')
103+
}
104+
}
105+
106+
export default new MyReporter()
107+
```
108+
:::
109+
110+
::: tip DEPRECATION NOTICE
111+
This method was added in Vitest 3, replacing `onPathsCollected` and `onSpecsCollected`, both of which are now deprecated.
112+
:::
113+
114+
## onTestRunEnd
115+
116+
```ts
117+
function onTestRunEnd(
118+
testModules: ReadonlyArray<TestModule>,
119+
unhandledErrors: ReadonlyArray<SerializedError>,
120+
reason: TestRunEndReason
121+
): Awaitable<void>
122+
```
123+
124+
This method is called after all tests have finished running and the coverage merged all reports, if it's enabled. Note that you can get the coverage information in [`onCoverage`](#oncoverage) hook.
125+
126+
It receives a readonly list of test modules. You can iterate over it via a [`testModule.children`](/advanced/api/test-collection) property to report the state and errors, if any.
127+
128+
The second argument is a readonly list of unhandled errors that Vitest wasn't able to attribute to any test. These can happen outside of the test run because of an error in a plugin, or inside the test run as a side-effect of a non-awaited function (for example, a timeout that threw an error after the test has finished running).
129+
130+
The third argument indicated why the test run was finished:
131+
132+
- `passed`: test run was finished normally and there are no errors
133+
- `failed`: test run has at least one error (due to a syntax error during collection or an actual error during test execution)
134+
- `interrupted`: test was interruped by [`vitest.cancelCurrentRun`](/advanced/api/vitest#cancelcurrentrun) call or `Ctrl+C` was pressed in the terminal (note that it's still possible to have failed tests in this case)
135+
136+
If Vitest didn't find any test files to run, this event will be invoked with empty arrays of modules and errors, and the state will depend on the value of [`config.passWithNoTests`](/config/#passwithnotests).
137+
138+
::: details Example
139+
```ts
140+
import type {
141+
Reporter,
142+
SerializedError,
143+
TestModule,
144+
TestRunEndReason,
145+
TestSpecification
146+
} from 'vitest/node'
147+
148+
class MyReporter implements Reporter {
149+
onTestRunEnd(
150+
testModules: ReadonlyArray<TestModule>,
151+
unhandledErrors: ReadonlyArray<SerializedError>,
152+
reason: TestRunEndReason,
153+
) {
154+
if (reason === 'passed') {
155+
testModules.forEach(module => console.log(module.moduleId, 'succeeded'))
156+
}
157+
else if (reason === 'failed') {
158+
// note that this will skip possible errors in suites
159+
// you can get them from testSuite.errors()
160+
for (const testCase of testModules.children.allTests()) {
161+
if (testCase.result().state === 'failed') {
162+
console.log(testCase.fullName, 'in', testCase.module.moduleId, 'failed')
163+
console.log(testCase.result().errors)
164+
}
165+
}
166+
}
167+
else {
168+
console.log('test run was interrupted, skipping report')
169+
}
170+
}
171+
}
172+
173+
export default new MyReporter()
174+
```
175+
:::
176+
177+
::: tip DEPRECATION NOTICE
178+
This method was added in Vitest 3, replacing `onFinished`, which is now deprecated.
179+
:::
180+
181+
## onCoverage
182+
183+
```ts
184+
function onCoverage(coverage: unknown): Awaitable<void>
185+
```
186+
187+
This hook is called after coverage results have been processed. Coverage provider's reporters are called after this hook. The typings of `coverage` depends on the `coverage.provider`. For Vitest's default built-in providers you can import the types from `istanbul-lib-coverage` package:
188+
189+
```ts
190+
import type { CoverageMap } from 'istanbul-lib-coverage'
191+
192+
declare function onCoverage(coverage: CoverageMap): Awaitable<void>
193+
```
194+
195+
If Vitest didn't perform any coverage, this hook is not called.
196+
197+
## onTestModuleQueued
198+
199+
```ts
200+
function onTestModuleQueued(testModule: TestModule): Awaitable<void>
201+
```
202+
203+
This method is called right before Vitest imports the setup file and the test module itself. This means that `testModule` will have no [`children`](/advanced/api/test-suite#children) yet, but you can start reporting it as the next test to run.
204+
205+
## onTestModuleCollected
206+
207+
```ts
208+
function onTestModuleCollected(testModule: TestModule): Awaitable<void>
209+
```
210+
211+
This method is called when all tests inside the file were collected, meaning [`testModule.children`](/advanced/api/test-suite#children) collection is populated, but tests don't have any results yet.
212+
213+
## onTestModuleStart
214+
215+
```ts
216+
function onTestModuleStart(testModule: TestModule): Awaitable<void>
217+
```
218+
219+
This method is called right after [`onTestModuleCollected`](#ontestmodulecollected) unless Vitest runs in collection mode ([`vitest.collect()`](/advanced/api/vitest#collect) or `vitest collect` in the CLI), in this case it will not be called at all because there are no tests to run.
220+
221+
## onTestModuleEnd
222+
223+
```ts
224+
function onTestModuleEnd(testModule: TestModule): Awaitable<void>
225+
```
226+
227+
This method is called when every test in the module finished running. This means, every test inside [`testModule.children`](/advanced/api/test-suite#children) will have a `test.result()` that is not equal to `pending`.
228+
229+
## onHookStart
230+
231+
```ts
232+
function onHookStart(context: ReportedHookContext): Awaitable<void>
233+
```
234+
235+
This method is called when any of these hooks have started running:
236+
237+
- `beforeAll`
238+
- `afterAll`
239+
- `beforeEach`
240+
- `afterEach`
241+
242+
If `beforeAll` or `afterAll` are started, the `entity` will be either [`TestSuite`](/advanced/api/test-suite) or [`TestModule`](/advanced/api/test-module).
243+
244+
If `beforeEach` or `afterEach` are started, the `entity` will always be [`TestCase`](/advanced/api/test-case).
245+
246+
::: warning
247+
`onHookStart` method will not be called if the hook did not run during the test run.
248+
:::
249+
250+
## onHookEnd
251+
252+
```ts
253+
function onHookEnd(context: ReportedHookContext): Awaitable<void>
254+
```
255+
256+
This method is called when any of these hooks have finished running:
257+
258+
- `beforeAll`
259+
- `afterAll`
260+
- `beforeEach`
261+
- `afterEach`
262+
263+
If `beforeAll` or `afterAll` have finished, the `entity` will be either [`TestSuite`](/advanced/api/test-suite) or [`TestModule`](/advanced/api/test-module).
264+
265+
If `beforeEach` or `afterEach` have finished, the `entity` will always be [`TestCase`](/advanced/api/test-case).
266+
267+
::: warning
268+
`onHookEnd` method will not be called if the hook did not run during the test run.
269+
:::
270+
271+
## onTestSuiteReady
272+
273+
```ts
274+
function onTestSuiteReady(testSuite: TestSuite): Awaitable<void>
275+
```
276+
277+
This method is called before the suite starts to run its tests. This method is also called if the suite was skipped.
278+
279+
If the file doesn't have any suites, this method will not be called. Consider using `onTestModuleStart` to cover this use case.
280+
281+
## onTestSuiteResult
282+
283+
```ts
284+
function onTestSuiteResult(testSuite: TestSuite): Awaitable<void>
285+
```
286+
287+
This method is called after the suite has finished running tests. This method is also called if the suite was skipped.
288+
289+
If the file doesn't have any suites, this method will not be called. Consider using `onTestModuleEnd` to cover this use case.
290+
291+
## onTestCaseReady
292+
293+
```ts
294+
function onTestCaseReady(testCase: TestCase): Awaitable<void>
295+
```
296+
297+
This method is called before the test starts to run or it was skipped. Note that `beforeEach` and `afterEach` hooks are considered part of the test because they can influence the result.
298+
299+
::: warning
300+
Notice that it's possible to have [`testCase.result()`](/advanced/api/test-case#result) with `passed` or `failed` state already when `onTestCaseReady` is called. This can happen if test was running too fast and both `onTestCaseReady` and `onTestCaseResult` were scheduled to run in the same microtask.
301+
:::
302+
303+
## onTestCaseResult
304+
305+
```ts
306+
function onTestCaseResult(testCase: TestCase): Awaitable<void>
307+
```
308+
309+
This method is called when the test has finished running or was just skipped. Note that this will be called after the `afterEach` hook is finished, if there are any.
310+
311+
At this point, [`testCase.result()`](/advanced/api/test-case#result) will have non-pending state.

0 commit comments

Comments
 (0)