Skip to content

Commit

Permalink
feat: verbose reporter to show slow in-progress tests
Browse files Browse the repository at this point in the history
  • Loading branch information
AriPerkkio committed Nov 12, 2024
1 parent 9f5803e commit 0e315ff
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 9 deletions.
20 changes: 19 additions & 1 deletion docs/guide/reporters.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ Example output using basic reporter:

### Verbose Reporter

Verbose reporter is same as `default` reporter, but it also displays each individual test after the suite has finished. Similar to `default` reporter, you can disable the summary by configuring the reporter.
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.

:::code-group
```bash [CLI]
Expand All @@ -190,6 +190,24 @@ export default defineConfig({
```
:::

Example output for tests in progress with default `slowTestThreshold: 300`:

```bash
✓ __tests__/example-1.test.ts (2) 725ms
✓ first test file (2) 725ms
✓ 2 + 2 should equal 4
✓ 4 - 2 should equal 2

❯ test/example-2.test.ts 3/5
↳ should run longer than three seconds 1.57s
❯ test/example-3.test.ts 1/5

Test Files 2 passed (4)
Tests 10 passed | 3 skipped (65)
Start at 11:01:36
Duration 2.00s
```

Example of final terminal output for a passing test suite:

```bash
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/node/reporters/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export class DefaultReporter extends BaseReporter {

onInit(ctx: Vitest) {
super.onInit(ctx)
this.summary?.onInit(ctx)
this.summary?.onInit(ctx, { verbose: this.verbose })
}

onPathsCollected(paths: string[] = []) {
Expand Down
84 changes: 77 additions & 7 deletions packages/vitest/src/node/reporters/summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ import type { Vitest } from '../core'
import type { Reporter } from '../types/reporter'
import { getTests } from '@vitest/runner/utils'
import c from 'tinyrainbow'
import { F_POINTER } from './renderers/figures'
import { F_DOWN_RIGHT, F_POINTER } from './renderers/figures'
import { formatProjectName, formatTime, formatTimeString, padSummaryTitle } from './renderers/utils'
import { WindowRenderer } from './renderers/windowedRenderer'

const DURATION_UPDATE_INTERVAL_MS = 500
const DURATION_UPDATE_INTERVAL_MS = 100
const FINISHED_TEST_CLEANUP_TIME_MS = 1_000

interface Options {
verbose?: boolean
}

interface Counter {
total: number
completed: number
Expand All @@ -22,6 +26,11 @@ interface Counter {
interface RunningTest extends Pick<Counter, 'total' | 'completed'> {
filename: File['name']
projectName: File['projectName']
slowTest?: {
name?: string
startTime: number
timeout: NodeJS.Timeout
}
}

/**
Expand All @@ -30,6 +39,7 @@ interface RunningTest extends Pick<Counter, 'total' | 'completed'> {
*/
export class SummaryReporter implements Reporter {
private ctx!: Vitest
private options!: Options
private renderer!: WindowRenderer

private suites = emptyCounters()
Expand All @@ -46,12 +56,18 @@ export class SummaryReporter implements Reporter {
private allFinishedTests = new Set<File['id']>()

private startTime = ''
private currentTime = 0
private duration = 0
private durationInterval: NodeJS.Timeout | undefined = undefined

onInit(ctx: Vitest) {
onInit(ctx: Vitest, options: Options = {}) {
this.ctx = ctx

this.options = {
verbose: false,
...options,
}

this.renderer = new WindowRenderer({
logger: ctx.logger,
getWindow: () => this.createSummary(),
Expand Down Expand Up @@ -88,7 +104,10 @@ export class SummaryReporter implements Reporter {
}

if (task?.type === 'test' || task?.type === 'custom') {
if (task.result?.state !== 'run') {
if (task.result?.state === 'run') {
this.onTestStart(task)
}
else {
this.onTestFinished(task)
}
}
Expand Down Expand Up @@ -129,6 +148,9 @@ export class SummaryReporter implements Reporter {
if (finished) {
clearTimeout(finished[1])
this.finishedTests.delete(finished[0])

const slowTest = this.runningTests.get(finished[0])?.slowTest
clearTimeout(slowTest?.timeout)
this.runningTests.delete(finished[0])
}
}
Expand All @@ -143,7 +165,7 @@ export class SummaryReporter implements Reporter {
this.maxParallelTests = Math.max(this.maxParallelTests, this.runningTests.size)
}

private onTestFinished(test: Test | Custom) {
private getTestStats(test: Test | Custom) {
const file = test.file
let stats = this.runningTests.get(file.id)

Expand All @@ -158,6 +180,43 @@ export class SummaryReporter implements Reporter {
}
}

return stats
}

private onTestStart(test: Test | Custom) {
// Track slow running tests only on verbose mode
if (!this.options.verbose) {
return
}

const stats = this.getTestStats(test)

if (!stats) {
return
}

const slowTest: NonNullable<RunningTest['slowTest']> = {
name: undefined,
startTime: performance.now(),
timeout: setTimeout(() => {
slowTest.name = test.name
}, this.ctx.config.slowTestThreshold).unref(),
}

clearTimeout(stats.slowTest?.timeout)
stats.slowTest = slowTest
}

private onTestFinished(test: Test | Custom) {
const file = test.file
const stats = this.getTestStats(test)

if (!stats) {
return
}

clearTimeout(stats.slowTest?.timeout)
stats.slowTest = undefined
stats.completed++
const result = test.result

Expand Down Expand Up @@ -225,6 +284,16 @@ export class SummaryReporter implements Reporter {
+ test.filename
+ c.dim(` ${test.completed}/${test.total}`),
)

if (test.slowTest?.name) {
const elapsed = this.currentTime - test.slowTest.startTime

summary.push(
c.bold(c.yellow(` ${F_DOWN_RIGHT} `))
+ test.slowTest.name
+ c.bold(c.yellow(` ${formatTime(Math.max(0, elapsed))}`)),
)
}
}

if (this.runningTests.size > 0) {
Expand All @@ -246,8 +315,9 @@ export class SummaryReporter implements Reporter {
this.startTime = formatTimeString(new Date())

this.durationInterval = setInterval(() => {
this.duration = performance.now() - start
}, DURATION_UPDATE_INTERVAL_MS)
this.currentTime = performance.now()
this.duration = this.currentTime - start
}, DURATION_UPDATE_INTERVAL_MS).unref()
}
}

Expand Down

0 comments on commit 0e315ff

Please sign in to comment.