[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

fix: make `attachmentsDir` root only config (#10334)

Co-authored-by: Codex <noreply@openai.com>

authored by

Hiroshi Ogawa
Codex
and committed by
GitHub
(May 13, 2026, 9:08 AM +0200) fab1b602 c8a43c1b

+58 -2
+4 -2
docs/config/attachmentsdir.md
··· 3 3 outline: deep 4 4 --- 5 5 6 - # attachmentsDir 6 + # attachmentsDir <CRoot /> 7 7 8 8 - **Type:** `string` 9 9 - **Default:** `'.vitest/attachments'` 10 10 11 - Directory path for storing attachments created by [`context.annotate`](/guide/test-context#annotate) relative to the project root. 11 + Directory path for storing file attachments created by [`context.annotate`](/guide/test-context#annotate). 12 + 13 + This option is resolved relative to the root Vitest config. When using [`projects`](/guide/projects), all projects share the same `attachmentsDir`; it cannot be configured per project.
+1
docs/guide/projects.md
··· 289 289 - `coverage`: coverage is done for the whole process 290 290 - `reporters`: only root-level reporters can be supported 291 291 - `resolveSnapshotPath`: only root-level resolver is respected 292 + - `attachmentsDir`: attachments are stored in one root-level directory shared by all projects 292 293 - all other options that don't affect test runners 293 294 294 295 All configuration options that are not supported inside a project configuration are marked with a <CRoot /> icon next to their name. They can only be defined once in the root config file.
+51
test/e2e/test/annotations.test.ts
··· 1 1 import type { TestArtifact } from '@vitest/runner' 2 2 import type { TestAnnotation } from 'vitest' 3 + import { readdirSync } from 'node:fs' 4 + import path from 'node:path' 3 5 import { playwright } from '@vitest/browser-playwright' 4 6 import { describe, expect, test } from 'vitest' 5 7 import { runInlineTests } from '../../test-utils' ··· 664 666 `) 665 667 }) 666 668 }) 669 + }) 670 + 671 + test('attachmentsDir is root only', async () => { 672 + const result = await runInlineTests( 673 + { 674 + 'packages/client/basic.test.ts': ` 675 + import { test } from 'vitest' 676 + test("hello", ({ annotate }) => { 677 + annotate("hello annotation", { path: "./hello.txt" }) 678 + }) 679 + `, 680 + 'packages/client/hello.txt': `HELLO`, 681 + 'packages/server/basic.test.ts': ` 682 + import { test } from 'vitest' 683 + test("world", ({ annotate }) => { 684 + annotate("world annotation", { path: "./world.txt" }) 685 + }) 686 + `, 687 + 'packages/server/world.txt': `WORLD`, 688 + }, 689 + { 690 + projects: ['./packages/*'], 691 + }, 692 + ) 693 + expect(result.stderr).toMatchInlineSnapshot(`""`) 694 + expect(result.errorTree({ project: true })).toMatchInlineSnapshot(` 695 + { 696 + "client": { 697 + "basic.test.ts": { 698 + "hello": "passed", 699 + }, 700 + }, 701 + "server": { 702 + "basic.test.ts": { 703 + "world": "passed", 704 + }, 705 + }, 706 + } 707 + `) 708 + const files = readdirSync(path.join(result.root, '.vitest/attachments')) 709 + const contents = files.sort().map(file => 710 + result.fs.readFile(path.join('.vitest/attachments', file)), 711 + ) 712 + expect(contents).toMatchInlineSnapshot(` 713 + [ 714 + "HELLO", 715 + "WORLD", 716 + ] 717 + `) 667 718 })
+2
packages/vitest/src/node/project.ts
··· 550 550 this.vitest, 551 551 { 552 552 ...options, 553 + // root-only configs 553 554 coverage: this.vitest.config.coverage, 555 + attachmentsDir: this.vitest.config.attachmentsDir, 554 556 }, 555 557 server.config, 556 558 )