Skip to content

Commit 511b73c

Browse files
authoredNov 18, 2024··
feat(reporters): summary option for verbose and default reporters (#6893)
1 parent 171041a commit 511b73c

22 files changed

+1124
-523
lines changed
 

‎docs/guide/reporters.md

+54-13
Original file line numberDiff line numberDiff line change
@@ -96,33 +96,54 @@ This example will write separate JSON and XML reports as well as printing a verb
9696

9797
### Default Reporter
9898

99-
By default (i.e. if no reporter is specified), Vitest will display results for each test suite hierarchically as they run, and then collapse after a suite passes. When all tests have finished running, the final terminal output will display a summary of results and details of any failed tests.
99+
By default (i.e. if no reporter is specified), Vitest will display summary of running tests and their status at the bottom. Once a suite passes, its status will be reported on top of the summary.
100+
101+
You can disable the summary by configuring the reporter:
102+
103+
:::code-group
104+
```ts [vitest.config.ts]
105+
export default defineConfig({
106+
test: {
107+
reporters: [
108+
['default', { summary: false }]
109+
]
110+
},
111+
})
112+
```
113+
:::
100114

101115
Example output for tests in progress:
102116

103117
```bash
104-
✓ __tests__/file1.test.ts (2) 725ms
105-
✓ __tests__/file2.test.ts (5) 746ms
106-
✓ second test file (2) 746ms
107-
✓ 1 + 1 should equal 2
108-
✓ 2 - 1 should equal 1
118+
✓ test/example-1.test.ts (5 tests | 1 skipped) 306ms
119+
✓ test/example-2.test.ts (5 tests | 1 skipped) 307ms
120+
121+
❯ test/example-3.test.ts 3/5
122+
❯ test/example-4.test.ts 1/5
123+
124+
Test Files 2 passed (4)
125+
Tests 10 passed | 3 skipped (65)
126+
Start at 11:01:36
127+
Duration 2.00s
109128
```
110129

111130
Final output after tests have finished:
112131

113132
```bash
114-
✓ __tests__/file1.test.ts (2) 725ms
115-
✓ __tests__/file2.test.ts (2) 746ms
133+
✓ test/example-1.test.ts (5 tests | 1 skipped) 306ms
134+
✓ test/example-2.test.ts (5 tests | 1 skipped) 307ms
135+
✓ test/example-3.test.ts (5 tests | 1 skipped) 307ms
136+
✓ test/example-4.test.ts (5 tests | 1 skipped) 307ms
116137

117-
Test Files 2 passed (2)
118-
Tests 4 passed (4)
138+
Test Files 4 passed (4)
139+
Tests 16 passed | 4 skipped (20)
119140
Start at 12:34:32
120141
Duration 1.26s (transform 35ms, setup 1ms, collect 90ms, tests 1.47s, environment 0ms, prepare 267ms)
121142
```
122143

123144
### Basic Reporter
124145

125-
The `basic` reporter displays the test files that have run and a summary of results after the entire suite has finished running. Individual tests are not included in the report unless they fail.
146+
The `basic` reporter is equivalent to `default` reporter without `summary`.
126147

127148
:::code-group
128149
```bash [CLI]
@@ -151,7 +172,7 @@ Example output using basic reporter:
151172

152173
### Verbose Reporter
153174

154-
Follows the same hierarchical structure as the `default` reporter, but does not collapse sub-trees for passed test suites. The final terminal output displays all tests that have run, including those that have passed.
175+
Verbose reporter is same as `default` reporter, but it also displays each individual test after the suite has finished. It also displays currently running tests that are taking longer than [`slowTestThreshold`](/config/#slowtestthreshold). Similar to `default` reporter, you can disable the summary by configuring the reporter.
155176

156177
:::code-group
157178
```bash [CLI]
@@ -161,12 +182,32 @@ npx vitest --reporter=verbose
161182
```ts [vitest.config.ts]
162183
export default defineConfig({
163184
test: {
164-
reporters: ['verbose']
185+
reporters: [
186+
['verbose', { summary: false }]
187+
]
165188
},
166189
})
167190
```
168191
:::
169192

193+
Example output for tests in progress with default `slowTestThreshold: 300`:
194+
195+
```bash
196+
✓ __tests__/example-1.test.ts (2) 725ms
197+
✓ first test file (2) 725ms
198+
✓ 2 + 2 should equal 4
199+
✓ 4 - 2 should equal 2
200+
201+
❯ test/example-2.test.ts 3/5
202+
↳ should run longer than three seconds 1.57s
203+
❯ test/example-3.test.ts 1/5
204+
205+
Test Files 2 passed (4)
206+
Tests 10 passed | 3 skipped (65)
207+
Start at 11:01:36
208+
Duration 2.00s
209+
```
210+
170211
Example of final terminal output for a passing test suite:
171212

172213
```bash

‎packages/vitest/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,11 @@
162162
"expect-type": "^1.1.0",
163163
"magic-string": "^0.30.12",
164164
"pathe": "^1.1.2",
165+
"restore-cursor": "^5.1.0",
165166
"std-env": "^3.8.0",
166167
"tinybench": "^2.9.0",
167168
"tinyexec": "^0.3.1",
168-
"tinypool": "^1.0.1",
169+
"tinypool": "^1.0.2",
169170
"tinyrainbow": "^1.2.0",
170171
"vite": "^5.0.0",
171172
"vite-node": "workspace:*",

‎packages/vitest/src/node/reporters/base.ts

+33-74
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@ import c from 'tinyrainbow'
1111
import { isCI, isDeno, isNode } from '../../utils/env'
1212
import { hasFailedSnapshot } from '../../utils/tasks'
1313
import { F_CHECK, F_POINTER, F_RIGHT } from './renderers/figures'
14-
import { countTestErrors, divider, formatProjectName, formatTimeString, getStateString, getStateSymbol, renderSnapshotSummary, taskFail, withLabel } from './renderers/utils'
14+
import { countTestErrors, divider, formatProjectName, formatTime, formatTimeString, getStateString, getStateSymbol, padSummaryTitle, renderSnapshotSummary, taskFail, withLabel } from './renderers/utils'
1515

1616
const BADGE_PADDING = ' '
17-
const LAST_RUN_LOG_TIMEOUT = 1_500
1817

1918
export interface BaseOptions {
2019
isTTY?: boolean
@@ -27,14 +26,12 @@ export abstract class BaseReporter implements Reporter {
2726
failedUnwatchedFiles: Task[] = []
2827
isTTY: boolean
2928
ctx: Vitest = undefined!
29+
renderSucceed = false
3030

3131
protected verbose = false
3232

3333
private _filesInWatchMode = new Map<string, number>()
3434
private _timeStart = formatTimeString(new Date())
35-
private _lastRunTimeout = 0
36-
private _lastRunTimer: NodeJS.Timeout | undefined
37-
private _lastRunCount = 0
3835

3936
constructor(options: BaseOptions = {}) {
4037
this.isTTY = options.isTTY ?? ((isNode || isDeno) && process.stdout?.isTTY && !isCI)
@@ -65,9 +62,6 @@ export abstract class BaseReporter implements Reporter {
6562
}
6663

6764
onTaskUpdate(packs: TaskResultPack[]) {
68-
if (this.isTTY) {
69-
return
70-
}
7165
for (const pack of packs) {
7266
const task = this.ctx.state.idMap.get(pack[0])
7367

@@ -117,6 +111,8 @@ export abstract class BaseReporter implements Reporter {
117111

118112
this.log(` ${title} ${task.name} ${suffix}`)
119113

114+
const anyFailed = tests.some(test => test.result?.state === 'fail')
115+
120116
for (const test of tests) {
121117
const duration = test.result?.duration
122118

@@ -137,6 +133,15 @@ export abstract class BaseReporter implements Reporter {
137133
+ ` ${c.yellow(Math.round(duration) + c.dim('ms'))}`,
138134
)
139135
}
136+
137+
// also print skipped tests that have notes
138+
else if (test.result?.state === 'skip' && test.result.note) {
139+
this.log(` ${getStateSymbol(test)} ${getTestName(test)}${c.dim(c.gray(` [${test.result.note}]`))}`)
140+
}
141+
142+
else if (this.renderSucceed || anyFailed) {
143+
this.log(` ${c.green(c.dim(F_CHECK))} ${getTestName(test, c.dim(' > '))}`)
144+
}
140145
}
141146
}
142147

@@ -153,8 +158,6 @@ export abstract class BaseReporter implements Reporter {
153158
}
154159

155160
onWatcherStart(files = this.ctx.state.getFiles(), errors = this.ctx.state.getUnhandledErrors()) {
156-
this.resetLastRunLog()
157-
158161
const failed = errors.length > 0 || hasFailed(files)
159162

160163
if (failed) {
@@ -177,38 +180,9 @@ export abstract class BaseReporter implements Reporter {
177180
}
178181

179182
this.log(BADGE_PADDING + hints.join(c.dim(', ')))
180-
181-
if (this._lastRunCount) {
182-
const LAST_RUN_TEXT = `rerun x${this._lastRunCount}`
183-
const LAST_RUN_TEXTS = [
184-
c.blue(LAST_RUN_TEXT),
185-
c.gray(LAST_RUN_TEXT),
186-
c.dim(c.gray(LAST_RUN_TEXT)),
187-
]
188-
this.ctx.logger.logUpdate(BADGE_PADDING + LAST_RUN_TEXTS[0])
189-
this._lastRunTimeout = 0
190-
this._lastRunTimer = setInterval(() => {
191-
this._lastRunTimeout += 1
192-
if (this._lastRunTimeout >= LAST_RUN_TEXTS.length) {
193-
this.resetLastRunLog()
194-
}
195-
else {
196-
this.ctx.logger.logUpdate(
197-
BADGE_PADDING + LAST_RUN_TEXTS[this._lastRunTimeout],
198-
)
199-
}
200-
}, LAST_RUN_LOG_TIMEOUT / LAST_RUN_TEXTS.length)
201-
}
202-
}
203-
204-
private resetLastRunLog() {
205-
clearInterval(this._lastRunTimer)
206-
this._lastRunTimer = undefined
207-
this.ctx.logger.logUpdate.clear()
208183
}
209184

210185
onWatcherRerun(files: string[], trigger?: string) {
211-
this.resetLastRunLog()
212186
this.watchFilters = files
213187
this.failedUnwatchedFiles = this.ctx.state.getFiles().filter(file =>
214188
!files.includes(file.filepath) && hasFailed(file),
@@ -222,11 +196,7 @@ export abstract class BaseReporter implements Reporter {
222196

223197
let banner = trigger ? c.dim(`${this.relative(trigger)} `) : ''
224198

225-
if (files.length > 1 || !files.length) {
226-
// we need to figure out how to handle rerun all from stdin
227-
this._lastRunCount = 0
228-
}
229-
else if (files.length === 1) {
199+
if (files.length === 1) {
230200
const rerun = this._filesInWatchMode.get(files[0]) ?? 1
231201
banner += c.blue(`x${rerun} `)
232202
}
@@ -248,10 +218,8 @@ export abstract class BaseReporter implements Reporter {
248218

249219
this.log('')
250220

251-
if (!this.isTTY) {
252-
for (const task of this.failedUnwatchedFiles) {
253-
this.printTask(task)
254-
}
221+
for (const task of this.failedUnwatchedFiles) {
222+
this.printTask(task)
255223
}
256224

257225
this._timeStart = formatTimeString(new Date())
@@ -351,6 +319,8 @@ export abstract class BaseReporter implements Reporter {
351319
}
352320

353321
reportTestSummary(files: File[], errors: unknown[]) {
322+
this.log()
323+
354324
const affectedFiles = [
355325
...this.failedUnwatchedFiles,
356326
...files,
@@ -364,21 +334,21 @@ export abstract class BaseReporter implements Reporter {
364334

365335
for (const [index, snapshot] of snapshotOutput.entries()) {
366336
const title = index === 0 ? 'Snapshots' : ''
367-
this.log(`${padTitle(title)} ${snapshot}`)
337+
this.log(`${padSummaryTitle(title)} ${snapshot}`)
368338
}
369339

370340
if (snapshotOutput.length > 1) {
371341
this.log()
372342
}
373343

374-
this.log(padTitle('Test Files'), getStateString(affectedFiles))
375-
this.log(padTitle('Tests'), getStateString(tests))
344+
this.log(padSummaryTitle('Test Files'), getStateString(affectedFiles))
345+
this.log(padSummaryTitle('Tests'), getStateString(tests))
376346

377347
if (this.ctx.projects.some(c => c.config.typecheck.enabled)) {
378348
const failed = tests.filter(t => t.meta?.typecheck && t.result?.errors?.length)
379349

380350
this.log(
381-
padTitle('Type Errors'),
351+
padSummaryTitle('Type Errors'),
382352
failed.length
383353
? c.bold(c.red(`${failed.length} failed`))
384354
: c.dim('no errors'),
@@ -387,19 +357,19 @@ export abstract class BaseReporter implements Reporter {
387357

388358
if (errors.length) {
389359
this.log(
390-
padTitle('Errors'),
360+
padSummaryTitle('Errors'),
391361
c.bold(c.red(`${errors.length} error${errors.length > 1 ? 's' : ''}`)),
392362
)
393363
}
394364

395-
this.log(padTitle('Start at'), this._timeStart)
365+
this.log(padSummaryTitle('Start at'), this._timeStart)
396366

397367
const collectTime = sum(files, file => file.collectDuration)
398368
const testsTime = sum(files, file => file.result?.duration)
399369
const setupTime = sum(files, file => file.setupDuration)
400370

401371
if (this.watchFilters) {
402-
this.log(padTitle('Duration'), time(collectTime + testsTime + setupTime))
372+
this.log(padSummaryTitle('Duration'), formatTime(collectTime + testsTime + setupTime))
403373
}
404374
else {
405375
const executionTime = this.end - this.start
@@ -409,16 +379,16 @@ export abstract class BaseReporter implements Reporter {
409379
const typecheck = sum(this.ctx.projects, project => project.typechecker?.getResult().time)
410380

411381
const timers = [
412-
`transform ${time(transformTime)}`,
413-
`setup ${time(setupTime)}`,
414-
`collect ${time(collectTime)}`,
415-
`tests ${time(testsTime)}`,
416-
`environment ${time(environmentTime)}`,
417-
`prepare ${time(prepareTime)}`,
418-
typecheck && `typecheck ${time(typecheck)}`,
382+
`transform ${formatTime(transformTime)}`,
383+
`setup ${formatTime(setupTime)}`,
384+
`collect ${formatTime(collectTime)}`,
385+
`tests ${formatTime(testsTime)}`,
386+
`environment ${formatTime(environmentTime)}`,
387+
`prepare ${formatTime(prepareTime)}`,
388+
typecheck && `typecheck ${formatTime(typecheck)}`,
419389
].filter(Boolean).join(', ')
420390

421-
this.log(padTitle('Duration'), time(executionTime) + c.dim(` (${timers})`))
391+
this.log(padSummaryTitle('Duration'), formatTime(executionTime) + c.dim(` (${timers})`))
422392
}
423393

424394
this.log()
@@ -544,17 +514,6 @@ function errorBanner(message: string) {
544514
return c.red(divider(c.bold(c.inverse(` ${message} `))))
545515
}
546516

547-
function padTitle(str: string) {
548-
return c.dim(`${str.padStart(11)} `)
549-
}
550-
551-
function time(time: number) {
552-
if (time > 1000) {
553-
return `${(time / 1000).toFixed(2)}s`
554-
}
555-
return `${Math.round(time)}ms`
556-
}
557-
558517
function sum<T>(items: T[], cb: (_next: T) => number | undefined) {
559518
return items.reduce((total, next) => {
560519
return total + Math.max(cb(next) || 0, 0)

‎packages/vitest/src/node/reporters/basic.ts

+12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import type { File } from '@vitest/runner'
2+
import type { Vitest } from '../core'
3+
import c from 'tinyrainbow'
24
import { BaseReporter } from './base'
35

46
export class BasicReporter extends BaseReporter {
@@ -7,6 +9,16 @@ export class BasicReporter extends BaseReporter {
79
this.isTTY = false
810
}
911

12+
onInit(ctx: Vitest) {
13+
super.onInit(ctx)
14+
15+
ctx.logger.log(c.inverse(c.bold(c.yellow(' DEPRECATED '))), c.yellow(
16+
`'basic' reporter is deprecated and will be removed in Vitest v3.\n`
17+
+ `Remove 'basic' from 'reporters' option. To match 'basic' reporter 100%, use configuration:\n${
18+
JSON.stringify({ test: { reporters: [['default', { summary: false }]] } }, null, 2)}`,
19+
))
20+
}
21+
1022
reportSummary(files: File[], errors: unknown[]) {
1123
// non-tty mode doesn't add a new line
1224
this.ctx.logger.log()

0 commit comments

Comments
 (0)
Please sign in to comment.