diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b293ac715..85dc8ba1b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -135,11 +135,11 @@ jobs: - uses: actions/upload-artifact@v7 if: ${{ !cancelled() }} with: - name: vitest-blob-${{ matrix.os }}-node-${{ matrix.node_version }} + name: vitest-results-${{ matrix.os }}-node-${{ matrix.node_version }} path: | README.md - test/unit/.vitest-reports - test/e2e/.vitest-reports + test/unit/.vitest + test/e2e/.vitest retention-days: 1 include-hidden-files: true @@ -284,7 +284,7 @@ jobs: - uses: actions/download-artifact@v4 with: - pattern: vitest-blob-* + pattern: vitest-results-* merge-multiple: true - name: Merge reports diff --git a/.gitignore b/.gitignore index 4915cb112..f1b344b6a 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,6 @@ test/**/__screenshots__/**/* test/**/__traces__/**/* test/browser/fixtures/update-snapshot/basic.test.ts test/e2e/fixtures/browser-multiple/basic-* -.vitest-reports *.tsbuildinfo # exclude static html reporter folder test/browser/html/ diff --git a/docs/api/advanced/vitest.md b/docs/api/advanced/vitest.md index 0f4365910..7ff17b48e 100644 --- a/docs/api/advanced/vitest.md +++ b/docs/api/advanced/vitest.md @@ -202,7 +202,7 @@ This method can be slow because it needs to filter `--changed` flags. Do not use function mergeReports(directory?: string): Promise ``` -Merge reports from multiple runs located in the specified directory (value from `--merge-reports` if not specified). This value can also be set on `config.mergeReports` (by default, it will read `.vitest-reports` folder). +Merge reports from multiple runs located in the specified directory (value from `--merge-reports` if not specified). This value can also be set on `config.mergeReports` (by default, it will read `.vitest/blob/` folder). Note that the `directory` will always be resolved relative to the working directory. diff --git a/docs/guide/cli.md b/docs/guide/cli.md index 6bcad4cf1..92bef00cf 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -232,7 +232,7 @@ If `--reporter=blob` is used without an output file, the default path will inclu - **Type:** `boolean | string` -Merges every blob report located in the specified folder (`.vitest-reports` by default). You can use any reporters with this command (except [`blob`](/guide/reporters#blob-reporter)): +Merges every blob report located in the specified folder (`.vitest/blob/` by default). You can use any reporters with this command (except [`blob`](/guide/reporters#blob-reporter)): ```sh vitest --merge-reports --reporter=junit diff --git a/docs/guide/improving-performance.md b/docs/guide/improving-performance.md index 2541eed93..af0db7882 100644 --- a/docs/guide/improving-performance.md +++ b/docs/guide/improving-performance.md @@ -126,7 +126,7 @@ vitest run --reporter=blob --shard=3/3 # 3rd machine > Vitest splits your _test files_, not your test cases, into shards. If you've got 1000 test files, the `--shard=1/4` option will run 250 test files, no matter how many test cases individual files have. -Collect the results stored in `.vitest-reports` directory from each machine and merge them with [`--merge-reports`](/guide/cli#merge-reports) option: +Collect the results stored in `.vitest/blob/` directory from each machine and merge them with [`--merge-reports`](/guide/cli#merge-reports) option: ```sh vitest run --merge-reports @@ -173,21 +173,12 @@ jobs: env: VITEST_BLOB_LABEL: ${{ matrix.os }} - - name: Upload blob report to GitHub Actions Artifacts + - name: Upload Vitest results GitHub Actions Artifacts if: ${{ !cancelled() }} uses: actions/upload-artifact@v4 with: - name: blob-report-${{ matrix.os }}-${{ matrix.shardIndex }} - path: .vitest-reports/* - include-hidden-files: true - retention-days: 1 - - - name: Upload attachments to GitHub Actions Artifacts - if: ${{ !cancelled() }} - uses: actions/upload-artifact@v4 - with: - name: blob-attachments-${{ matrix.os }}-${{ matrix.shardIndex }} - path: .vitest/** + name: vitest-results-${{ matrix.os }}-${{ matrix.shardIndex }} + path: .vitest include-hidden-files: true retention-days: 1 @@ -208,18 +199,10 @@ jobs: - name: Install dependencies run: pnpm i - - name: Download blob reports from GitHub Actions Artifacts - uses: actions/download-artifact@v4 - with: - path: .vitest-reports - pattern: blob-report-* - merge-multiple: true - - - name: Download attachments from GitHub Actions Artifacts + - name: Download Vitest results from GitHub Actions Artifacts uses: actions/download-artifact@v4 with: path: .vitest - pattern: blob-attachments-* merge-multiple: true - name: Merge reports diff --git a/docs/guide/reporters.md b/docs/guide/reporters.md index 8aafe8dc7..0eb70bc1e 100644 --- a/docs/guide/reporters.md +++ b/docs/guide/reporters.md @@ -742,7 +742,7 @@ export default defineConfig({ ### Blob Reporter Stores test results on the machine so they can be later merged using [`--merge-reports`](/guide/cli#merge-reports) command. -By default, stores all results in `.vitest-reports` folder, but can be overridden with `--outputFile` or `--outputFile.blob` flags. +By default, stores all results in `.vitest/blob/` folder, but can be overridden with `--outputFile` or `--outputFile.blob` flags. ```bash npx vitest --reporter=blob --outputFile=reports/blob-1.json diff --git a/packages/vitest/src/node/cli/cli-config.ts b/packages/vitest/src/node/cli/cli-config.ts index fab428d3f..f73620591 100644 --- a/packages/vitest/src/node/cli/cli-config.ts +++ b/packages/vitest/src/node/cli/cli-config.ts @@ -855,7 +855,7 @@ export const cliOptionsConfig: VitestCLIOptions = { argument: '[path]', transform(value) { if (!value || typeof value === 'boolean') { - return '.vitest-reports' + return '.vitest/blob' } return value }, diff --git a/packages/vitest/src/node/reporters/blob.ts b/packages/vitest/src/node/reporters/blob.ts index 036fbdbf5..5a9da1224 100644 --- a/packages/vitest/src/node/reporters/blob.ts +++ b/packages/vitest/src/node/reporters/blob.ts @@ -48,18 +48,6 @@ export class BlobReporter implements Reporter { const errors = [...unhandledErrors] const coverage = this.coverage - let outputFile - = this.options.outputFile ?? getOutputFile(this.ctx.config, 'blob') - if (!outputFile) { - const shard = this.ctx.config.shard - const filename = [ - 'blob', - this.ctx.config.mergeReportsLabel, - shard && `${shard.index}-${shard.count}`, - ].filter(Boolean).join('-') - outputFile = `.vitest-reports/${sanitizeFilePath(filename)}.json` - } - const environmentModules: MergeReportEnvironmentModules = {} this.ctx.projects.forEach((project) => { const serializedProject: MergeReportEnvironmentModules[string] = { @@ -87,31 +75,45 @@ export class BlobReporter implements Reporter { environmentModules[project.name] = serializedProject }) - const report = [ + const content = stringify([ this.ctx.version, files, errors, coverage, executionTime, environmentModules, - ] satisfies MergeReport + ] satisfies MergeReport) - const reportFile = resolve(this.ctx.config.root, outputFile) - await writeBlob(report, reportFile) + let outputFile = this.options.outputFile ?? getOutputFile(this.ctx.config, 'blob') - this.ctx.logger.log('blob report written to', reportFile) - } -} + if (outputFile) { + outputFile = resolve(this.ctx.config.root, outputFile) -export async function writeBlob(content: MergeReport, filename: string): Promise { - const report = stringify(content) + const dir = dirname(outputFile) + if (!existsSync(dir)) { + await mkdir(dir, { recursive: true }) + } - const dir = dirname(filename) - if (!existsSync(dir)) { - await mkdir(dir, { recursive: true }) - } + await writeFile(outputFile, content, 'utf-8') + } + else { + const report = this.ctx.createReport('blob') + + const shard = this.ctx.config.shard + outputFile = [ + 'blob', + this.ctx.config.mergeReportsLabel, + shard ? `-${shard.index}-${shard.count}` : '', + ].join('') - await writeFile(filename, report, 'utf-8') + outputFile = `${sanitizeFilePath(outputFile)}.json` + + await report.writeFile(outputFile, content, 'utf-8') + outputFile = resolve(report.root, outputFile) + } + + this.ctx.logger.log('blob report written to', outputFile) + } } export async function readBlobs( @@ -223,7 +225,7 @@ export interface MergedBlobs { executionTimes: number[] } -type MergeReport = [ +export type MergeReport = [ vitestVersion: string, files: File[], errors: unknown[], diff --git a/packages/vitest/src/node/types/config.ts b/packages/vitest/src/node/types/config.ts index 2d7d99458..fe77fb756 100644 --- a/packages/vitest/src/node/types/config.ts +++ b/packages/vitest/src/node/types/config.ts @@ -1102,7 +1102,7 @@ export interface UserConfig extends InlineConfig { /** * Directory of blob reports to merge - * @default '.vitest-reports' + * @default '.vitest/blob' */ mergeReports?: string diff --git a/test/config/test/failures.test.ts b/test/config/test/failures.test.ts index cecaf6124..c57656432 100644 --- a/test/config/test/failures.test.ts +++ b/test/config/test/failures.test.ts @@ -379,7 +379,7 @@ test('nextTick can be mocked inside worker_threads', async () => { }) test('mergeReports doesn\'t work with watch mode enabled', async () => { - const { stderr } = await runVitest({ watch: true, mergeReports: '.vitest-reports' }) + const { stderr } = await runVitest({ watch: true, mergeReports: '.vitest/blob' }) expect(stderr).toMatch('Cannot merge reports with --watch enabled') }) diff --git a/test/coverage-test/test/merge-reports.test.ts b/test/coverage-test/test/merge-reports.test.ts index fc4314c67..766f4e1fe 100644 --- a/test/coverage-test/test/merge-reports.test.ts +++ b/test/coverage-test/test/merge-reports.test.ts @@ -12,7 +12,7 @@ test('--merge-reports', async () => { await runVitest({ // Pass default value - this option is publicly only available via CLI so it's a bit hacky usage here - mergeReports: '.vitest-reports', + mergeReports: '.vitest/blob', coverage: { reporter: 'json', }, diff --git a/test/e2e/test/reporters/merge-reports.test.ts b/test/e2e/test/reporters/merge-reports.test.ts index d7ec8b618..c7d42e013 100644 --- a/test/e2e/test/reporters/merge-reports.test.ts +++ b/test/e2e/test/reporters/merge-reports.test.ts @@ -1,19 +1,21 @@ import type { RunVitestConfig } from '#test-utils' import type { File, Test } from '@vitest/runner/types' import type { TestUserConfig, Vitest } from 'vitest/node' -import { rmSync } from 'node:fs' -import { resolve } from 'node:path' +import type { MergeReport } from 'vitest/src/node/reporters/blob.js' +import { existsSync, rmSync } from 'node:fs' +import { mkdir, writeFile } from 'node:fs/promises' import { buildTestTree, runVitest, useFS } from '#test-utils' import { playwright } from '@vitest/browser-playwright' import { createFileTask } from '@vitest/runner/utils' +import { stringify } from 'flatted' +import { dirname, resolve } from 'pathe' import { beforeEach, expect, test } from 'vitest' import { version } from 'vitest/package.json' -import { writeBlob } from 'vitest/src/node/reporters/blob.js' import { getModuleGraph } from 'vitest/src/utils/graph.js' // always relative to CWD because it's used only from the CLI, // so we need to correctly resolve it here -const reportsDir = resolve('./fixtures/reporters/merge-reports/.vitest-reports') +const reportsDir = resolve('./fixtures/reporters/merge-reports/.vitest/blob') beforeEach(() => { rmSync(reportsDir, { force: true, recursive: true }) @@ -23,13 +25,13 @@ test('merge reports', async () => { await runVitest({ root: './fixtures/reporters/merge-reports', include: ['first.test.ts'], - reporters: [['blob', { outputFile: './.vitest-reports/first-run.json' }]], + reporters: [['blob', { outputFile: './.vitest/blob/first-run.json' }]], }) await runVitest({ root: './fixtures/reporters/merge-reports', include: ['second.test.ts'], - reporters: [['blob', { outputFile: './.vitest-reports/second-run.json' }]], + reporters: [['blob', { outputFile: './.vitest/blob/second-run.json' }]], }) const { stdout: reporterDefault, stderr: stderrDefault, exitCode } = await runVitest({ @@ -259,13 +261,13 @@ test('total and merged execution times are shown', async () => { await writeBlob( [version, [file], [], undefined, 1500 * index, {}], - resolve(`./fixtures/reporters/merge-reports/.vitest-reports/blob-${index}-2.json`), + resolve(`./fixtures/reporters/merge-reports/.vitest/blob/blob-${index}-2.json`), ) } const { stdout } = await runVitest({ root: resolve('./fixtures/reporters/merge-reports'), - mergeReports: resolve('./fixtures/reporters/merge-reports/.vitest-reports'), + mergeReports: resolve('./fixtures/reporters/merge-reports/.vitest/blob'), reporters: [['default', { isTTY: false }]], }) @@ -281,7 +283,7 @@ test.for([ 'browser', ])('module graph and html reporter $0', async (mode) => { const root = resolve('./fixtures/reporters/merge-reports-module-graph') - const reportsDir = resolve(root, '.vitest-reports') + const reportsDir = resolve(root, '.vitest/blob') rmSync(reportsDir, { force: true, recursive: true }) const baseConfig: TestUserConfig = { @@ -414,7 +416,7 @@ test.for([ const result3 = await runVitest({ ...baseConfig, - mergeReports: resolve(root, '.vitest-reports'), + mergeReports: resolve(root, '.vitest/blob'), reporters: ['html'], }) expect(result3.stderr).toMatchInlineSnapshot(`""`) @@ -592,7 +594,7 @@ test("macos only", () => {}) `) const result = await runVitest({ root, - mergeReports: resolve(root, '.vitest-reports'), + mergeReports: resolve(root, '.vitest/blob'), }) expect(trimReporterOutput(result.stdout)).toMatchInlineSnapshot(` "✓ linux first.test.ts > always good