Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/advanced/api/reporters.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Vitest has its own test run lifecycle. These are represented by reporter's metho
- [`onHookEnd(afterAll)`](#onhookend)
- [`onTestSuiteResult`](#ontestsuiteresult)
- [`onTestModuleEnd`](#ontestmoduleend)
- [`onCoverage`](#oncoverage)
- [`onTestRunEnd`](#ontestrunend)

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.
Expand Down
2 changes: 1 addition & 1 deletion docs/advanced/pool.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ The function is called only once (unless the server config was updated), and it'

Vitest calls `runTest` when new tests are scheduled to run. It will not call it if `files` is empty. The first argument is an array of [TestSpecifications](/advanced/api/test-specification). Files are sorted using [`sequencer`](/config/#sequence-sequencer) before `runTests` is called. It's possible (but unlikely) to have the same file twice, but it will always have a different project - this is implemented via [`projects`](/guide/projects) configuration.

Vitest will wait until `runTests` is executed before finishing a run (i.e., it will emit [`onFinished`](/advanced/reporters) only after `runTests` is resolved).
Vitest will wait until `runTests` is executed before finishing a run (i.e., it will emit [`onTestRunEnd`](/advanced/reporters) only after `runTests` is resolved).

If you are using a custom pool, you will have to provide test files and their results yourself - you can reference [`vitest.state`](https://github.com/vitest-dev/vitest/blob/main/packages/vitest/src/node/state.ts) for that (most important are `collectFiles` and `updateTasks`). Vitest uses `startTests` function from `@vitest/runner` package to do that.

Expand Down
20 changes: 6 additions & 14 deletions docs/advanced/reporters.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ And here is an example of a custom reporter:
import { BaseReporter } from 'vitest/reporters'

export default class CustomReporter extends BaseReporter {
onCollected() {
onTestModuleCollected() {
const files = this.ctx.state.getFiles(this.watchFilters)
this.reportTestSummary(files)
}
Expand All @@ -39,7 +39,7 @@ Or implement the `Reporter` interface:
import type { Reporter } from 'vitest/node'

export default class CustomReporter implements Reporter {
onCollected() {
onTestModuleCollected() {
// print something
}
}
Expand All @@ -65,22 +65,14 @@ Instead of using the tasks that reporters receive, it is recommended to use the
You can get access to this API by calling `vitest.state.getReportedEntity(runnerTask)`:

```ts twoslash
import type { Reporter, RunnerTestFile, TestModule, Vitest } from 'vitest/node'
import type { Reporter, TestModule } from 'vitest/node'

class MyReporter implements Reporter {
private vitest!: Vitest

onInit(vitest: Vitest) {
this.vitest = vitest
}

onFinished(files: RunnerTestFile[]) {
for (const file of files) {
// note that the old task implementation uses "file" instead of "module"
const testModule = this.vitest.state.getReportedEntity(file) as TestModule
onTestRunEnd(testModules: ReadonlyArray<TestModule>) {
for (const testModule of testModules) {
for (const task of testModule.children) {
// ^?
console.log('finished', task.type, task.fullName)
console.log('test run end', task.type, task.fullName)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions docs/guide/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ Vitest 4.0 removes some deprecated APIs, including:
- `poolMatchGlobs` config option. Use [`projects`](/guide/projects) instead.
- `environmentMatchGlobs` config option. Use [`projects`](/guide/projects) instead.
- `workspace` config option. Use [`projects`](/guide/projects) instead.
- Reporter APIs `onCollected`, `onSpecsCollected`, `onPathsCollected`, `onTaskUpdate` and `onFinished`. See [`Reporters API`](/advanced/api/reporters) for new alternatives. These APIs were introduced in Vitest `v3.0.0`.
- `deps.external`, `deps.inline`, `deps.fallbackCJS` config options. Use `server.deps.external`, `server.deps.inline`, or `server.deps.fallbackCJS` instead.

This release also removes all deprecated types. This finally fixes an issue where Vitest accidentally pulled in `@types/node` (see [#5481](https://github.com/vitest-dev/vitest/issues/5481) and [#6141](https://github.com/vitest-dev/vitest/issues/6141)).
Expand Down
5 changes: 0 additions & 5 deletions packages/browser/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,6 @@ export type Awaitable<T> = T | PromiseLike<T>

export interface WebSocketEvents {
onCollected?: (files: RunnerTestFile[]) => Awaitable<void>
onFinished?: (
files: File[],
errors: unknown[],
coverage?: unknown
) => Awaitable<void>
onTaskUpdate?: (packs: TaskResultPack[]) => Awaitable<void>
onUserConsoleLog?: (log: UserConsoleLog) => Awaitable<void>
onPathsCollected?: (paths?: string[]) => Awaitable<void>
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/node/reporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export default class HTMLReporter implements Reporter {
await fs.mkdir(resolve(this.reporterDir, 'assets'), { recursive: true })
}

async onFinished(): Promise<void> {
async onTestRunEnd(): Promise<void> {
const result: HTMLReportData = {
paths: this.ctx.state.getPaths(),
files: this.ctx.state.getFiles(),
Expand Down
28 changes: 20 additions & 8 deletions packages/vitest/src/api/setup.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { File, TaskEventPack, TaskResultPack, TestAnnotation } from '@vitest/runner'
import type { SerializedError } from '@vitest/utils'
import type { IncomingMessage } from 'node:http'
import type { ViteDevServer } from 'vite'
import type { WebSocket } from 'ws'
import type { Vitest } from '../node/core'
import type { TestCase } from '../node/reporters/reported-tasks'
import type { TestCase, TestModule } from '../node/reporters/reported-tasks'
import type { TestSpecification } from '../node/spec'
import type { Reporter } from '../node/types/reporter'
import type { SerializedTestSpecification } from '../runtime/types/utils'
import type { Awaitable, LabelColor, ModuleGraphData, UserConsoleLog } from '../types/general'
import type { LabelColor, ModuleGraphData, UserConsoleLog } from '../types/general'
import type {
TransformResultWithSource,
WebSocketEvents,
Expand Down Expand Up @@ -163,21 +164,25 @@ export class WebSocketReporter implements Reporter {
public clients: Map<WebSocket, WebSocketRPC>,
) {}

onCollected(files?: File[]): void {
onTestModuleCollected(testModule: TestModule): void {
if (this.clients.size === 0) {
return
}

this.clients.forEach((client) => {
client.onCollected?.(files)?.catch?.(noop)
client.onCollected?.([testModule.task])?.catch?.(noop)
})
}

onSpecsCollected(specs?: SerializedTestSpecification[] | undefined): Awaitable<void> {
onTestRunStart(specifications: ReadonlyArray<TestSpecification>): void {
if (this.clients.size === 0) {
return
}

const serializedSpecs = specifications.map(spec => spec.toJSON())

this.clients.forEach((client) => {
client.onSpecsCollected?.(specs)?.catch?.(noop)
client.onSpecsCollected?.(serializedSpecs)?.catch?.(noop)
})
}

Expand Down Expand Up @@ -220,7 +225,14 @@ export class WebSocketReporter implements Reporter {
})
}

onFinished(files: File[], errors: unknown[]): void {
onTestRunEnd(testModules: ReadonlyArray<TestModule>, unhandledErrors: ReadonlyArray<SerializedError>): void {
if (!this.clients.size) {
return
}

const files = testModules.map(testModule => testModule.task)
const errors = [...unhandledErrors]

this.clients.forEach((client) => {
client.onFinished?.(files, errors)?.catch?.(noop)
})
Expand Down
3 changes: 0 additions & 3 deletions packages/vitest/src/node/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,7 +446,6 @@ export class Vitest {
this.state.blobs = { files, errors, coverages, executionTimes }

await this.report('onInit', this)
await this.report('onPathsCollected', files.flatMap(f => f.filepath))

const specifications: TestSpecification[] = []
for (const file of files) {
Expand All @@ -455,7 +454,6 @@ export class Vitest {
specifications.push(specification)
}

await this.report('onSpecsCollected', specifications.map(spec => spec.toJSON()))
await this._testRun.start(specifications).catch(noop)

for (const file of files) {
Expand Down Expand Up @@ -698,7 +696,6 @@ export class Vitest {
}
}
finally {
// TODO: wait for coverage only if `onFinished` is defined
const coverage = await this.coverageProvider?.generateCoverage({ allTestsRun })

const errors = this.state.getUnhandledErrors()
Expand Down
11 changes: 10 additions & 1 deletion packages/vitest/src/node/pools/typecheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { Vitest } from '../core'
import type { ProcessPool } from '../pool'
import type { TestProject } from '../project'
import type { TestSpecification } from '../spec'
import type { TestRunEndReason } from '../types/reporter'
import { hasFailed } from '@vitest/runner/utils'
import { createDefer } from '@vitest/utils'
import { Typechecker } from '../../typecheck/typechecker'
Expand Down Expand Up @@ -42,7 +43,15 @@ export function createTypecheckPool(vitest: Vitest): ProcessPool {

// triggered by TSC watcher, not Vitest watcher, so we need to emulate what Vitest does in this case
if (vitest.config.watch && !vitest.runningPromise) {
await vitest.report('onFinished', files, [])
const modules = files.map(file => vitest.state.getReportedEntity(file)).filter(e => e?.type === 'module')

const state: TestRunEndReason = vitest.isCancelling
? 'interrupted'
: modules.some(m => !m.ok())
? 'failed'
: 'passed'

await vitest.report('onTestRunEnd', modules, [], state)
await vitest.report('onWatcherStart', files, [
...(project.config.typecheck.ignoreSourceErrors ? [] : sourceErrors),
...vitest.state.getUnhandledErrors(),
Expand Down
12 changes: 10 additions & 2 deletions packages/vitest/src/node/reporters/base.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { File, Task } from '@vitest/runner'
import type { SerializedError } from '@vitest/utils'
import type { TestError, UserConsoleLog } from '../../types/general'
import type { Vitest } from '../core'
import type { Reporter } from '../types/reporter'
import type { Reporter, TestRunEndReason } from '../types/reporter'
import type { TestCase, TestCollection, TestModule, TestModuleState, TestResult, TestSuite, TestSuiteState } from './reported-tasks'
import { performance } from 'node:perf_hooks'
import { getFullName, getSuites, getTestName, getTests, hasFailed } from '@vitest/runner/utils'
Expand Down Expand Up @@ -57,7 +58,14 @@ export abstract class BaseReporter implements Reporter {
return relative(this.ctx.config.root, path)
}

onFinished(files: File[] = this.ctx.state.getFiles(), errors: unknown[] = this.ctx.state.getUnhandledErrors()): void {
onTestRunEnd(
testModules: ReadonlyArray<TestModule>,
unhandledErrors: ReadonlyArray<SerializedError>,
_reason: TestRunEndReason,
): void {
const files = testModules.map(testModule => testModule.task)
const errors = [...unhandledErrors]

this.end = performance.now()
if (!files.length && !errors.length) {
this.ctx.logger.printNoTestFound(this.ctx.filenamePattern)
Expand Down
14 changes: 11 additions & 3 deletions packages/vitest/src/node/reporters/benchmark/reporter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { File, TaskResultPack } from '@vitest/runner'
import type { TaskResultPack } from '@vitest/runner'
import type { SerializedError } from '@vitest/utils'
import type { Vitest } from '../../core'
import type { TestRunEndReason } from '../../types/reporter'
import type { TestModule, TestSuite } from '../reported-tasks'
import fs from 'node:fs'
import { getFullName } from '@vitest/runner/utils'
Expand Down Expand Up @@ -84,8 +86,12 @@ export class BenchmarkReporter extends DefaultReporter {
}
}

async onFinished(files: File[] = this.ctx.state.getFiles(), errors: unknown[] = this.ctx.state.getUnhandledErrors()): Promise<void> {
super.onFinished(files, errors)
async onTestRunEnd(
testModules: ReadonlyArray<TestModule>,
unhandledErrors: ReadonlyArray<SerializedError>,
reason: TestRunEndReason,
): Promise<void> {
super.onTestRunEnd(testModules, unhandledErrors, reason)

// write output for future comparison
let outputFile = this.ctx.config.benchmark?.outputJson
Expand All @@ -98,7 +104,9 @@ export class BenchmarkReporter extends DefaultReporter {
await fs.promises.mkdir(outputDirectory, { recursive: true })
}

const files = testModules.map(t => t.task.file)
const output = createBenchmarkJsonReport(files)

await fs.promises.writeFile(outputFile, JSON.stringify(output, null, 2))
this.log(`Benchmark report written to ${outputFile}`)
}
Expand Down
18 changes: 13 additions & 5 deletions packages/vitest/src/node/reporters/blob.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type { File } from '@vitest/runner'
import type { SerializedError } from '@vitest/utils'
import type { Vitest } from '../core'
import type { TestProject } from '../project'
import type { Reporter } from '../types/reporter'
import type { TestModule } from './reported-tasks'
import { existsSync } from 'node:fs'
import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises'
import { parse, stringify } from 'flatted'
Expand All @@ -16,6 +18,7 @@ export class BlobReporter implements Reporter {
start = 0
ctx!: Vitest
options: BlobOptions
coverage: unknown | undefined

constructor(options: BlobOptions) {
this.options = options
Expand All @@ -28,15 +31,20 @@ export class BlobReporter implements Reporter {

this.ctx = ctx
this.start = performance.now()
this.coverage = undefined
}

async onFinished(
files: File[] = [],
errors: unknown[] = [],
coverage: unknown,
): Promise<void> {
onCoverage(coverage: unknown): void {
this.coverage = coverage
}

async onTestRunEnd(testModules: ReadonlyArray<TestModule>, unhandledErrors: ReadonlyArray<SerializedError>): Promise<void> {
const executionTime = performance.now() - this.start

const files = testModules.map(testModule => testModule.task)
const errors = [...unhandledErrors]
const coverage = this.coverage

let outputFile
= this.options.outputFile ?? getOutputFile(this.ctx.config, 'blob')
if (!outputFile) {
Expand Down
15 changes: 11 additions & 4 deletions packages/vitest/src/node/reporters/default.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import type { SerializedError } from '@vitest/utils'
import type { Vitest } from '../core'
import type { TestSpecification } from '../spec'
import type { TestRunEndReason } from '../types/reporter'
import type { BaseOptions } from './base'
import type { ReportedHookContext, TestCase, TestModule } from './reported-tasks'
import { BaseReporter } from './base'
Expand Down Expand Up @@ -43,6 +45,15 @@ export class DefaultReporter extends BaseReporter {
this.summary?.onTestRunStart(specifications)
}

onTestRunEnd(
testModules: ReadonlyArray<TestModule>,
unhandledErrors: ReadonlyArray<SerializedError>,
reason: TestRunEndReason,
): void {
super.onTestRunEnd(testModules, unhandledErrors, reason)
this.summary?.onTestRunEnd()
}

onTestModuleQueued(file: TestModule): void {
this.summary?.onTestModuleQueued(file)
}
Expand Down Expand Up @@ -77,8 +88,4 @@ export class DefaultReporter extends BaseReporter {
super.onInit(ctx)
this.summary?.onInit(ctx, { verbose: this.verbose })
}

onTestRunEnd(): void {
this.summary?.onTestRunEnd()
}
}
12 changes: 9 additions & 3 deletions packages/vitest/src/node/reporters/dot.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import type { File, Test } from '@vitest/runner'
import type { Test } from '@vitest/runner'
import type { SerializedError } from '@vitest/utils'
import type { Writable } from 'node:stream'
import type { Vitest } from '../core'
import type { TestRunEndReason } from '../types/reporter'
import type { TestCase, TestModule } from './reported-tasks'
import c from 'tinyrainbow'
import { BaseReporter } from './base'
Expand Down Expand Up @@ -40,7 +42,11 @@ export class DotReporter extends BaseReporter {
super.onWatcherRerun(files, trigger)
}

onFinished(files?: File[], errors?: unknown[]): void {
onTestRunEnd(
testModules: ReadonlyArray<TestModule>,
unhandledErrors: ReadonlyArray<SerializedError>,
reason: TestRunEndReason,
): void {
if (this.isTTY) {
const finalLog = formatTests(Array.from(this.tests.values()))
this.ctx.logger.log(finalLog)
Expand All @@ -52,7 +58,7 @@ export class DotReporter extends BaseReporter {
this.tests.clear()
this.renderer?.finish()

super.onFinished(files, errors)
super.onTestRunEnd(testModules, unhandledErrors, reason)
}

onTestModuleCollected(module: TestModule): void {
Expand Down
11 changes: 9 additions & 2 deletions packages/vitest/src/node/reporters/github-actions.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import type { File, TestAnnotation } from '@vitest/runner'
import type { SerializedError } from '@vitest/utils'
import type { Vitest } from '../core'
import type { TestProject } from '../project'
import type { Reporter } from '../types/reporter'
import type { TestCase } from './reported-tasks'
import type { TestCase, TestModule } from './reported-tasks'
import { stripVTControlCharacters } from 'node:util'
import { getFullName, getTasks } from '@vitest/runner/utils'
import { capturePrintError } from '../printError'
Expand Down Expand Up @@ -42,7 +43,13 @@ export class GithubActionsReporter implements Reporter {
this.ctx.logger.log(`\n${formatted}`)
}

onFinished(files: File[] = [], errors: unknown[] = []): void {
onTestRunEnd(
testModules: ReadonlyArray<TestModule>,
unhandledErrors: ReadonlyArray<SerializedError>,
): void {
const files = testModules.map(testModule => testModule.task)
const errors = [...unhandledErrors]

// collect all errors and associate them with projects
const projectErrors = new Array<{
project: TestProject
Expand Down
Loading
Loading