feat(reporters)!: write json and junit reporter output files to .vitest by default#10621
Conversation
The `json` and `junit` reporters now write to `.vitest/json/output.json` and `.vitest/junit/output.xml` when no `outputFile` is configured, instead of printing to stdout. This aligns them with the `.vitest/` artifact convention and the `blob` reporter, and produces an on-disk artifact by default. Explicit `outputFile` is unchanged. BREAKING CHANGE: json/junit reporters no longer print to stdout by default. Read the generated artifact file instead (e.g. `jq . .vitest/json/output.json`). Closes vitest-dev#10619 AI-Agent: Opencode
✅ Deploy Preview for vitest-dev ready!Built without sensitive environment variables
To edit notification comments on pull requests, go to your Netlify project configuration. |
Add a migration guide section documenting that the json and junit reporters now write to `.vitest/json/output.json` and `.vitest/junit/output.xml` by default instead of printing to stdout. Also revert the outputFile config doc, which is handled separately. AI-Agent: Opencode
AI-Agent: Opencode
The html reporter location change is handled separately; keep its existing outputFile behavior and scope the .vitest defaults to the json and junit reporters only. AI-Agent: Opencode
AI-Agent: Opencode
|
|
||
| ### Generated Reports and Artifacts Use the `.vitest` Directory | ||
|
|
||
| Vitest now uses a single `.vitest` directory at the project root as the shared artifact root. The `json` and `junit` reporters now write to this directory by default instead of printing to stdout: |
There was a problem hiding this comment.
What is wrong with printing to stdout? Why do we remove this completely?
There was a problem hiding this comment.
For the use case I understand, they look effectively useless and misguided. See #10619
In practice, "print to stdout" pushes users into awkward workarounds to get the file they actually wanted. Common patterns in the wild:
- Redirect to a file, then upload as a CI artifact:
vitest run --reporter=json > vitest-report.jsonthen upload it (e.g. mizchi/flaker.github/workflows/ci.yml, which redirects → uploads → re-imports the report in a later job).- Pipe into a post-processor:
vitest --reporter=json | jq '.numFailedTests'.- Redirect a bench/report run:
vitest bench --run --reporter=json > benchmark-results.json(e.g. thibmeu/act-ts).A scoped
.vitest/default serves all of these better, with zero config:
- Artifact upload: just run
vitest --reporter=jsonand upload.vitest/json/output.json— no redirect.- Post-analysis:
jq '.numFailedTests' .vitest/json/output.json— and it's re-runnable without re-running the tests, since the artifact persists.So the stdout default isn't a feature these workflows rely on; it's the limitation they work around. Moving to
.vitest/removes the workaround.
There was a problem hiding this comment.
Did we discuss maybe supporting outputFile: "-" to mean stdout?
There was a problem hiding this comment.
So the stdout default isn't a feature these workflows rely on; it's the limitation they work around. Moving to .vitest/ removes the workaround.
This seems weird - if it's so much better to keep a file, why don't they use --outputFile and read from it? Just because it's not a default?
There was a problem hiding this comment.
So the stdout default isn't a feature these workflows rely on; it's the limitation they work around. Moving to .vitest/ removes the workaround.
This seems weird - if it's so much better to keep a file, why don't they use
--outputFileand read from it? Just because it's not a default?
This is just my educated guess and basically my biased opinion.
There was a problem hiding this comment.
Also I prefer the file because direct stdout writing such as process.stdout.write can break stdout redirection approach (or console.log in main process like global setup).
These suites asserted on stdout, which no longer carries the junit report now that it is written to .vitest/junit/output.xml by default. AI-Agent: Opencode
The default-mode json/junit reporter tests asserted on the captured logger output, which now only contains the "report written" log line. Teach the mock getContext() to provide createReport() backed by a temp directory and read the generated artifact instead. AI-Agent: Opencode
# Conflicts: # docs/guide/migration.md # docs/guide/reporters.md
AI-Agent: Opencode
The rollup-error e2e suite only needed a sink to grep the surfaced resolve errors; the default reporter prints them to stderr, so drop the junit reporter dependency (and the .vitest report file read) and assert the unescaped error text directly. AI-Agent: Opencode
AI-Agent: Opencode
AriPerkkio
left a comment
There was a problem hiding this comment.
Looks good to me. I don't think stdout as JSON or Junit output ever made sense. The output could have contained Node's and Vitest's deprecation warnings, so parsing the XML/JSON from there would be difficult.
| - `junit` writes `.vitest/junit/output.xml` | ||
| - `html` writes `.vitest/index.html` | ||
|
|
||
| The `json` and `junit` locations can be overridden with the `outputFile` [configuration option](/config/outputfile) either in your Vite configuration file or via CLI; the `html` reporter uses its [`outputDir`](#html-reporter) option instead. |
There was a problem hiding this comment.
Do we mention outputFile reporter option anywhere?
vitest/packages/vitest/src/node/reporters/junit.ts
Lines 50 to 51 in c409014
I would rather start pointing users to configure this via reporter options and remove whole top level outputFile in future.
There was a problem hiding this comment.
It looks like there was never a mention of ['json', { outputFile: ... }] nor junit. Will add some in the doc.
I agree that curent ouptutFile option system are rather scattered, but not sure we can drop whole top level one. Before dropping it, probably we need reporter option from cli like --reporter.json.outputFile=... somehow.
There was a problem hiding this comment.
tweaked docs and created cli reporter option request #10650
| this.baseLog = async (text: string) => { | ||
| if (!this.fileFd) { | ||
| this.fileFd = await fs.open(this.reportFile!, 'w+') | ||
| } | ||
| } | ||
| else { | ||
| this.baseLog = async (text: string) => this.ctx.logger.log(text) | ||
|
|
||
| await fs.writeFile(this.fileFd, `${text}\n`) |
There was a problem hiding this comment.
Out-of-scope of this PR, but probably good refactoring for future:
I'm not exactly sure why this reporter was built to stream its results into the output file, while everything is still handled in onTestFinished. Maybe to avoid OOMs? Anyway, we could move this whole fileId initialization in onTestFinished and keep it in function scope there.
warnings are in stderr though. jest actually forwards every log to stderr so that json reporter is parsable |
How comprehensive is this though? Does any user code on main process stdout gets collected and redirected to stderr like global setup? |
Co-authored-by: OpenCode (claude-opus-4-8) <[email protected]>
|
This argument feels weird to me thought. I am not against changing the default, but I am unsure why we need to take away the stdout output. It feels like a natural programmatic way to use it in a terminal - just because user can manually break it doesn’t mean it’s vitest fault. We allow custom reporters even though they can throw an error. |
|
I would keep it as —stdout flag or something. Maybe —reporter.json.stdout like Ari wanted for other reporter options |
AriPerkkio
left a comment
There was a problem hiding this comment.
If reading JSON and XML from terminal output is expected use case, let's keep it. I cannot see when it would be useful though.
|
I also suggested this thing |
Allow opting back into printing the json/junit report to the terminal via a per-reporter `stdout` option, now that the default writes to a file under `.vitest/`. Ignored when `outputFile` is set. Co-authored-by: OpenCode (claude-opus-4-8) <[email protected]>
Co-authored-by: OpenCode (claude-opus-4-8) <[email protected]>
Co-authored-by: OpenCode (claude-opus-4-8) <[email protected]>
Revert the createReport temp-dir plumbing in getContext() now that the json/junit reporters support a `stdout` option, and drop the redundant explicit default reporter in rollup-error e2e tests. Co-authored-by: OpenCode (claude-opus-4-8) <[email protected]>
Co-authored-by: Ari Perkkiö <[email protected]>

Description
jsonandjunitreporters default output use.vitestas the report artifact root #10619Please don't delete this checklist! Before submitting the PR, please make sure you do the following:
pnpm-lock.yamlunless you introduce a new test example.Tests
pnpm test:ci.Documentation
pnpm run docscommand.Changesets
feat:,fix:,perf:,docs:, orchore:.