new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Vitest · Guide · all subjects

advanced/projects

61 notes in this subject, read out of this brain and free to use. This is page 1 of 2.

Projects feature overview and terminology

Vitest provides a way to define multiple project configurations within a single Vitest process. This feature is useful for monorepo setups or to run tests with different configurations, such as resolve.alias, plugins, or test.browser. The projects feature was previously known as workspace, but workspace is deprecated since version 3.2 and replaced with the projects configuration. They are functionally the same.

Define projects with glob patterns in root config

Projects can be defined in the root vitest.config.ts file using the test.projects configuration option. You can use glob patterns to reference project folders. For example, 'packages/*' will treat every folder in packages as a separate project, even if it doesn't have a config file inside.

Project config file naming requirements

If a project entry resolves to a file from a glob pattern or direct file path, Vitest validates that the config file name either starts with vitest.config or vite.config (for example, vitest.config.unit.ts) or matches vitest.<name>.config.* or vite.<name>.config.*, where <name> can contain letters, numbers, underscore (_), and hyphen (-). Valid examples include: vitest.config.ts, vite.config.js, vitest.unit.config.ts, vitest.e2e-node.config.ts, vite.e2e.config.js.

Disable inheritance for inline projects

To prevent an inline project from inheriting options from the root config file, set extends: false in the project configuration. This means the project will not inherit plugins, pool, and other root-level options.

Exclude projects using negation pattern

To exclude folders and files from projects, use the negation pattern with an exclamation mark. For example, projects: ['packages/*', '!packages/excluded'] will include all folders inside packages except the excluded folder.

Nested folder projects using bracket patterns

When you have nested folder structures where some folders need to be projects but other folders have their own subfolders, use brackets to avoid matching the parent folder. For example, projects: ['packages/!(business)', 'packages/business/*'] will create projects from packages/a, packages/b, packages/business/c, and packages/business/d, but packages/business itself will not be a project.

Root config is not treated as a project by default

Vitest does not treat the root vitest.config file as a project unless it is explicitly specified in the configuration. The root configuration only influences global options such as reporters and coverage. However, Vitest will always run certain plugin hooks like apply, config, configResolved, or configureServer specified in the root config file. Vitest also uses the same plugins to execute global setups and custom coverage provider.

Reference projects with specific config file patterns

Projects can be referenced with config files using patterns like 'packages/*/vitest.config.{e2e,unit}.ts'. This pattern will only include projects with a vitest.config file that contains 'e2e' or 'unit' before the extension.

Mix glob patterns and inline project configurations

Projects configuration supports both syntaxes simultaneously. You can define both glob pattern matches like 'packages/*' and inline project objects in the same projects array.

Inline project name configuration

For inline projects, it is recommended to define a name property. If a name is not provided in the inline configuration, Vitest will assign a number. For project configurations defined with glob syntax, Vitest will default to using the 'name' property in the nearest package.json file or, if none exists, the folder name. Project names must be unique; otherwise, Vitest will throw an error. The color of the name label can be changed using name: { label: 'node', color: 'green' }.

Use defineProject for type safety in project configs

Projects do not support all configuration properties. For better type safety, use the defineProject method instead of defineConfig within project configuration files. This will provide errors for unsupported configuration options like reporters.

Run all tests with projects configuration

To run all tests in a projects setup, define a script in your root package.json with 'vitest' command, then run it with your package manager (npm run test, yarn test, pnpm run test, or bun run test).

Inline projects inherit from root config by default

Projects defined with an inline configuration inherit all options from the root-level configuration by default since Vitest 5.0. This is controlled by the extends option, which is enabled by default. Inline projects will inherit options like plugins and pool from the root config.

Inherit project options from a shared config file

The extends option in a project configuration also accepts a path to another config file if you want to inherit options from a config file other than the root config. For example, extends: './vitest.shared.ts' will inherit options from that shared config file.

Array options are concatenated when extending configs

When extending a config, array-based options like setupFiles are concatenated, not overridden. The project's array values are added to the inherited array values.

Special option inheritance rules for projects

