Vitest Testing
This skill equips you to configure, run, debug, and review a Vitest suite: the runner's own surface — its config file, its vi API, its pools and reporters, its coverage providers, and its Browser Mode — plus the operational rules an agent needs to drive it without hanging a session or burning a context window.
It is the runner-specific layer. A tool-agnostic unit-testing capability owns what to assert, how to name a spec, what makes a fixture good, and whether a behavior deserves a unit test at all; those questions have the same answers whatever runner executes them. This skill owns the mechanism underneath: which option, which file, which vi call, which flag. Where a rule here touches judgment — which dependency is worth mocking, how much of a payload to pin, whether a snapshot earns its place — it points at the tool-agnostic owner rather than answering it.
Two neighbours are disclaimed explicitly. An end-to-end capability owns user journeys, the locator fallback hierarchy, server lifecycle, and scenario coverage even when the runner underneath is Vitest; this skill owns the runner configuration that suite runs on. A component-development capability owns test-hook conventions such as data-testid and testID; this skill owns the Browser Mode machinery that queries them. A project on a different runner should reach for that runner's own capability, not this one.
Version posture is lookup-first. Rules are verified against Vitest 4.1.10 (Node >= 20, Vite >= 6) and name the release they were checked against. Vitest 4 moved or removed roughly two dozen configuration options and silently ignores the old names rather than erroring, so a v3-era answer copied off the open web fails invisibly — the suite runs, the option does nothing. Wherever a surface is known to have moved, consult the installed version's own documentation at https://vitest.dev rather than recalling an option name; each reference below names the upstream page it was verified against.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
Running Vitest as an Agent
See running-as-an-agent.md for:
vitest run or --no-watch for every non-interactive run; bare vitest enters watch mode and never exits
- a project
test script defined as vitest or vitest --watch, which hands that hang to anyone who runs it
- narrowing with a path,
-t, file.test.ts:42, --project, --changed, or --related
- the
minimal reporter's token cost argument, and the custom reporters array that disables its auto-selection
- reading the reported diff and stack frame instead of re-running the suite to see the failure again
- third-party Vitest MCP servers, and why adding one needs the human's approval
Version Discipline
See version-discipline.md for:
- Vitest 4 ignoring an unrecognized
test key instead of erroring, so a v3-era option runs green and does nothing
workspace → projects, maxThreads/maxForks → maxWorkers, singleThread → maxWorkers: 1 + isolate: false
poolOptions.* flattened to the top level, and deps.external/inline → server.deps.*
coverage.all, environmentMatchGlobs, poolMatchGlobs, minWorkers, and the basic reporter, all removed
getMockName() now returning "vi.fn()", which rewrites every snapshot containing a mock
- which features need 4.1.0, 4.1.1, 4.1.3, or 4.1.4 — tags, the fixture builder, custom snapshot matchers, ARIA snapshots
Configuration
See configuration.md for:
defineConfig imported from vitest/config, not from vite, which does not type the test block
- v4's default exclusions shrinking to
node_modules and .git, so dist is now scanned unless excluded
globals: false and explicit imports as the posture, and the vitest/globals types entry globals: true then needs
clearMocks, mockReset, restoreMocks, unstubEnvs, unstubGlobals as config rather than a remembered hook
testTimeout at 5,000 ms, and raising the narrowest scope — per-test or hookTimeout — instead of the suite
dangerouslyIgnoreUnhandledErrors disabling a real check, and the experimental block as a separate risk class
Test Projects
See test-projects.md for:
projects as the one mechanism, replacing workspace, poolMatchGlobs, and environmentMatchGlobs together
- glob, config path, and inline entries,
! negation, and the packages/!(business) bracket idiom
- the
vitest.config.* / vitest.<name>.config.* naming rule a file must match to be picked up at all
- projects not inheriting the root config —
extends: true or mergeConfig, or shared setup files are simply absent
coverage, reporters, and resolveSnapshotPath being process-wide and therefore illegal in a project
- splitting a
node project from a browser one, or unit from integration with different isolation
Test Environment
See environments.md for:
node, jsdom, happy-dom, edge-runtime, and DOM startup cost paid per file
- the
// @vitest-environment jsdom docblock for a handful of files rather than the whole suite
- a dependency importing CSS or an asset failing under a DOM shim until the whole chain is in
server.deps.inline
viteEnvironment as v4's replacement for transformMode in a custom environment
- "passes under jsdom, breaks in the browser" as the signal to move to Browser Mode rather than stub further
Run Lifecycle
See run-lifecycle.md for:
globalSetup running in the main process with a different global scope, so only provide/inject crosses it
setupFiles running in the worker, which is why mock registration and expect.extend belong there
- setup files re-running under
isolate: false while the modules they import stay cached
globalSetup not re-running in watch mode, so a seeded database keeps the previous run's state
forceRerunTriggers and watchTriggerPatterns for a non-imported file that should invalidate a run
Test API
See test-api.md for:
- test options in the second argument; the v3 third-argument form is removed and silently unread
skip, skipIf, runIf, only, todo, fails, concurrent, and sequential's deprecation
test.each spreading an array case while test.for does not, and for supplying the test context
aroundEach/aroundAll and the runTest call without which nothing inside runs
onTestFinished/onTestFailed as the per-test cleanup that survives concurrency
retry's error predicate, for absorbing one failure mode rather than every error the test can raise
Fixtures and Test Context
See fixtures-and-context.md for:
context.expect being required for a snapshot inside a concurrent test
- the 4.1
test.extend builder inferring types, and onCleanup being callable only once per fixture
- test, file, and worker scope, and a fixture reaching only its own scope or a longer-lived one
- only destructured fixtures being initialized, so
{ database } and context.database are not equivalent
test.override for a suite's value, and injected fixtures fed from config.provide per project
Assertions and Async Tests
See assertions-and-async.md for:
toEqual and toStrictEqual disagreeing exactly on a present-undefined key and a sparse array
expect.objectContaining, expect.stringMatching, expect.closeTo, and expect.schemaMatching for Standard Schema
.resolves/.rejects failing the test in v4 when unawaited, where v3 passed silently
vi.waitFor vs vi.waitUntil vs expect.poll, and expect.poll rejecting snapshots, .rejects, and toThrow
expect.hasAssertions() for a test whose assertions sit inside a callback
expect.extend's MatcherResult, and why branching pass on isNot cancels out
Function Mocks and Spies
See function-mocks.md for:
- a spy seeing only calls made after it is installed, so import-time work is invisible to it
mockClear keeping the implementation, mockReset restoring the original, mockRestore restoring the descriptor
clearAllMocks/resetAllMocks/restoreAllMocks not reaching automocked modules at all
mockThrow/mockThrowOnce, withImplementation, and the Once variants for a call sequence
invocationCallOrder starting at 1 in v4, and preferring the mock matchers over reading mock.calls
vi.defineHelper so a shared assertion helper reports the caller's line
Module Mocking
See module-mocking.md for:
vi.mock hoisting above imports, leaving a module-level const undefined when the factory runs
vi.hoisted for a value the factory closes over, and importOriginal needing an await
- automocking emptying arrays and returning
undefined from methods, versus { spy: true } keeping real behavior
__mocks__ staying inert until a vi.mock call names the module
- a call between two functions in the same file being unmockable, and the extract-or-inject refactor that fixes it
server.deps.inline when vi.mock against an externalized dependency has no effect
Timers and Ambient State
See timers-and-ambient-state.md for:
- a fake clock left installed leaking into later files in the same worker
- the
*Async advance variants being required whenever a scheduled callback awaits
runAllTimers throwing at loopLimit on code that reschedules itself
vi.setSystemTime freezing a drifting snapshot, and TZ still governing formatted output
vi.stubGlobal/vi.stubEnv persisting across tests unless unstubGlobals/unstubEnvs are set
memfs through __mocks__/fs.cjs and __mocks__/fs/promises.cjs, mocking both specifiers
Network Mocking
See network-mocking.md for:
- intercepting with MSW rather than replacing
fetch, so the request path under test stays real
server.listen() / resetHandlers() / close() across beforeAll, afterEach, and afterAll
onUnhandledRequest: "error" as what stops an unmatched request reaching the real network
- why an end-to-end test deliberately wants the real dependency instead
Snapshots
See snapshots.md for:
- inline for a diff-reviewable value,
.snap for larger output, file snapshots for markup, ARIA for structure
- errors rendering as
[Error: message] rather than the bare message
-u accepting new, all, and none, and --update=new leaving existing snapshots alone
- CI refusing to write a snapshot, so a missing one fails rather than being created
- an obsolete snapshot failing CI, which catches a deleted test whose stored output stayed
Coverage
See coverage.md for:
@vitest/coverage-v8 and @vitest/coverage-istanbul needing explicit installation in v4
- only imported files being reported unless
coverage.include names the source globs
autoUpdate rewriting a threshold after a run, so a drop is recorded rather than rejected
- ignore hints needing
-- @preserve or esbuild strips them before the provider sees them
DEBUG=vitest:coverage when collection itself is what is slow
Performance and Parallelism
See performance-and-parallelism.md for:
- files across workers versus tests within a file, and which axis a given slowdown sits on
forks as the compatible default, and threads producing worker-termination and segfault failures
isolate: false as the biggest win, what module-level state survives it, and vmThreads forbidding it
- concurrency only helping tests that wait, since synchronous tests still share one thread
- reading the transform, collect, setup, and environment timings before changing any setting
- sharding splitting files rather than cases, so one slow file cannot be sharded
Filtering and Tags
See filtering-and-tags.md for:
- a positional path filter,
-t, and file.test.ts:10 needing the full filename
allowOnly at its default failing a CI run that contains .only
- declaring tags in config before use, with
timeout, retry, and priority per tag
--tags-filter expressions with and/or/not, wildcards, and not > and > or precedence
@module-tag applying to a whole file rather than one test
- filters applying per file, so Vitest still loads every file to find matches
Reporters and CI Output
See reporters-and-ci.md for:
minimal, aliased agent, printing only failures and their errors
- its auto-selection on
std-env AI-agent detection or an AI_AGENT variable
- any custom
reporters array skipping that detection, and github-actions needing listing for the same reason
blob with --shard, merged by --merge-reports, as the only correct way to report a sharded run
outputFile as a path or a per-reporter map
context.annotate severities, and the default reporter printing them only for failed tests
Browser Mode
See browser-mode.md for:
- CSS layout, computed style, and real event dispatch as the bug class a DOM shim cannot produce
- v4 providers being objects from
@vitest/browser-playwright, -webdriverio, or -preview, not strings
- imports moving to
vitest/browser from the removed @vitest/browser/context
await expect.element(...) as the retrying assertion, where the synchronous expect does not
userEvent from vitest/browser driving real input, distinct from Testing Library's synthetic events
alert/confirm blocking the thread, and sealed ESM namespaces needing { spy: true } to observe an export
Visual Regression
See visual-regression.md for:
toMatchScreenshot writing a reference on first run and failing, into a committed __screenshots__/
allowedMismatchedPixelRatio versus allowedMismatchedPixels, where the stricter limit wins
- stable-screenshot detection reshooting until two frames agree
- fonts, GPU, and headless mode making a local screenshot differ from CI's on an unchanged page
toMatchAriaSnapshot asserting the accessibility tree instead, and surviving a font bump
Type and In-Source Tests
See types-and-in-source.md for:
*.test-d.ts with expectTypeOf/assertType, run only under --typecheck
- type tests being analyzed rather than executed, so a
test.each name is never evaluated
import.meta.vitest with includeSource, and "vitest/importMeta" in the tsconfig types
- the production-build
define: { "import.meta.vitest": "undefined" } without which those tests ship to users
- the documentation scoping in-source tests to small utilities, not components or e2e
Debugging and Failure Modes
See debugging-and-failure-modes.md for:
--inspect-brk --no-file-parallelism --test-timeout=0 together, and what each flag removes
--isolate false keeping a debugger attached across watch reruns
- the
hanging-process reporter and --detect-async-leaks for a run that will not exit
Failed to Terminate Worker from Node's fetch under threads, fixed by forks
- segfaults from native modules under
threads, fixed the same way
Cannot find module from an unresolved path alias, and ssr.resolve.conditions for ignored export conditions
Suite Hygiene
See suite-hygiene.md for:
expect-expect, no-focused-tests, and valid-expect from @vitest/eslint-plugin
allowOnly and obsolete-snapshot failures as the two guards against an abandoned edit
restoreMocks/unstubEnvs/unstubGlobals moving hygiene out of every author's memory
--detect-async-leaks for a handle that outlives the test that opened it
- whether CI invokes
vitest run rather than a form that can enter watch mode, and whether a changed config key still exists in the installed version
Extending Vitest
See extending-vitest.md for:
- the removed v3 reporter hooks (
onCollected, onTaskUpdate, onFinished) producing silence, not an error
viteEnvironment in a custom environment, and the rewritten v4 pool interface
startVitest, createVitest, and vitest.state.getTestModules() from vitest/node
experimental.openTelemetry needing an sdkPath whose module default-exports an unstarted SDK
- the per-worker startup cost tracing adds unless the run is not isolated
1---2name: vitest-testing3description: Working on Vitest itself — the 4.x runner layer rather than the tests' content, covering config, the `vi` API, pools, coverage, and Browser Mode, plus driving it without hanging a session. Triggers on `vitest.config`, `vi.mock`, `vi.useFakeTimers`, `expect.poll`, `toMatchInlineSnapshot`, `test.extend`, `projects`, `pool`, `browser.instances`, `toMatchScreenshot`, `*.test-d.ts`, `import.meta.vitest`, or a config key Vitest 4 silently ignores. For what to assert and how a spec is shaped, use a unit-testing capability; for journeys and server lifecycle, an end-to-end one. Lookup-first where the option surface moved from v3.4---56# Vitest Testing78This skill equips you to configure, run, debug, and review a Vitest suite: the runner's own surface — its config file, its `vi` API, its pools and reporters, its coverage providers, and its Browser Mode — plus the operational rules an agent needs to drive it without hanging a session or burning a context window.910It is the **runner-specific layer**. A tool-agnostic unit-testing capability owns what to assert, how to name a spec, what makes a fixture good, and whether a behavior deserves a unit test at all; those questions have the same answers whatever runner executes them. This skill owns the mechanism underneath: which option, which file, which `vi` call, which flag. Where a rule here touches judgment — which dependency is worth mocking, how much of a payload to pin, whether a snapshot earns its place — it points at the tool-agnostic owner rather than answering it.1112Two neighbours are disclaimed explicitly. An end-to-end capability owns user journeys, the locator fallback hierarchy, server lifecycle, and scenario coverage **even when the runner underneath is Vitest**; this skill owns the runner configuration that suite runs on. A component-development capability owns test-hook conventions such as `data-testid` and `testID`; this skill owns the Browser Mode machinery that queries them. A project on a different runner should reach for that runner's own capability, not this one.1314**Version posture is lookup-first.** Rules are verified against **Vitest 4.1.10** (Node >= 20, Vite >= 6) and name the release they were checked against. Vitest 4 moved or removed roughly two dozen configuration options and **silently ignores the old names rather than erroring**, so a v3-era answer copied off the open web fails invisibly — the suite runs, the option does nothing. Wherever a surface is known to have moved, consult the installed version's own documentation at <https://vitest.dev> rather than recalling an option name; each reference below names the upstream page it was verified against.1516The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119.html).1718## Running Vitest as an Agent1920See [running-as-an-agent.md](./references/running-as-an-agent.md) for:2122- `vitest run` or `--no-watch` for every non-interactive run; bare `vitest` enters watch mode and never exits23- a project `test` script defined as `vitest` or `vitest --watch`, which hands that hang to anyone who runs it24- narrowing with a path, `-t`, `file.test.ts:42`, `--project`, `--changed`, or `--related`25- the `minimal` reporter's token cost argument, and the custom `reporters` array that disables its auto-selection26- reading the reported diff and stack frame instead of re-running the suite to see the failure again27- third-party Vitest MCP servers, and why adding one needs the human's approval2829## Version Discipline3031See [version-discipline.md](./references/version-discipline.md) for:3233- Vitest 4 ignoring an unrecognized `test` key instead of erroring, so a v3-era option runs green and does nothing34- `workspace` → `projects`, `maxThreads`/`maxForks` → `maxWorkers`, `singleThread` → `maxWorkers: 1` + `isolate: false`35- `poolOptions.*` flattened to the top level, and `deps.external`/`inline` → `server.deps.*`36- `coverage.all`, `environmentMatchGlobs`, `poolMatchGlobs`, `minWorkers`, and the `basic` reporter, all removed37- `getMockName()` now returning `"vi.fn()"`, which rewrites every snapshot containing a mock38- which features need 4.1.0, 4.1.1, 4.1.3, or 4.1.4 — tags, the fixture builder, custom snapshot matchers, ARIA snapshots3940## Configuration4142See [configuration.md](./references/configuration.md) for:4344- `defineConfig` imported from `vitest/config`, not from `vite`, which does not type the `test` block45- v4's default exclusions shrinking to `node_modules` and `.git`, so `dist` is now scanned unless excluded46- `globals: false` and explicit imports as the posture, and the `vitest/globals` types entry `globals: true` then needs47- `clearMocks`, `mockReset`, `restoreMocks`, `unstubEnvs`, `unstubGlobals` as config rather than a remembered hook48- `testTimeout` at 5,000 ms, and raising the narrowest scope — per-test or `hookTimeout` — instead of the suite49- `dangerouslyIgnoreUnhandledErrors` disabling a real check, and the `experimental` block as a separate risk class5051## Test Projects5253See [test-projects.md](./references/test-projects.md) for:5455- `projects` as the one mechanism, replacing `workspace`, `poolMatchGlobs`, and `environmentMatchGlobs` together56- glob, config path, and inline entries, `!` negation, and the `packages/!(business)` bracket idiom57- the `vitest.config.*` / `vitest.<name>.config.*` naming rule a file must match to be picked up at all58- projects not inheriting the root config — `extends: true` or `mergeConfig`, or shared setup files are simply absent59- `coverage`, `reporters`, and `resolveSnapshotPath` being process-wide and therefore illegal in a project60- splitting a `node` project from a browser one, or unit from integration with different isolation6162## Test Environment6364See [environments.md](./references/environments.md) for:6566- `node`, `jsdom`, `happy-dom`, `edge-runtime`, and DOM startup cost paid per file67- the `// @vitest-environment jsdom` docblock for a handful of files rather than the whole suite68- a dependency importing CSS or an asset failing under a DOM shim until the whole chain is in `server.deps.inline`69- `viteEnvironment` as v4's replacement for `transformMode` in a custom environment70- "passes under jsdom, breaks in the browser" as the signal to move to Browser Mode rather than stub further7172## Run Lifecycle7374See [run-lifecycle.md](./references/run-lifecycle.md) for:7576- `globalSetup` running in the main process with a different global scope, so only `provide`/`inject` crosses it77- `setupFiles` running in the worker, which is why mock registration and `expect.extend` belong there78- setup files re-running under `isolate: false` while the modules they import stay cached79- `globalSetup` not re-running in watch mode, so a seeded database keeps the previous run's state80- `forceRerunTriggers` and `watchTriggerPatterns` for a non-imported file that should invalidate a run8182## Test API8384See [test-api.md](./references/test-api.md) for:8586- test options in the **second** argument; the v3 third-argument form is removed and silently unread87- `skip`, `skipIf`, `runIf`, `only`, `todo`, `fails`, `concurrent`, and `sequential`'s deprecation88- `test.each` spreading an array case while `test.for` does not, and `for` supplying the test context89- `aroundEach`/`aroundAll` and the `runTest` call without which nothing inside runs90- `onTestFinished`/`onTestFailed` as the per-test cleanup that survives concurrency91- `retry`'s error predicate, for absorbing one failure mode rather than every error the test can raise9293## Fixtures and Test Context9495See [fixtures-and-context.md](./references/fixtures-and-context.md) for:9697- `context.expect` being required for a snapshot inside a concurrent test98- the 4.1 `test.extend` builder inferring types, and `onCleanup` being callable only once per fixture99- test, file, and worker scope, and a fixture reaching only its own scope or a longer-lived one100- only destructured fixtures being initialized, so `{ database }` and `context.database` are not equivalent101- `test.override` for a suite's value, and `injected` fixtures fed from `config.provide` per project102103## Assertions and Async Tests104105See [assertions-and-async.md](./references/assertions-and-async.md) for:106107- `toEqual` and `toStrictEqual` disagreeing exactly on a present-`undefined` key and a sparse array108- `expect.objectContaining`, `expect.stringMatching`, `expect.closeTo`, and `expect.schemaMatching` for Standard Schema109- `.resolves`/`.rejects` failing the test in v4 when unawaited, where v3 passed silently110- `vi.waitFor` vs `vi.waitUntil` vs `expect.poll`, and `expect.poll` rejecting snapshots, `.rejects`, and `toThrow`111- `expect.hasAssertions()` for a test whose assertions sit inside a callback112- `expect.extend`'s `MatcherResult`, and why branching `pass` on `isNot` cancels out113114## Function Mocks and Spies115116See [function-mocks.md](./references/function-mocks.md) for:117118- a spy seeing only calls made after it is installed, so import-time work is invisible to it119- `mockClear` keeping the implementation, `mockReset` restoring the original, `mockRestore` restoring the descriptor120- `clearAllMocks`/`resetAllMocks`/`restoreAllMocks` not reaching automocked modules at all121- `mockThrow`/`mockThrowOnce`, `withImplementation`, and the `Once` variants for a call sequence122- `invocationCallOrder` starting at 1 in v4, and preferring the mock matchers over reading `mock.calls`123- `vi.defineHelper` so a shared assertion helper reports the caller's line124125## Module Mocking126127See [module-mocking.md](./references/module-mocking.md) for:128129- `vi.mock` hoisting above imports, leaving a module-level `const` undefined when the factory runs130- `vi.hoisted` for a value the factory closes over, and `importOriginal` needing an `await`131- automocking emptying arrays and returning `undefined` from methods, versus `{ spy: true }` keeping real behavior132- `__mocks__` staying inert until a `vi.mock` call names the module133- a call between two functions in the same file being unmockable, and the extract-or-inject refactor that fixes it134- `server.deps.inline` when `vi.mock` against an externalized dependency has no effect135136## Timers and Ambient State137138See [timers-and-ambient-state.md](./references/timers-and-ambient-state.md) for:139140- a fake clock left installed leaking into later files in the same worker141- the `*Async` advance variants being required whenever a scheduled callback awaits142- `runAllTimers` throwing at `loopLimit` on code that reschedules itself143- `vi.setSystemTime` freezing a drifting snapshot, and `TZ` still governing formatted output144- `vi.stubGlobal`/`vi.stubEnv` persisting across tests unless `unstubGlobals`/`unstubEnvs` are set145- `memfs` through `__mocks__/fs.cjs` and `__mocks__/fs/promises.cjs`, mocking both specifiers146147## Network Mocking148149See [network-mocking.md](./references/network-mocking.md) for:150151- intercepting with MSW rather than replacing `fetch`, so the request path under test stays real152- `server.listen()` / `resetHandlers()` / `close()` across `beforeAll`, `afterEach`, and `afterAll`153- `onUnhandledRequest: "error"` as what stops an unmatched request reaching the real network154- why an end-to-end test deliberately wants the real dependency instead155156## Snapshots157158See [snapshots.md](./references/snapshots.md) for:159160- inline for a diff-reviewable value, `.snap` for larger output, file snapshots for markup, ARIA for structure161- errors rendering as `[Error: message]` rather than the bare message162- `-u` accepting `new`, `all`, and `none`, and `--update=new` leaving existing snapshots alone163- CI refusing to **write** a snapshot, so a missing one fails rather than being created164- an **obsolete** snapshot failing CI, which catches a deleted test whose stored output stayed165166## Coverage167168See [coverage.md](./references/coverage.md) for:169170- `@vitest/coverage-v8` and `@vitest/coverage-istanbul` needing explicit installation in v4171- only imported files being reported unless `coverage.include` names the source globs172- `autoUpdate` rewriting a threshold after a run, so a drop is recorded rather than rejected173- ignore hints needing `-- @preserve` or esbuild strips them before the provider sees them174- `DEBUG=vitest:coverage` when collection itself is what is slow175176## Performance and Parallelism177178See [performance-and-parallelism.md](./references/performance-and-parallelism.md) for:179180- files across workers versus tests within a file, and which axis a given slowdown sits on181- `forks` as the compatible default, and `threads` producing worker-termination and segfault failures182- `isolate: false` as the biggest win, what module-level state survives it, and `vmThreads` forbidding it183- concurrency only helping tests that wait, since synchronous tests still share one thread184- reading the transform, collect, setup, and environment timings before changing any setting185- sharding splitting files rather than cases, so one slow file cannot be sharded186187## Filtering and Tags188189See [filtering-and-tags.md](./references/filtering-and-tags.md) for:190191- a positional path filter, `-t`, and `file.test.ts:10` needing the full filename192- `allowOnly` at its default failing a CI run that contains `.only`193- declaring tags in config before use, with `timeout`, `retry`, and `priority` per tag194- `--tags-filter` expressions with `and`/`or`/`not`, wildcards, and `not` > `and` > `or` precedence195- `@module-tag` applying to a whole file rather than one test196- filters applying per file, so Vitest still loads every file to find matches197198## Reporters and CI Output199200See [reporters-and-ci.md](./references/reporters-and-ci.md) for:201202- `minimal`, aliased `agent`, printing only failures and their errors203- its auto-selection on `std-env` AI-agent detection or an `AI_AGENT` variable204- any custom `reporters` array skipping that detection, and `github-actions` needing listing for the same reason205- `blob` with `--shard`, merged by `--merge-reports`, as the only correct way to report a sharded run206- `outputFile` as a path or a per-reporter map207- `context.annotate` severities, and the default reporter printing them only for failed tests208209## Browser Mode210211See [browser-mode.md](./references/browser-mode.md) for:212213- CSS layout, computed style, and real event dispatch as the bug class a DOM shim cannot produce214- v4 providers being objects from `@vitest/browser-playwright`, `-webdriverio`, or `-preview`, not strings215- imports moving to `vitest/browser` from the removed `@vitest/browser/context`216- `await expect.element(...)` as the retrying assertion, where the synchronous `expect` does not217- `userEvent` from `vitest/browser` driving real input, distinct from Testing Library's synthetic events218- `alert`/`confirm` blocking the thread, and sealed ESM namespaces needing `{ spy: true }` to observe an export219220## Visual Regression221222See [visual-regression.md](./references/visual-regression.md) for:223224- `toMatchScreenshot` writing a reference on first run and **failing**, into a committed `__screenshots__/`225- `allowedMismatchedPixelRatio` versus `allowedMismatchedPixels`, where the stricter limit wins226- stable-screenshot detection reshooting until two frames agree227- fonts, GPU, and headless mode making a local screenshot differ from CI's on an unchanged page228- `toMatchAriaSnapshot` asserting the accessibility tree instead, and surviving a font bump229230## Type and In-Source Tests231232See [types-and-in-source.md](./references/types-and-in-source.md) for:233234- `*.test-d.ts` with `expectTypeOf`/`assertType`, run only under `--typecheck`235- type tests being analyzed rather than executed, so a `test.each` name is never evaluated236- `import.meta.vitest` with `includeSource`, and `"vitest/importMeta"` in the tsconfig types237- the production-build `define: { "import.meta.vitest": "undefined" }` without which those tests ship to users238- the documentation scoping in-source tests to small utilities, not components or e2e239240## Debugging and Failure Modes241242See [debugging-and-failure-modes.md](./references/debugging-and-failure-modes.md) for:243244- `--inspect-brk --no-file-parallelism --test-timeout=0` together, and what each flag removes245- `--isolate false` keeping a debugger attached across watch reruns246- the `hanging-process` reporter and `--detect-async-leaks` for a run that will not exit247- `Failed to Terminate Worker` from Node's `fetch` under `threads`, fixed by `forks`248- segfaults from native modules under `threads`, fixed the same way249- `Cannot find module` from an unresolved path alias, and `ssr.resolve.conditions` for ignored export conditions250251## Suite Hygiene252253See [suite-hygiene.md](./references/suite-hygiene.md) for:254255- `expect-expect`, `no-focused-tests`, and `valid-expect` from `@vitest/eslint-plugin`256- `allowOnly` and obsolete-snapshot failures as the two guards against an abandoned edit257- `restoreMocks`/`unstubEnvs`/`unstubGlobals` moving hygiene out of every author's memory258- `--detect-async-leaks` for a handle that outlives the test that opened it259- whether CI invokes `vitest run` rather than a form that can enter watch mode, and whether a changed config key still exists in the installed version260261## Extending Vitest262263See [extending-vitest.md](./references/extending-vitest.md) for:264265- the removed v3 reporter hooks (`onCollected`, `onTaskUpdate`, `onFinished`) producing silence, not an error266- `viteEnvironment` in a custom environment, and the rewritten v4 pool interface267- `startVitest`, `createVitest`, and `vitest.state.getTestModules()` from `vitest/node`268- `experimental.openTelemetry` needing an `sdkPath` whose module default-exports an unstarted SDK269- the per-worker startup cost tracing adds unless the run is not isolated