From 149f8e509570e2743ff6a5bcca800efc6aa4d6e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ari=20Perkki=C3=B6?= Date: Mon, 4 Aug 2025 15:30:28 +0300 Subject: [PATCH] fix(reporter)!: remove deprecated APIs (#8223) Co-authored-by: Vladimir Sheremet --- docs/advanced/api/reporters.md | 1 + docs/advanced/pool.md | 2 +- docs/advanced/reporters.md | 20 ++----- docs/guide/migration.md | 1 + packages/browser/src/types.ts | 5 -- packages/ui/node/reporter.ts | 2 +- packages/vitest/src/api/setup.ts | 28 ++++++--- packages/vitest/src/node/core.ts | 3 - packages/vitest/src/node/pools/typecheck.ts | 11 +++- packages/vitest/src/node/reporters/base.ts | 12 +++- .../src/node/reporters/benchmark/reporter.ts | 14 ++++- packages/vitest/src/node/reporters/blob.ts | 18 ++++-- packages/vitest/src/node/reporters/default.ts | 15 +++-- packages/vitest/src/node/reporters/dot.ts | 12 +++- .../src/node/reporters/github-actions.ts | 11 +++- packages/vitest/src/node/reporters/json.ts | 19 +++--- packages/vitest/src/node/reporters/junit.ts | 7 ++- .../vitest/src/node/reporters/tap-flat.ts | 7 ++- packages/vitest/src/node/reporters/tap.ts | 7 ++- packages/vitest/src/node/test-run.ts | 27 +++------ packages/vitest/src/node/types/reporter.ts | 25 +------- test/browser/specs/runner.test.ts | 7 +-- test/cli/test/browser-multiple.test.ts | 2 +- test/cli/test/create-vitest.test.ts | 27 +++++---- test/cli/test/public-api.test.ts | 58 +++++++++---------- test/cli/test/reported-tasks.test.ts | 11 ++-- test/reporters/reportPkg/index.js | 2 +- test/reporters/src/context.ts | 1 + test/reporters/src/custom-reporter.js | 2 +- test/reporters/src/custom-reporter.ts | 2 +- .../tests/configuration-options.test-d.ts | 2 +- test/reporters/tests/console.test.ts | 1 - test/reporters/tests/reporters.spec.ts | 52 +++++++++++------ test/reporters/tests/silent.test.ts | 1 - test/reporters/tests/tap.test.ts | 38 ++++++++++++ 35 files changed, 263 insertions(+), 190 deletions(-) diff --git a/docs/advanced/api/reporters.md b/docs/advanced/api/reporters.md index 2925fc255..552f2fd87 100644 --- a/docs/advanced/api/reporters.md +++ b/docs/advanced/api/reporters.md @@ -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. diff --git a/docs/advanced/pool.md b/docs/advanced/pool.md index 0485164bd..bf63d9af0 100644 --- a/docs/advanced/pool.md +++ b/docs/advanced/pool.md @@ -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. diff --git a/docs/advanced/reporters.md b/docs/advanced/reporters.md index 6a7e01d87..b75f080db 100644 --- a/docs/advanced/reporters.md +++ b/docs/advanced/reporters.md @@ -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) } @@ -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 } } @@ -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) { + 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) } } } diff --git a/docs/guide/migration.md b/docs/guide/migration.md index a7dfea304..bc532f7e7 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -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)). diff --git a/packages/browser/src/types.ts b/packages/browser/src/types.ts index 94fcd72e6..414eb24ea 100644 --- a/packages/browser/src/types.ts +++ b/packages/browser/src/types.ts @@ -63,11 +63,6 @@ export type Awaitable = T | PromiseLike export interface WebSocketEvents { onCollected?: (files: RunnerTestFile[]) => Awaitable - onFinished?: ( - files: File[], - errors: unknown[], - coverage?: unknown - ) => Awaitable onTaskUpdate?: (packs: TaskResultPack[]) => Awaitable onUserConsoleLog?: (log: UserConsoleLog) => Awaitable onPathsCollected?: (paths?: string[]) => Awaitable diff --git a/packages/ui/node/reporter.ts b/packages/ui/node/reporter.ts index dd0731765..d22680b1d 100644 --- a/packages/ui/node/reporter.ts +++ b/packages/ui/node/reporter.ts @@ -71,7 +71,7 @@ export default class HTMLReporter implements Reporter { await fs.mkdir(resolve(this.reporterDir, 'assets'), { recursive: true }) } - async onFinished(): Promise { + async onTestRunEnd(): Promise { const result: HTMLReportData = { paths: this.ctx.state.getPaths(), files: this.ctx.state.getFiles(), diff --git a/packages/vitest/src/api/setup.ts b/packages/vitest/src/api/setup.ts index dd0d0d8c1..828fd03ff 100644 --- a/packages/vitest/src/api/setup.ts +++ b/packages/vitest/src/api/setup.ts @@ -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, @@ -163,21 +164,25 @@ export class WebSocketReporter implements Reporter { public clients: Map, ) {} - 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 { + onTestRunStart(specifications: ReadonlyArray): 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) }) } @@ -220,7 +225,14 @@ export class WebSocketReporter implements Reporter { }) } - onFinished(files: File[], errors: unknown[]): void { + onTestRunEnd(testModules: ReadonlyArray, unhandledErrors: ReadonlyArray): 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) }) diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index 384598dfa..5bc9253c2 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -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) { @@ -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) { @@ -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() diff --git a/packages/vitest/src/node/pools/typecheck.ts b/packages/vitest/src/node/pools/typecheck.ts index 13b093fca..7b3a19bfe 100644 --- a/packages/vitest/src/node/pools/typecheck.ts +++ b/packages/vitest/src/node/pools/typecheck.ts @@ -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' @@ -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(), diff --git a/packages/vitest/src/node/reporters/base.ts b/packages/vitest/src/node/reporters/base.ts index 1dbc93d1f..8150d8613 100644 --- a/packages/vitest/src/node/reporters/base.ts +++ b/packages/vitest/src/node/reporters/base.ts @@ -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' @@ -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, + unhandledErrors: ReadonlyArray, + _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) diff --git a/packages/vitest/src/node/reporters/benchmark/reporter.ts b/packages/vitest/src/node/reporters/benchmark/reporter.ts index 02e2669c7..2c2e820f9 100644 --- a/packages/vitest/src/node/reporters/benchmark/reporter.ts +++ b/packages/vitest/src/node/reporters/benchmark/reporter.ts @@ -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' @@ -84,8 +86,12 @@ export class BenchmarkReporter extends DefaultReporter { } } - async onFinished(files: File[] = this.ctx.state.getFiles(), errors: unknown[] = this.ctx.state.getUnhandledErrors()): Promise { - super.onFinished(files, errors) + async onTestRunEnd( + testModules: ReadonlyArray, + unhandledErrors: ReadonlyArray, + reason: TestRunEndReason, + ): Promise { + super.onTestRunEnd(testModules, unhandledErrors, reason) // write output for future comparison let outputFile = this.ctx.config.benchmark?.outputJson @@ -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}`) } diff --git a/packages/vitest/src/node/reporters/blob.ts b/packages/vitest/src/node/reporters/blob.ts index 664320d96..e2e89ce47 100644 --- a/packages/vitest/src/node/reporters/blob.ts +++ b/packages/vitest/src/node/reporters/blob.ts @@ -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' @@ -16,6 +18,7 @@ export class BlobReporter implements Reporter { start = 0 ctx!: Vitest options: BlobOptions + coverage: unknown | undefined constructor(options: BlobOptions) { this.options = options @@ -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 { + onCoverage(coverage: unknown): void { + this.coverage = coverage + } + + async onTestRunEnd(testModules: ReadonlyArray, unhandledErrors: ReadonlyArray): Promise { 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) { diff --git a/packages/vitest/src/node/reporters/default.ts b/packages/vitest/src/node/reporters/default.ts index 33cce7780..7d129aefd 100644 --- a/packages/vitest/src/node/reporters/default.ts +++ b/packages/vitest/src/node/reporters/default.ts @@ -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' @@ -43,6 +45,15 @@ export class DefaultReporter extends BaseReporter { this.summary?.onTestRunStart(specifications) } + onTestRunEnd( + testModules: ReadonlyArray, + unhandledErrors: ReadonlyArray, + reason: TestRunEndReason, + ): void { + super.onTestRunEnd(testModules, unhandledErrors, reason) + this.summary?.onTestRunEnd() + } + onTestModuleQueued(file: TestModule): void { this.summary?.onTestModuleQueued(file) } @@ -77,8 +88,4 @@ export class DefaultReporter extends BaseReporter { super.onInit(ctx) this.summary?.onInit(ctx, { verbose: this.verbose }) } - - onTestRunEnd(): void { - this.summary?.onTestRunEnd() - } } diff --git a/packages/vitest/src/node/reporters/dot.ts b/packages/vitest/src/node/reporters/dot.ts index 69f490a36..29ce0e767 100644 --- a/packages/vitest/src/node/reporters/dot.ts +++ b/packages/vitest/src/node/reporters/dot.ts @@ -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' @@ -40,7 +42,11 @@ export class DotReporter extends BaseReporter { super.onWatcherRerun(files, trigger) } - onFinished(files?: File[], errors?: unknown[]): void { + onTestRunEnd( + testModules: ReadonlyArray, + unhandledErrors: ReadonlyArray, + reason: TestRunEndReason, + ): void { if (this.isTTY) { const finalLog = formatTests(Array.from(this.tests.values())) this.ctx.logger.log(finalLog) @@ -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 { diff --git a/packages/vitest/src/node/reporters/github-actions.ts b/packages/vitest/src/node/reporters/github-actions.ts index af490c6c4..ce1cb3654 100644 --- a/packages/vitest/src/node/reporters/github-actions.ts +++ b/packages/vitest/src/node/reporters/github-actions.ts @@ -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' @@ -42,7 +43,13 @@ export class GithubActionsReporter implements Reporter { this.ctx.logger.log(`\n${formatted}`) } - onFinished(files: File[] = [], errors: unknown[] = []): void { + onTestRunEnd( + testModules: ReadonlyArray, + unhandledErrors: ReadonlyArray, + ): 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 diff --git a/packages/vitest/src/node/reporters/json.ts b/packages/vitest/src/node/reporters/json.ts index a5218d594..68616bec7 100644 --- a/packages/vitest/src/node/reporters/json.ts +++ b/packages/vitest/src/node/reporters/json.ts @@ -1,8 +1,9 @@ -import type { File, Suite, TaskMeta, TaskState } from '@vitest/runner' +import type { Suite, TaskMeta, TaskState } from '@vitest/runner' import type { SnapshotSummary } from '@vitest/snapshot' import type { CoverageMap } from 'istanbul-lib-coverage' import type { Vitest } from '../core' import type { Reporter } from '../types/reporter' +import type { TestModule } from './reported-tasks' import { existsSync, promises as fs } from 'node:fs' import { getSuites, getTests } from '@vitest/runner/utils' import { dirname, resolve } from 'pathe' @@ -78,6 +79,7 @@ export class JsonReporter implements Reporter { start = 0 ctx!: Vitest options: JsonOptions + coverageMap?: CoverageMap constructor(options: JsonOptions) { this.options = options @@ -86,9 +88,16 @@ export class JsonReporter implements Reporter { onInit(ctx: Vitest): void { this.ctx = ctx this.start = Date.now() + this.coverageMap = undefined } - protected async logTasks(files: File[], coverageMap?: CoverageMap | null): Promise { + onCoverage(coverageMap: unknown): void { + this.coverageMap = coverageMap as CoverageMap + } + + async onTestRunEnd(testModules: ReadonlyArray): Promise { + const files = testModules.map(testModule => testModule.task) + const suites = getSuites(files) const numTotalTestSuites = suites.length const tests = getTests(files) @@ -190,16 +199,12 @@ export class JsonReporter implements Reporter { startTime: this.start, success, testResults, - coverageMap, + coverageMap: this.coverageMap, } await this.writeReport(JSON.stringify(result)) } - async onFinished(files: File[] = this.ctx.state.getFiles(), _errors: unknown[] = [], coverageMap?: unknown): Promise { - await this.logTasks(files, coverageMap as CoverageMap) - } - /** * Writes the report to an output file if specified in the config, * or logs it to the console otherwise. diff --git a/packages/vitest/src/node/reporters/junit.ts b/packages/vitest/src/node/reporters/junit.ts index c2011ce21..ae97c8666 100644 --- a/packages/vitest/src/node/reporters/junit.ts +++ b/packages/vitest/src/node/reporters/junit.ts @@ -1,6 +1,7 @@ -import type { File, Task } from '@vitest/runner' +import type { Task } from '@vitest/runner' import type { Vitest } from '../core' import type { Reporter } from '../types/reporter' +import type { TestModule } from './reported-tasks' import { existsSync, promises as fs } from 'node:fs' import { hostname } from 'node:os' @@ -284,7 +285,9 @@ export class JUnitReporter implements Reporter { } } - async onFinished(files: File[] = this.ctx.state.getFiles()): Promise { + async onTestRunEnd(testModules: ReadonlyArray): Promise { + const files = testModules.map(testModule => testModule.task) + await this.logger.log('') const transformed = files.map((file) => { diff --git a/packages/vitest/src/node/reporters/tap-flat.ts b/packages/vitest/src/node/reporters/tap-flat.ts index 89e177b08..d216dbcf8 100644 --- a/packages/vitest/src/node/reporters/tap-flat.ts +++ b/packages/vitest/src/node/reporters/tap-flat.ts @@ -1,5 +1,6 @@ -import type { File, Task } from '@vitest/runner' +import type { Task } from '@vitest/runner' import type { Vitest } from '../core' +import type { TestModule } from './reported-tasks' import { TapReporter } from './tap' function flattenTasks(task: Task, baseName = ''): Task[] { @@ -25,10 +26,10 @@ export class TapFlatReporter extends TapReporter { super.onInit(ctx) } - onFinished(files: File[] = this.ctx.state.getFiles()): void { + onTestRunEnd(testModules: ReadonlyArray): void { this.ctx.logger.log('TAP version 13') - const flatTasks = files.flatMap(task => flattenTasks(task)) + const flatTasks = testModules.flatMap(testModule => flattenTasks(testModule.task)) this.logTasks(flatTasks) } diff --git a/packages/vitest/src/node/reporters/tap.ts b/packages/vitest/src/node/reporters/tap.ts index fced21658..ff54113c2 100644 --- a/packages/vitest/src/node/reporters/tap.ts +++ b/packages/vitest/src/node/reporters/tap.ts @@ -1,7 +1,8 @@ -import type { File, Task } from '@vitest/runner' +import type { Task } from '@vitest/runner' import type { ParsedStack, TestError } from '@vitest/utils' import type { Vitest } from '../core' import type { Reporter } from '../types/reporter' +import type { TestModule } from './reported-tasks' import { parseErrorStacktrace } from '../../utils/source-map' import { IndentedLogger } from './renderers/indented-logger' @@ -132,7 +133,9 @@ export class TapReporter implements Reporter { } } - onFinished(files: File[] = this.ctx.state.getFiles()): void { + onTestRunEnd(testModules: ReadonlyArray): void { + const files = testModules.map(testModule => testModule.task) + this.logger.log('TAP version 13') this.logTasks(files) diff --git a/packages/vitest/src/node/test-run.ts b/packages/vitest/src/node/test-run.ts index 845045ace..7deb1326e 100644 --- a/packages/vitest/src/node/test-run.ts +++ b/packages/vitest/src/node/test-run.ts @@ -28,8 +28,6 @@ export class TestRun { const filepaths = specifications.map(spec => spec.moduleId) this.vitest.state.collectPaths(filepaths) - await this.vitest.report('onPathsCollected', Array.from(new Set(filepaths))) - await this.vitest.report('onSpecsCollected', specifications.map(spec => spec.toJSON())) await this.vitest.report('onTestRunStart', [...specifications]) } @@ -41,13 +39,12 @@ export class TestRun { async collected(project: TestProject, files: RunnerTestFile[]): Promise { this.vitest.state.collectFiles(project, files) - await Promise.all([ - this.vitest.report('onCollected', files), - ...files.map((file) => { + await Promise.all( + files.map((file) => { const testModule = this.vitest.state.getReportedEntity(file) as TestModule return this.vitest.report('onTestModuleCollected', testModule) }), - ]) + ) } async log(log: UserConsoleLog): Promise { @@ -86,9 +83,12 @@ export class TestRun { } async end(specifications: TestSpecification[], errors: unknown[], coverage?: unknown): Promise { + if (coverage) { + await this.vitest.report('onCoverage', coverage) + } + // specification won't have the File task if they were filtered by the --shard command const modules = specifications.map(spec => spec.testModule).filter(s => s != null) - const files = modules.map(m => m.task) const state: TestRunEndReason = this.vitest.isCancelling ? 'interrupted' @@ -102,18 +102,7 @@ export class TestRun { process.exitCode = 1 } - try { - await Promise.all([ - this.vitest.report('onTestRunEnd', modules, [...errors] as SerializedError[], state), - // TODO: in a perfect world, the coverage should be done in parallel to `onFinished` - this.vitest.report('onFinished', files, errors, coverage), - ]) - } - finally { - if (coverage) { - await this.vitest.report('onCoverage', coverage) - } - } + this.vitest.report('onTestRunEnd', modules, [...errors] as SerializedError[], state) } private hasFailed(modules: TestModule[]) { diff --git a/packages/vitest/src/node/types/reporter.ts b/packages/vitest/src/node/types/reporter.ts index 7a9fa2f84..37c9d9e61 100644 --- a/packages/vitest/src/node/types/reporter.ts +++ b/packages/vitest/src/node/types/reporter.ts @@ -1,6 +1,5 @@ import type { File, TaskEventPack, TaskResultPack, TestAnnotation } from '@vitest/runner' import type { SerializedError } from '@vitest/utils' -import type { SerializedTestSpecification } from '../../runtime/types/utils' import type { Awaitable, UserConsoleLog } from '../../types/general' import type { Vitest } from '../core' import type { TestProject } from '../project' @@ -17,29 +16,7 @@ export interface Reporter { * @experimental */ onBrowserInit?: (project: TestProject) => Awaitable - /** - * @deprecated use `onTestRunStart` instead - */ - onPathsCollected?: (paths?: string[]) => Awaitable - /** - * @deprecated use `onTestRunStart` instead - */ - onSpecsCollected?: (specs?: SerializedTestSpecification[]) => Awaitable - /** - * @deprecated use `onTestModuleCollected` instead - */ - onCollected?: (files: File[]) => Awaitable - /** - * @deprecated use `onTestRunEnd` instead - */ - onFinished?: ( - files: File[], - errors: unknown[], - coverage?: unknown - ) => Awaitable - /** - * @deprecated use `onTestModuleQueued`, `onTestModuleStart`, `onTestModuleEnd`, `onTestCaseReady`, `onTestCaseResult` instead - */ + /** @internal */ onTaskUpdate?: (packs: TaskResultPack[], events: TaskEventPack[]) => Awaitable onTestRemoved?: (trigger?: string) => Awaitable onWatcherStart?: (files?: File[], errors?: unknown[]) => Awaitable diff --git a/test/browser/specs/runner.test.ts b/test/browser/specs/runner.test.ts index f5aace7f1..1024ec5f4 100644 --- a/test/browser/specs/runner.test.ts +++ b/test/browser/specs/runner.test.ts @@ -33,10 +33,9 @@ describe('running browser tests', async () => { 'json', { onInit: noop, - onPathsCollected: noop, - onCollected: noop, - onFinished: noop, - onTaskUpdate: noop, + onTestRunStart: noop, + onTestModuleCollected: noop, + onTestRunEnd: noop, onTestRemoved: noop, onWatcherStart: noop, onWatcherRerun: noop, diff --git a/test/cli/test/browser-multiple.test.ts b/test/cli/test/browser-multiple.test.ts index 48ed85ac9..ea6c53b2d 100644 --- a/test/cli/test/browser-multiple.test.ts +++ b/test/cli/test/browser-multiple.test.ts @@ -19,7 +19,7 @@ it('automatically assigns the port', async () => { onInit(ctx_) { ctx = ctx_ }, - onFinished() { + onTestRunEnd() { urls = ctx.projects.map(p => p.browser?.vite.resolvedUrls?.local[0]) }, }, diff --git a/test/cli/test/create-vitest.test.ts b/test/cli/test/create-vitest.test.ts index ab18bca6e..2419bc4bb 100644 --- a/test/cli/test/create-vitest.test.ts +++ b/test/cli/test/create-vitest.test.ts @@ -1,29 +1,28 @@ +import type { TestModule } from 'vitest/node' import { expect, it, vi } from 'vitest' import { createVitest } from 'vitest/node' it(createVitest, async () => { - const onFinished = vi.fn() + const onTestRunEnd = vi.fn() const ctx = await createVitest('test', { watch: false, root: 'fixtures/create-vitest', reporters: [ { - onFinished, + onTestRunEnd, }, ], }) const testFiles = await ctx.globTestSpecifications() await ctx.runTestSpecifications(testFiles, false) - expect(onFinished.mock.calls[0]).toMatchObject([ - [ - { - name: 'basic.test.ts', - result: { - state: 'pass', - }, - }, - ], - [], - undefined, - ]) + + const [testModules, errors, reason] = onTestRunEnd.mock.calls[0] + expect(testModules).toHaveLength(1) + + const testModule = testModules[0] + expect((testModule as TestModule).task?.name).toBe('basic.test.ts') + expect((testModule as TestModule).state()).toBe('passed') + + expect(errors).toHaveLength(0) + expect(reason).toBe('passed') }) diff --git a/test/cli/test/public-api.test.ts b/test/cli/test/public-api.test.ts index 35693dbd3..f7cb60ff4 100644 --- a/test/cli/test/public-api.test.ts +++ b/test/cli/test/public-api.test.ts @@ -1,5 +1,5 @@ -import type { RunnerTaskResultPack, RunnerTestFile } from 'vitest' -import type { TestUserConfig } from 'vitest/node' +import type { TaskMeta } from '@vitest/runner' +import type { TestModule, TestUserConfig } from 'vitest/node' import { resolve } from 'pathe' import { expect, it } from 'vitest' import { rolldownVersion } from 'vitest/node' @@ -15,23 +15,28 @@ it.each([ }, }, ] as TestUserConfig[])('passes down metadata when $name', { timeout: 60_000, retry: 1 }, async (config) => { - const taskUpdate: RunnerTaskResultPack[] = [] - const finishedFiles: RunnerTestFile[] = [] - const collectedFiles: RunnerTestFile[] = [] + const finishedTestCaseMetas: TaskMeta[] = [] + const finishedTestModuleMetas: TaskMeta[] = [] + + const finishedTestModules: TestModule[] = [] + const collectedTestModules: TestModule[] = [] const { ctx, stdout, stderr } = await runVitest({ root: resolve(__dirname, '..', 'fixtures', 'public-api'), include: ['**/*.spec.ts'], reporters: [ ['verbose', { isTTY: false }], { - onTaskUpdate(packs) { - taskUpdate.push(...packs.filter(i => i[1]?.state === 'pass')) + onTestCaseResult(testCase) { + finishedTestCaseMetas.push(testCase.meta()) }, - onFinished(files) { - finishedFiles.push(...files || []) + onTestModuleEnd(testModule) { + finishedTestModuleMetas.push(testModule.meta()) }, - onCollected(files) { - collectedFiles.push(...files || []) + onTestRunEnd(testModules) { + finishedTestModules.push(...testModules) + }, + onTestModuleCollected(testModule) { + collectedTestModules.push(testModule) }, }, ], @@ -46,39 +51,30 @@ it.each([ const suiteMeta = { done: true } const testMeta = { custom: 'some-custom-hanlder' } - expect(taskUpdate).toHaveLength(4) - expect(finishedFiles).toHaveLength(1) + expect(finishedTestCaseMetas).toHaveLength(3) + expect(finishedTestModuleMetas).toHaveLength(1) + expect(finishedTestModules).toHaveLength(1) const files = ctx?.state.getFiles() || [] expect(files).toHaveLength(1) - expect(taskUpdate).toContainEqual( - [ - expect.any(String), - expect.anything(), - suiteMeta, - ], - ) + expect(finishedTestModuleMetas).toContainEqual(suiteMeta) - expect(taskUpdate).toContainEqual( - [ - expect.any(String), - expect.anything(), - testMeta, - ], - ) + expect(finishedTestCaseMetas).toContainEqual(testMeta) - expect(finishedFiles[0].meta).toEqual(suiteMeta) - expect(finishedFiles[0].tasks[0].meta).toEqual(testMeta) + const test = finishedTestModules[0].children.tests().next().value! + + expect(finishedTestModules[0].meta()).toEqual(suiteMeta) + expect(test.meta()).toEqual(testMeta) expect(files[0].meta).toEqual(suiteMeta) expect(files[0].tasks[0].meta).toEqual(testMeta) - expect(finishedFiles[0].tasks[0].location).toEqual({ + expect(test.location).toEqual({ line: 14, column: 1, }) - expect(collectedFiles[0].tasks[0].location).toEqual({ + expect(collectedTestModules[0].task.tasks[0].location).toEqual({ line: 14, column: 1, }) diff --git a/test/cli/test/reported-tasks.test.ts b/test/cli/test/reported-tasks.test.ts index c4f66c583..594162456 100644 --- a/test/cli/test/reported-tasks.test.ts +++ b/test/cli/test/reported-tasks.test.ts @@ -7,8 +7,7 @@ import { beforeAll, expect, it } from 'vitest' import { runVitest } from '../../test-utils' const now = new Date() -// const finishedFiles: File[] = [] -const collectedFiles: RunnerTestFile[] = [] +const collectedTestModules: TestModule[] = [] let state: StateManager let project: WorkspaceProject let files: RunnerTestFile[] @@ -23,11 +22,8 @@ beforeAll(async () => { reporters: [ 'verbose', { - // onFinished(files) { - // finishedFiles.push(...files || []) - // }, - onCollected(files) { - collectedFiles.push(...files || []) + onTestModuleCollected(testModule) { + collectedTestModules.push(testModule) }, }, ], @@ -40,6 +36,7 @@ beforeAll(async () => { expect(files).toHaveLength(1) testModule = state.getReportedEntity(files[0])! as TestModule expect(testModule).toBeDefined() + expect(testModule).toBe(collectedTestModules[0]) }) it('correctly reports a file', () => { diff --git a/test/reporters/reportPkg/index.js b/test/reporters/reportPkg/index.js index 1f3c597a6..6c661fa90 100644 --- a/test/reporters/reportPkg/index.js +++ b/test/reporters/reportPkg/index.js @@ -3,7 +3,7 @@ export default class PackageReporter { this.ctx = ctx } - onFinished() { + onTestRunEnd() { this.ctx.logger.log('hello from package reporter') } } diff --git a/test/reporters/src/context.ts b/test/reporters/src/context.ts index 9c670e18e..aadd149c6 100644 --- a/test/reporters/src/context.ts +++ b/test/reporters/src/context.ts @@ -44,6 +44,7 @@ export function getContext(): Context { context.logger = { ctx: context as Vitest, log: (text: string) => output += `${text}\n`, + highlight: () => {}, } as unknown as Logger return { diff --git a/test/reporters/src/custom-reporter.js b/test/reporters/src/custom-reporter.js index 5c98cf7fa..47f271ed6 100644 --- a/test/reporters/src/custom-reporter.js +++ b/test/reporters/src/custom-reporter.js @@ -3,7 +3,7 @@ export default class TestReporter { this.ctx = ctx } - onFinished() { + onTestRunEnd() { this.ctx.logger.log('hello from custom reporter') } } diff --git a/test/reporters/src/custom-reporter.ts b/test/reporters/src/custom-reporter.ts index 77e037a54..fd8bc2b61 100644 --- a/test/reporters/src/custom-reporter.ts +++ b/test/reporters/src/custom-reporter.ts @@ -12,7 +12,7 @@ export default class TestReporter implements Reporter { this.ctx = ctx } - onFinished() { + onTestRunEnd() { this.ctx.logger.log('hello from custom reporter') if (this.options) { diff --git a/test/reporters/tests/configuration-options.test-d.ts b/test/reporters/tests/configuration-options.test-d.ts index 1890eebdd..a336b4abb 100644 --- a/test/reporters/tests/configuration-options.test-d.ts +++ b/test/reporters/tests/configuration-options.test-d.ts @@ -17,7 +17,7 @@ test('reporters, single', () => { assertType({ reporters: 'custom-reporter' }) assertType({ reporters: './reporter.mjs' }) - assertType({ reporters: { onFinished() {} } }) + assertType({ reporters: { onTestRunEnd() {} } }) }) test('reporters, multiple', () => { diff --git a/test/reporters/tests/console.test.ts b/test/reporters/tests/console.test.ts index be2005c0f..b261daf2a 100644 --- a/test/reporters/tests/console.test.ts +++ b/test/reporters/tests/console.test.ts @@ -7,7 +7,6 @@ import { runVitest } from '../../test-utils' class LogReporter extends DefaultReporter { isTTY = true - onTaskUpdate() {} } test('should print logs correctly', async () => { diff --git a/test/reporters/tests/reporters.spec.ts b/test/reporters/tests/reporters.spec.ts index b597d7727..d8dee2bd7 100644 --- a/test/reporters/tests/reporters.spec.ts +++ b/test/reporters/tests/reporters.spec.ts @@ -1,3 +1,4 @@ +import type { TestModule } from '../../../packages/vitest/src/node/reporters/reported-tasks' import { existsSync, readFileSync, rmSync } from 'node:fs' import { normalize, resolve } from 'pathe' import { beforeEach, expect, test, vi } from 'vitest' @@ -9,6 +10,9 @@ import { getContext } from '../src/context' import { files, passedFiles } from '../src/data' const beautify = (json: string) => JSON.parse(json) +function getTestModules(_files = files) { + return _files.map(task => ({ task }) as TestModule) +} vi.mock('os', () => ({ hostname: () => 'hostname', @@ -25,10 +29,11 @@ test('tap reporter', async () => { // Arrange const reporter = new TapReporter() const context = getContext() + const testModules = getTestModules() // Act reporter.onInit(context.vitest) - await reporter.onFinished(files) + reporter.onTestRunEnd(testModules) // Assert expect(context.output).toMatchSnapshot() @@ -38,10 +43,11 @@ test('tap-flat reporter', async () => { // Arrange const reporter = new TapFlatReporter() const context = getContext() + const testModules = getTestModules() // Act reporter.onInit(context.vitest) - await reporter.onFinished(files) + reporter.onTestRunEnd(testModules) // Assert expect(context.output).toMatchSnapshot() @@ -54,7 +60,7 @@ test('JUnit reporter', async () => { // Act await reporter.onInit(context.vitest) - await reporter.onFinished([]) + await reporter.onTestRunEnd([]) // Assert expect(context.output).toMatchSnapshot() @@ -64,11 +70,12 @@ test('JUnit reporter without classname', async () => { // Arrange const reporter = new JUnitReporter({}) const context = getContext() + const testModules = getTestModules(passedFiles) // Act await reporter.onInit(context.vitest) - await reporter.onFinished(passedFiles) + await reporter.onTestRunEnd(testModules) // Assert expect(context.output).toMatchSnapshot() @@ -78,11 +85,12 @@ test('JUnit reporter with custom string classname', async () => { // Arrange const reporter = new JUnitReporter({ classnameTemplate: 'my-custom-classname' }) const context = getContext() + const testModules = getTestModules(passedFiles) // Act await reporter.onInit(context.vitest) - await reporter.onFinished(passedFiles) + await reporter.onTestRunEnd(testModules) // Assert expect(context.output).toMatchSnapshot() @@ -92,11 +100,12 @@ test('JUnit reporter with custom function classnameTemplate', async () => { // Arrange const reporter = new JUnitReporter({ classnameTemplate: task => `filename:${task.filename} - filepath:${task.filepath}` }) const context = getContext() + const testModules = getTestModules(passedFiles) // Act await reporter.onInit(context.vitest) - await reporter.onFinished(passedFiles) + await reporter.onTestRunEnd(testModules) // Assert expect(context.output).toMatchSnapshot() @@ -105,11 +114,12 @@ test('JUnit reporter with custom string classnameTemplate', async () => { // Arrange const reporter = new JUnitReporter({ classnameTemplate: `filename:{filename} - filepath:{filepath}` }) const context = getContext() + const testModules = getTestModules(passedFiles) // Act await reporter.onInit(context.vitest) - await reporter.onFinished(passedFiles) + await reporter.onTestRunEnd(testModules) // Assert expect(context.output).toMatchSnapshot() @@ -123,7 +133,7 @@ test('JUnit reporter (no outputFile entry)', async () => { // Act await reporter.onInit(context.vitest) - await reporter.onFinished([]) + await reporter.onTestRunEnd([]) // Assert expect(context.output).toMatchSnapshot() @@ -138,7 +148,7 @@ test('JUnit reporter with outputFile', async () => { // Act await reporter.onInit(context.vitest) - await reporter.onFinished([]) + await reporter.onTestRunEnd([]) // Assert expect(normalizeCwd(context.output)).toMatchSnapshot() @@ -160,7 +170,7 @@ test('JUnit reporter with outputFile object', async () => { // Act await reporter.onInit(context.vitest) - await reporter.onFinished([]) + await reporter.onTestRunEnd([]) // Assert expect(normalizeCwd(context.output)).toMatchSnapshot() @@ -181,7 +191,7 @@ test('JUnit reporter with outputFile in non-existing directory', async () => { // Act await reporter.onInit(context.vitest) - await reporter.onFinished([]) + await reporter.onTestRunEnd([]) // Assert expect(normalizeCwd(context.output)).toMatchSnapshot() @@ -204,7 +214,7 @@ test('JUnit reporter with outputFile object in non-existing directory', async () // Act await reporter.onInit(context.vitest) - await reporter.onFinished([]) + await reporter.onTestRunEnd([]) // Assert expect(normalizeCwd(context.output)).toMatchSnapshot() @@ -219,12 +229,13 @@ test('json reporter', async () => { // Arrange const reporter = new JsonReporter({}) const context = getContext() + const testModules = getTestModules() vi.setSystemTime(1642587001759) // Act reporter.onInit(context.vitest) - await reporter.onFinished(files) + await reporter.onTestRunEnd(testModules) // Assert expect(JSON.parse(context.output)).toMatchSnapshot() @@ -235,12 +246,13 @@ test('json reporter (no outputFile entry)', async () => { const reporter = new JsonReporter({}) const context = getContext() context.vitest.config.outputFile = {} + const testModules = getTestModules() vi.setSystemTime(1642587001759) // Act reporter.onInit(context.vitest) - await reporter.onFinished(files) + await reporter.onTestRunEnd(testModules) // Assert expect(JSON.parse(context.output)).toMatchSnapshot() @@ -252,12 +264,13 @@ test('json reporter with outputFile', async () => { const outputFile = resolve('report.json') const context = getContext() context.vitest.config.outputFile = outputFile + const testModules = getTestModules() vi.setSystemTime(1642587001759) // Act reporter.onInit(context.vitest) - await reporter.onFinished(files) + await reporter.onTestRunEnd(testModules) // Assert expect(normalizeCwd(context.output)).toMatchSnapshot() @@ -276,12 +289,13 @@ test('json reporter with outputFile object', async () => { context.vitest.config.outputFile = { json: outputFile, } + const testModules = getTestModules() vi.setSystemTime(1642587001759) // Act reporter.onInit(context.vitest) - await reporter.onFinished(files) + await reporter.onTestRunEnd(testModules) // Assert expect(normalizeCwd(context.output)).toMatchSnapshot() @@ -299,12 +313,13 @@ test('json reporter with outputFile in non-existing directory', async () => { const outputFile = `${rootDirectory}/deeply/nested/report.json` const context = getContext() context.vitest.config.outputFile = outputFile + const testModules = getTestModules() vi.setSystemTime(1642587001759) // Act reporter.onInit(context.vitest) - await reporter.onFinished(files) + await reporter.onTestRunEnd(testModules) // Assert expect(normalizeCwd(context.output)).toMatchSnapshot() @@ -324,12 +339,13 @@ test('json reporter with outputFile object in non-existing directory', async () context.vitest.config.outputFile = { json: outputFile, } + const testModules = getTestModules() vi.setSystemTime(1642587001759) // Act reporter.onInit(context.vitest) - await reporter.onFinished(files) + await reporter.onTestRunEnd(testModules) // Assert expect(normalizeCwd(context.output)).toMatchSnapshot() diff --git a/test/reporters/tests/silent.test.ts b/test/reporters/tests/silent.test.ts index eb98e6171..72a530802 100644 --- a/test/reporters/tests/silent.test.ts +++ b/test/reporters/tests/silent.test.ts @@ -101,5 +101,4 @@ Log from failed suite`, class LogReporter extends DefaultReporter { isTTY = true - onTaskUpdate() {} } diff --git a/test/reporters/tests/tap.test.ts b/test/reporters/tests/tap.test.ts index 51cdf518c..beacbb4b6 100644 --- a/test/reporters/tests/tap.test.ts +++ b/test/reporters/tests/tap.test.ts @@ -2,6 +2,44 @@ import { expect, test } from 'vitest' import { runVitest } from '../../test-utils' test('handle custom error without name', async () => { + let { stdout, stderr } = await runVitest({ reporters: 'tap', root: './fixtures/custom-error' }) + stdout = stdout.replaceAll(/time=(\S*)/g, 'time=[...]') // strip non-deterministic output + expect(stdout).toMatchInlineSnapshot(` + "TAP version 13 + 1..1 + not ok 1 - basic.test.ts # time=[...] { + 1..4 + not ok 1 - no name object # time=[...] + --- + error: + name: "Unknown Error" + message: "undefined" + ... + not ok 2 - string # time=[...] + --- + error: + name: "Unknown Error" + message: "hi" + ... + not ok 3 - number # time=[...] + --- + error: + name: "Unknown Error" + message: "1234" + ... + not ok 4 - number name object # time=[...] + --- + error: + name: "1234" + message: "undefined" + ... + } + " + `) + expect(stderr).toBe('') +}) + +test('tap-flat handles custom error without name', async () => { let { stdout, stderr } = await runVitest({ reporters: 'tap-flat', root: './fixtures/custom-error' }) stdout = stdout.replaceAll(/time=(\S*)/g, 'time=[...]') // strip non-deterministic output expect(stdout).toMatchInlineSnapshot(`