The following options have special inheritance behavior: name and projects are never inherited. globalSetup is not inherited from the root config because the root-level globalSetup already runs once per test run, so inheriting it would run the same files again for every project. globalSetup is still inherited when extending a non-root config file. The project's own tags replace the inherited array instead of being merged with it.

Projects from config files or directories do not inherit root options

Projects referenced as config files or directories do not inherit any options from the root config. You can create a shared config file and merge it with the project config yourself using mergeConfig function.

Merge shared config with project config

To manually inherit options from a shared config in a project referenced as a config file or directory, use the mergeConfig function from vitest/config. Example: export default mergeConfig(configShared, defineProject({ test: { environment: 'jsdom' } }))

Unsupported configuration options in project configs

The following configuration options are not allowed in a project config: coverage (coverage is done for the whole process), reporters (only root-level reporters can be supported), resolveSnapshotPath (only root-level resolver is respected), attachmentsDir (attachments are stored in one root-level directory shared by all projects), and all other options that don't affect test runners. Configuration options not supported in project config are marked with a CRoot icon in the documentation.

Nested projects configuration

A project referenced as a config file or a directory containing one can declare projects itself. Such a config behaves like the root config: it doesn't run any tests on its own, it only provides the projects that do. This makes it possible to reference a workspace that already defines its own projects.

Nested project naming and filtering

The names of nested projects are prefixed with the name of the config that declares them. For example, if a config named 'app' declares projects 'unit' and 'e2e', they become 'app (unit)' and 'app (e2e)'. The --project filter matches the prefix as well: --project app runs every project of the app config, while --project "app (unit)" runs only one of them.

Run tests from the declaring config in nested projects

To run the tests of the config that declares projects, reference its own config file in the projects array. This allows the declaring config to run its own include tests alongside its declared projects.

Nested projects only in config files, not inline

Only config files can define nested projects. The projects option inside an inline configuration is not supported.

Nested projects inherit from declaring config

Nested projects' inline configurations extend the config that declares them (not the root config). The extends paths in nested projects are resolved relative to the declaring config, and the declaring config's own globalSetup is inherited by the extending projects like any other non-root config.

Worker scope fixture configuration

To enable worker-scoped fixtures that persist across multiple files, set `isolate: false` in the Vitest test configuration. By default, every test file runs in its own worker, making `scope: 'file'` and `scope: 'worker'` behave identically. With `isolate: false`, Vitest reuses workers across files (capped by `maxWorkers`).

Risks of turning off isolation between test files

When `isolate: false` is set to reuse workers across files, shared module instances inside the worker can leak state between tests in different files. Tests that mutate top-level state like counters, caches, or monkey-patched globals can cause that state to affect whichever file runs next in the same worker. Per-test database rollbacks handle data isolation but cannot protect module state in the worker.

vmThreads and vmForks always isolate regardless of isolate flag

The `vmThreads` and `vmForks` pool options always run isolated regardless of the `isolate` flag setting. Worker-scoped fixtures fall back to per-file behavior in those pools.

Unsafe cases to deisolate a test file

A test file is NOT safe to deisolate if it mutates module-level state (counters, caches, top-level let bindings), calls vi.stubGlobal or vi.stubEnv, monkey-patches prototypes (Date.prototype, Array.prototype, etc.), registers listeners on process or other long-lived emitters, or depends on a fresh module instance for vi.mock factories.

File isolation disabled by default adds setup cost

By default, every test file runs in its own isolated module graph which protects against one file leaking state into another. However, this isolation costs setup time on every file, which is wasteful for pure unit tests that don't share mutable state but necessary for integration tests.

Use projects to apply per-file isolation settings

Use the projects configuration option to apply isolate: false to a unit test suite while keeping a separate integration test suite isolated. This allows fine-grained control over isolation per project.

Per-file isolation configuration example with projects

Example configuration showing how to use projects with different isolation settings: ```ts import { defineConfig } from 'vitest/config' export default defineConfig({ test: { projects: [ { test: { name: 'Unit tests', isolate: false, exclude: ['**.integration.test.ts'], }, }, { test: { name: 'Integration tests', include: ['**.integration.test.ts'], }, }, ], }, }) ``` This configuration disables isolation for unit tests while keeping integration tests isolated.

