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
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -284,7 +284,7 @@ jobs:

- uses: actions/download-artifact@v4
with:
pattern: vitest-blob-*
pattern: vitest-results-*
merge-multiple: true

- name: Merge reports
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down
2 changes: 1 addition & 1 deletion docs/api/advanced/vitest.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ This method can be slow because it needs to filter `--changed` flags. Do not use
function mergeReports(directory?: string): Promise<TestRunResult>
```

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.

Expand Down
2 changes: 1 addition & 1 deletion docs/guide/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 5 additions & 22 deletions docs/guide/improving-performance.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Comment thread
AriPerkkio marked this conversation as resolved.
include-hidden-files: true
retention-days: 1

Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/reporters.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/node/cli/cli-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
Expand Down
56 changes: 29 additions & 27 deletions packages/vitest/src/node/reporters/blob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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] = {
Expand Down Expand Up @@ -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<void> {
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(
Expand Down Expand Up @@ -223,7 +225,7 @@ export interface MergedBlobs {
executionTimes: number[]
}

type MergeReport = [
export type MergeReport = [
vitestVersion: string,
files: File[],
errors: unknown[],
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/node/types/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1102,7 +1102,7 @@ export interface UserConfig extends InlineConfig {

/**
* Directory of blob reports to merge
* @default '.vitest-reports'
* @default '.vitest/blob'
*/
mergeReports?: string

Expand Down
2 changes: 1 addition & 1 deletion test/config/test/failures.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
})
Expand Down
2 changes: 1 addition & 1 deletion test/coverage-test/test/merge-reports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
},
Expand Down
37 changes: 25 additions & 12 deletions test/e2e/test/reporters/merge-reports.test.ts
Original file line number Diff line number Diff line change
@@ -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 })
Expand All @@ -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({
Expand Down Expand Up @@ -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 }]],
})

Expand All @@ -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 = {
Expand Down Expand Up @@ -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(`""`)
Expand Down Expand Up @@ -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 <time>
Expand Down Expand Up @@ -788,7 +790,7 @@ test("works on browser", () => {
`)
const result = await runVitest({
...baseConfig,
mergeReports: resolve(root, '.vitest-reports'),
mergeReports: resolve(root, '.vitest/blob'),
})
expect(trimReporterOutput(result.stdout)).toMatchInlineSnapshot(`
"✓ |node| linux basic.test.ts > always good <time>
Expand Down Expand Up @@ -956,3 +958,14 @@ test("top-test", () => {})
}
`)
})

async function writeBlob(content: MergeReport, filename: string): Promise<void> {
const report = stringify(content)

const dir = dirname(filename)
if (!existsSync(dir)) {
await mkdir(dir, { recursive: true })
}

await writeFile(filename, report, 'utf-8')
}
2 changes: 1 addition & 1 deletion test/e2e/test/reporters/reporter-error.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { runVitest } from '#test-utils'
import { beforeEach, describe, expect, test } from 'vitest'

const root = './fixtures/reporters/reporter-error'
const reportsDir = resolve(root, '.vitest-reports')
const reportsDir = resolve(root, '.vitest/blob')

beforeEach(() => {
rmSync(reportsDir, { force: true, recursive: true })
Expand Down
2 changes: 1 addition & 1 deletion test/unit/test/cli-test.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ test('clearScreen', async (ctx) => {
})

test('merge-reports', () => {
expect(getCLIOptions('--merge-reports')).toEqual({ mergeReports: '.vitest-reports' })
expect(getCLIOptions('--merge-reports')).toEqual({ mergeReports: '.vitest/blob' })
expect(getCLIOptions('--merge-reports=different-folder')).toEqual({ mergeReports: 'different-folder' })
expect(getCLIOptions('--merge-reports different-folder')).toEqual({ mergeReports: 'different-folder' })
})
Expand Down
Loading