Annotations built on artifacts
Test annotations are built on top of the artifact system. When using annotations in tests, they create internal:annotation artifacts under the hood. Annotations won't appear in the task.artifacts array for backwards compatibility reasons until the next major version. Use annotations if you just want to add notes to tests; use artifacts if you need custom data.
TestArtifact type definition
TestArtifact type is a union containing all artifacts Vitest can produce, including custom ones. All artifacts extend from TestArtifactBase.
Test Artifacts overview and purpose
Test artifacts allow attaching or recording structured data, files, or metadata during test execution. Each artifact includes a type discriminator (unique identifier for artifact type), custom data (any relevant information), optional attachments (files or inline content), and source code location. Test artifacts are a low-level feature primarily designed for internal use and framework authors creating custom testing tools on top of Vitest.
TestCase artifacts() method returns test artifacts experimental 4.0.11
The artifacts() method has signature function artifacts(): ReadonlyArray<TestArtifact> and returns test artifacts recorded via the recordArtifact API during test execution. This feature is experimental and available since Vitest 4.0.11.
moduleId property format
The moduleId property is usually an absolute unix file path, even on Windows. It can be a virtual id if the file is not on the disk. This value corresponds to Vite's ModuleGraph id. Valid formats use forward slashes: 'C:/Users/Documents/project/example.test.ts' and '/Users/mac/project/example.test.ts' are valid. Backslashes like 'C:\\Users\\Documents\\project\\example.test.ts' are invalid.
relativeModuleId property
The relativeModuleId property is the module id relative to the project. This is the same as task.name in the deprecated API. Valid formats use forward slashes such as 'project/example.test.ts' or 'example.test.ts'. Backslashes like 'project\\example.test.ts' are invalid.
viteEnvironment property
The viteEnvironment property is a Vite's DevEnvironment that transforms all files inside of the test module. This was added as experimental in v4.0.15 and released in v4.1.0.
TestModule.state() method signature
The state() method has the signature: function state(): TestModuleState. It works the same way as testSuite.state(), but can also return 'queued' if the module was not executed yet.
TestModule.meta() method signature
The meta() method has the signature: function meta(): TaskMeta. It returns custom metadata that was attached to the module during its execution or collection. Metadata can be attached by assigning a property to the task.meta object during a test run. If metadata was attached during collection (outside of the test function), it will be available in the onTestModuleCollected hook in custom reporters.
TestModule.diagnostic() method signature and ModuleDiagnostic interface
The diagnostic() method has the signature: function diagnostic(): ModuleDiagnostic. The ModuleDiagnostic interface includes the following readonly properties: environmentSetupDuration (number, time to import and initiate environment), prepareDuration (number, time for Vitest to setup test harness), collectDuration (number, time to import test module including suite callbacks), setupDuration (number, time to import setup module), duration (number, accumulated duration of all tests and hooks), heap (number | undefined, memory in bytes only if logHeapUsage flag used), importDurations (Record<string, ImportDuration>, time spent importing non-externalized dependencies), concurrencyId (number, worker id not higher than maxWorkers, 0 if not run yet), and workerId (number, incremental worker number, 0 if not run yet). Node.js and browser tests use different pools and cannot share concurrencyId or workerId.
ImportDuration interface properties
The ImportDuration interface has two properties: selfTime (number, time spent importing and executing the file itself, not counting non-externalized imports the file does) and totalTime (number, time spent importing and executing the file and all its imports).
TestModule.logs() method signature
The logs() method has the signature: function logs(): ReadonlyArray<UserConsoleLog>. It returns console logs recorded at the top level of the module during test collection. Logs inside describe blocks or test functions are not included.
TestModule.toTestSpecification() method signature
The toTestSpecification() method has the signature: function toTestSpecification(testCases?: TestCase[]): TestSpecification. It returns a new test specification that can be used to filter or run this specific test module. It accepts an optional array of test cases that should be filtered.
TestModule class available only in main thread
The TestModule class represents a single module in a single project and is only available in the main thread. For runtime tasks, refer to the Runner API instead.
TestModule type property value
The TestModule instance always has a type property with the value of 'module'. This can be used to distinguish between different task types by checking if task.type === 'module'.
TestModule inherits from TestSuite
The TestModule class inherits all methods and properties from the TestSuite class.
vitest-test-writer agent purpose and scope
The vitest-test-writer agent is used when the user needs to write comprehensive tests for Vitest features. This includes unit tests for individual functions, integration tests for CLI functionality, and browser mode tests. The agent should be invoked when implementing new features that require test coverage, fixing bugs that need regression tests, or expanding test coverage for existing functionality.
Unit tests location and purpose
Unit tests should be placed in test/unit/. These tests import individual functions directly regardless of which package defines them, and test pure functionality without process spawning. They should cover edge cases, error conditions, and typical usage with descriptive test names.
Integration tests location and purpose
Integration tests should be placed in test/e2e/. These tests validate CLI functionality and features that require running Vitest as a process. They use the runInlineTests utility to define and run test scenarios.
Browser mode tests location
Browser mode tests should be placed in test/browser/. However, if a feature supports both normal tests and browser tests, the tests should be placed in test/e2e/ instead.
runInlineTests utility for integration testing
For integration tests, the runInlineTests utility should always be used to create and run test scenarios. This utility allows defining inline test files and validating their output.
toMatchInlineSnapshot for output validation
Snapshot validation should always use toMatchInlineSnapshot(). The snapshot is automatically generated on the first run. This method is preferred because it captures the exact expected output, makes changes visible in code review, and catches regressions precisely.
Avoid using toContain for output validation
Do not use toContain() for output validation in integration tests. This method fails to catch extra unexpected output, repeated output that shouldn't occur, and subtle formatting differences. Use toMatchInlineSnapshot() instead.
Handling dynamic content in test output
When output contains dynamic content such as timestamps, absolute paths, durations, or process IDs: first check test-utils for existing utilities that normalize this content, then if no utility exists, manually process with stdout.replace() using appropriate regular expressions. Common patterns to normalize include timing information (e.g., 1.234s → [time]), root paths (e.g., /Users/name/project → <root>), and process IDs or temporary file paths.
Validating test results with testTree and errorTree
To ensure all tests actually passed and not just that they ran, use testTree or errorTree helper functions. Pass the result to toMatchInlineSnapshot() to verify the correct number of tests ran, tests are organized in expected suites, and no unexpected failures or skipped tests occurred.
Test quality standards
Every test should have a clear purpose with descriptive names explaining the behavior being verified. Related tests should be grouped in describe blocks. Include both positive (happy path) and negative (error) test cases. Consider boundary conditions and edge cases. Tests should be independent and not rely on execution order.
Handling bugs discovered during test writing
If a bug in the behaviour is encountered while writing tests, write a failing test and report that there is a bug or unexpected behaviour. Delegate fixing the bug to the main agent if possible.
Integration test pattern with runInlineTests
When writing integration tests using runInlineTests, define realistic test file content, validate both stderr and the test results structure, test error scenarios and edge cases, and ensure tests are deterministic with no flaky behaviour.
Unit test writing pattern
For unit tests in test/unit/, import the function directly from its source package and test pure functionality without process spawning. Cover edge cases, error conditions, and typical usage with clear test names.