|
| 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