0
# Reporters
1
2
Vitest reporters format and output test results. Import from `vitest/reporters`.
3
4
## Built-in Reporters
5
6
11 built-in reporter classes are available:
7
8
### DefaultReporter
9
10
Default colored console output with test hierarchy.
11
12
```typescript { .api }
13
class DefaultReporter extends BaseReporter {
14
// Colored output with test results
15
// Shows: pass (✓), fail (✕), skip (-)
16
}
17
```
18
19
**Usage:**
20
21
```typescript
22
import { defineConfig } from 'vitest/config';
23
24
export default defineConfig({
25
test: {
26
reporters: ['default'] // or omit for default
27
}
28
});
29
```
30
31
### VerboseReporter
32
33
Extended test information with full output.
34
35
```typescript { .api }
36
class VerboseReporter extends BaseReporter {
37
// Detailed output including test names, durations, and full error stacks
38
}
39
```
40
41
**Usage:**
42
43
```typescript
44
export default defineConfig({
45
test: {
46
reporters: ['verbose']
47
}
48
});
49
```
50
51
### DotReporter
52
53
Minimal dot-based output.
54
55
```typescript { .api }
56
class DotReporter extends BaseReporter {
57
// Outputs: . (pass), x (fail), - (skip)
58
}
59
```
60
61
### TreeReporter
62
63
Tree-structured test hierarchy output.
64
65
```typescript { .api }
66
class TreeReporter extends BaseReporter {
67
// Tree view of test suites and tests
68
}
69
```
70
71
### JsonReporter
72
73
JSON-formatted test results.
74
75
```typescript { .api }
76
class JsonReporter extends BaseReporter {
77
// Outputs JSON test results to file
78
}
79
80
interface JsonTestResults {
81
numTotalTests: number;
82
numPassedTests: number;
83
numFailedTests: number;
84
numPendingTests: number;
85
testResults: JsonTestResult[];
86
}
87
88
interface JsonTestResult {
89
name: string;
90
status: 'passed' | 'failed' | 'skipped';
91
duration: number;
92
assertionResults: JsonAssertionResult[];
93
}
94
```
95
96
**Usage:**
97
98
```typescript
99
export default defineConfig({
100
test: {
101
reporters: ['json'],
102
outputFile: './test-results.json'
103
}
104
});
105
```
106
107
### JUnitReporter
108
109
JUnit XML format output.
110
111
```typescript { .api }
112
class JUnitReporter extends BaseReporter {
113
// Outputs JUnit XML format compatible with CI systems
114
}
115
```
116
117
**Usage:**
118
119
```typescript
120
export default defineConfig({
121
test: {
122
reporters: ['junit'],
123
outputFile: './junit.xml'
124
}
125
});
126
```
127
128
### TapReporter
129
130
TAP (Test Anything Protocol) format.
131
132
```typescript { .api }
133
class TapReporter extends BaseReporter {
134
// TAP format output
135
}
136
```
137
138
### TapFlatReporter
139
140
Flat TAP format output.
141
142
```typescript { .api }
143
class TapFlatReporter extends BaseReporter {
144
// Flattened TAP output
145
}
146
```
147
148
### GithubActionsReporter
149
150
GitHub Actions format for CI integration.
151
152
```typescript { .api }
153
class GithubActionsReporter extends BaseReporter {
154
// Formats errors for GitHub Actions annotations
155
}
156
```
157
158
**Usage:**
159
160
```typescript
161
export default defineConfig({
162
test: {
163
reporters: process.env.GITHUB_ACTIONS
164
? ['github-actions']
165
: ['default']
166
}
167
});
168
```
169
170
### HangingProcessReporter
171
172
Detects and reports hanging processes after tests complete.
173
174
```typescript { .api }
175
class HangingProcessReporter extends BaseReporter {
176
// Detects processes preventing test exit
177
}
178
```
179
180
### BlobReporter
181
182
Binary format for test results.
183
184
```typescript { .api }
185
class BlobReporter extends BaseReporter {
186
// Outputs binary blob format
187
}
188
```
189
190
## Custom Reporters
191
192
Create custom reporters by extending `BaseReporter`.
193
194
```typescript { .api }
195
abstract class BaseReporter {
196
/**
197
* Called when Vitest is initialized
198
*/
199
onInit(ctx: Vitest): void;
200
201
/**
202
* Called when test paths are collected
203
*/
204
onPathsCollected(paths?: string[]): void;
205
206
/**
207
* Called when tests are collected from files
208
*/
209
onCollected(files?: File[]): Promise<void> | void;
210
211
/**
212
* Called when all tests finish
213
*/
214
onFinished(
215
files?: File[],
216
errors?: unknown[]
217
): Promise<void> | void;
218
219
/**
220
* Called when a task updates
221
*/
222
onTaskUpdate(task: [string, TaskResult | undefined][]): void;
223
224
/**
225
* Called when a test is removed
226
*/
227
onTestRemoved(trigger?: string): void;
228
229
/**
230
* Called when watcher starts
231
*/
232
onWatcherStart(files?: File[], errors?: unknown[]): void;
233
234
/**
235
* Called when watcher reruns tests
236
*/
237
onWatcherRerun(files: string[], trigger?: string): void;
238
239
/**
240
* Called when server restarts
241
*/
242
onServerRestart(reason?: string): void;
243
244
/**
245
* Called when process times out
246
*/
247
onProcessTimeout(): void;
248
}
249
```
250
251
**Usage:**
252
253
```typescript
254
import { BaseReporter } from 'vitest/reporters';
255
import type { File, Vitest } from 'vitest';
256
257
class MyCustomReporter extends BaseReporter {
258
onInit(ctx: Vitest) {
259
console.log('Tests starting...');
260
}
261
262
onCollected(files?: File[]) {
263
console.log(`Collected ${files?.length} test files`);
264
}
265
266
onFinished(files?: File[], errors?: unknown[]) {
267
if (errors?.length) {
268
console.error(`Failed with ${errors.length} errors`);
269
} else {
270
console.log('All tests passed!');
271
}
272
}
273
}
274
275
export default defineConfig({
276
test: {
277
reporters: [new MyCustomReporter()]
278
}
279
});
280
```
281
282
## Multiple Reporters
283
284
Use multiple reporters simultaneously.
285
286
```typescript
287
export default defineConfig({
288
test: {
289
reporters: ['default', 'json', 'junit'],
290
outputFile: {
291
json: './results.json',
292
junit: './junit.xml'
293
}
294
}
295
});
296
```
297
298
## Reporter Registry
299
300
```typescript { .api }
301
/**
302
* Registry of built-in reporters
303
*/
304
const ReportersMap: Record<BuiltinReporters, typeof BaseReporter>;
305
306
type BuiltinReporters =
307
| 'default'
308
| 'verbose'
309
| 'dot'
310
| 'tree'
311
| 'json'
312
| 'blob'
313
| 'tap'
314
| 'tap-flat'
315
| 'junit'
316
| 'github-actions'
317
| 'hanging-process';
318
```
319
320
## Benchmark Reporters
321
322
Specialized reporters for benchmarking.
323
324
```typescript { .api }
325
class BenchmarkReporter {
326
onInit(ctx: Vitest): void;
327
onFinished(files?: File[], errors?: unknown[]): Promise<void> | void;
328
}
329
330
class VerboseBenchmarkReporter extends BenchmarkReporter {
331
// Detailed benchmark results with statistics
332
}
333
334
type BenchmarkBuiltinReporters = 'default' | 'verbose';
335
```
336
337
**Usage:**
338
339
```typescript
340
export default defineConfig({
341
test: {
342
benchmark: {
343
reporters: ['verbose']
344
}
345
}
346
});
347
```
348
349
## Common Patterns
350
351
### CI-Specific Reporters
352
353
```typescript
354
export default defineConfig({
355
test: {
356
reporters: process.env.CI
357
? ['junit', 'github-actions']
358
: ['default']
359
}
360
});
361
```
362
363
### Custom Metrics Reporter
364
365
```typescript
366
import { BaseReporter } from 'vitest/reporters';
367
368
class MetricsReporter extends BaseReporter {
369
private startTime: number = 0;
370
371
onInit() {
372
this.startTime = Date.now();
373
}
374
375
onFinished(files?: File[]) {
376
const duration = Date.now() - this.startTime;
377
const total = files?.reduce((acc, f) => acc + f.tasks.length, 0) || 0;
378
379
const passed = files?.reduce((acc, f) => {
380
return acc + f.tasks.filter(t => t.result?.state === 'pass').length;
381
}, 0) || 0;
382
383
console.log({
384
duration,
385
total,
386
passed,
387
passRate: (passed / total * 100).toFixed(2) + '%'
388
});
389
}
390
}
391
```
392
393
### Conditional Output
394
395
```typescript
396
import { BaseReporter } from 'vitest/reporters';
397
398
class QuietReporter extends BaseReporter {
399
onFinished(files?: File[], errors?: unknown[]) {
400
// Only output if there are errors
401
if (errors?.length) {
402
console.error(`Tests failed with ${errors.length} errors`);
403
errors.forEach(e => console.error(e));
404
}
405
}
406
}
407
```
408
409
## Type Definitions
410
411
```typescript { .api }
412
interface File {
413
filepath: string;
414
tasks: Task[];
415
result?: TaskResult;
416
}
417
418
interface Task {
419
id: string;
420
name: string;
421
mode: 'run' | 'skip' | 'only' | 'todo';
422
result?: TaskResult;
423
}
424
425
interface TaskResult {
426
state: 'pass' | 'fail' | 'skip';
427
duration?: number;
428
error?: Error;
429
}
430
431
interface Reporter {
432
onInit?(ctx: Vitest): void;
433
onPathsCollected?(paths?: string[]): void;
434
onCollected?(files?: File[]): Promise<void> | void;
435
onFinished?(files?: File[], errors?: unknown[]): Promise<void> | void;
436
onTaskUpdate?(task: [string, TaskResult | undefined][]): void;
437
}
438
```
439