Verify per-file isolation safety with test shuffling

To verify that deisolating a test file is safe, run the suite twice with shuffling using the command: vitest --shuffle --run --project='Unit tests'. Run this command twice and compare the results. If the second run produces different results, the tests have order-dependent pollution and isolation should remain enabled for that file.

isolate flag only affects threads and forks pools

The isolate flag only governs the threads and forks pool types. The vmThreads and vmForks pools always run isolated regardless of the isolate flag setting, since they trade startup cost for stronger guarantees.

fileParallelism controls file-level parallelism, not test-level parallelism

fileParallelism controls whether different test files run in parallel workers. It does not make tests inside a single file run concurrently. Tests inside a file are sequential by default. The describe.concurrent and test.concurrent options control whether tests within one file run concurrently, which is separate from file-level parallelism.

fileParallelism false prevents files from running concurrently

The fileParallelism: false configuration option at the project level keeps test files in that project running one at a time, while the rest of the suite continues running concurrently. It is equivalent to maxWorkers: 1.

sequence.groupOrder forces project execution order

The sequence.groupOrder configuration (available in Vitest 3.2.0+) forces a specific execution order between projects. Projects with lower groupOrder values execute first. This ensures the parallel project finishes before the sequential project starts, preventing resource conflicts.

Parallel and sequential projects configuration pattern

To run most tests in parallel while keeping certain files sequential, split the test suite into two projects in vitest.config.ts: one with exclude: ['**.sequential.test.ts'] for parallel tests, and one with include: ['**.sequential.test.ts'] and fileParallelism: false for sequential tests. This avoids disabling parallelism globally.

describe.concurrent and test.concurrent do not affect file scheduling

Using describe.concurrent or test.concurrent on tests within a file makes those tests run concurrently within that file, but it does not affect how files are scheduled relative to each other.

Example: parallel and sequential projects with groupOrder

```ts import { defineConfig } from 'vitest/config' export default defineConfig({ test: { projects: [ { test: { name: 'Parallel', exclude: ['**.sequential.test.ts'], sequence: { groupOrder: 0 }, }, }, { test: { name: 'Sequential', include: ['**.sequential.test.ts'], fileParallelism: false, sequence: { groupOrder: 1 }, }, }, ], }, }) ``` This configuration runs all non-sequential tests in parallel first, then runs sequential tests one at a time after the parallel batch completes.

project flag filters projects to run

The `-p, --project <name>` CLI flag specifies which project to run when using Vitest workspace feature. Can be repeated for multiple projects: `--project=1 --project=2`. Supports wildcards: `--project=packages*` and exclusions: `--project=!pattern`.

sharedViteServer option for inline projects

Inline projects in Vitest 5.0 now reuse the Vite server of the config that declares them by default (sharedViteServer: true). This applies only when inline projects don't modify Vite config. Projects still get their own server when they define Vite-level options (plugins, resolve), non-default extends, or test options affecting Vite config (alias, browser, css, deps.moduleDirectories, deps.optimizer, mode, root). Set sharedViteServer: false to disable this behavior.

Inline projects inherit root config by default in Vitest 5.0

In Vitest 5.0, inline projects defined in test.projects now inherit all options from the root configuration by default (extends: true). This includes Vite options like plugins and resolve.alias. Options are merged using the same rules as explicit extends: true in Vitest 4. Arrays are merged, not overridden. Name and projects are never inherited. globalSetup is not inherited from the root config. The project's own tags replace the inherited array instead of merging.

TestProject.globalConfig property

The globalConfig is the test config that Vitest was initialized with. If the project is the root project, globalConfig and config will reference the same object. This config is useful for values that cannot be set on the project level, like coverage or reporters.

TestProject.config property

The config property is the project's resolved test config.

TestProject.hash property

The hash is a unique hash of the project that is consistent between reruns. It is based on the root of the project and its name. Note that the root path is not consistent between different OS, so the hash will also be different across operating systems.

TestProject.vite property

The vite property is the project's ViteDevServer. Note that the server is not necessarily exclusive to this project: other projects can reuse it when the sharedViteServer option applies, and browser instances of the same cluster share a single browser server.

TestProject.sharedViteServer property

The sharedViteServer property is a boolean that is true when the project reuses the Vite server of the config that declared it instead of resolving its own. The project that owns the server reports false even when other projects reuse it. To detect any two projects sharing a server (including browser instances), compare their vite references.

TestProject.createSpecification() method

The createSpecification method creates a test specification that can be used in vitest.runTestSpecifications. It takes a moduleId (string, must be resolved) and optional locations (number array of code lines where tests are defined). Specification scopes the test file to a specific project and test locations. If locations are provided, Vitest will only run tests defined on those lines. If testNamePattern is defined, it will also be applied. Note that createSpecification expects resolved module ID and does not auto-resolve the file or check that it exists on the file system.

TestProject.isRootProject() method

The isRootProject method checks if the current project is the root project. The root project can also be obtained by calling vitest.getRootProject().

TestProject.globTestFiles() method

The globTestFiles method globs all test files and returns an object with testFiles (test files that match the filters) and typecheckTestFiles (typecheck test files that match the filters, empty unless typecheck.enabled is true). This method accepts optional filters that can only be a part of the file path. Vitest uses fast-glob to find test files. The method looks at test.include, test.exclude for regular test files; test.includeSource, test.exclude for in-source tests; and test.typecheck.include, test.typecheck.exclude for typecheck tests.

TestProject.matchesTestGlob() method

The matchesTestGlob method checks if a file is a regular test file. It takes moduleId (string) and optional source (function returning source code as string). It uses the same config properties that globTestFiles uses for validation. If the file matches includeSource glob but is not a test file, Vitest will synchronously read the file unless the source is provided.

TestProject.import() method

The import method imports a file using Vite module runner. The file will be transformed by Vite with the provided project's config and executed in a separate context. Note that moduleId will be relative to config.root. The project.import method reuses Vite's module graph, so importing the same module using a regular import will return a different module.

TestProject.close() method

The close method closes the project and all associated resources. This can only be called once; the closing promise is cached until the server restarts. If the resources are needed again, create a new project. In detail, this method closes the Vite server, stops the typechecker service, closes the browser if it's running, deletes the temporary directory that holds the source code, and resets the provided context.

TestProject createSpecification example

Example of using createSpecification: import { createVitest } from 'vitest/node'; import { resolve } from 'node:path/posix'; const vitest = await createVitest('test'); const project = vitest.projects[0]; const specification = project.createSpecification(resolve('./example.test.ts'), [20, 40]); await vitest.runTestSpecifications([specification]);

TestProject matchesTestGlob example

Example of using matchesTestGlob: import { createVitest } from 'vitest/node'; import { resolve } from 'node:path/posix'; const vitest = await createVitest('test'); const project = vitest.projects[0]; project.matchesTestGlob(resolve('./basic.test.ts')); // true; project.matchesTestGlob(resolve('./basic.ts')); // false; project.matchesTestGlob(resolve('./basic.ts'), () => `if (import.meta.vitest) { // ... }`); // true if includeSource is set

TestProject import pitfall

The project.import method reuses Vite's module graph, so importing the same module using a regular import will return a different module. This means that project.import('./example.js') will return a different reference than a static import * as staticExample from './example.js'.

TestProject serializedConfig is always new reference

The serializedConfig property is a getter that serializes the config every time it is accessed, meaning it always returns a different reference. Therefore, project.serializedConfig === project.serializedConfig will return false.

TestProject.globTestFiles filter restrictions

Filters in globTestFiles can only be a part of the file path, unlike in other methods. Valid example: project.globTestFiles(['foo']). Invalid example: project.globTestFiles(['basic/foo.js:10'])

TestProject.vitest property

The vitest property on a TestProject references the global Vitest process.

TestProject name resolution

The TestProject name is a unique string assigned by the user or interpreted by Vitest. If the user does not provide a name, Vitest tries to load a package.json in the root of the project and takes the name property from there. If there is no package.json, Vitest uses the name of the folder by default. Inline projects use numbers as the name (converted to string).

Give your agent this brain