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: 7 additions & 1 deletion docs/api/advanced/test-case.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,13 @@ test('the validation works correctly', ({ task }) => {
})
```

If the test did not finish running yet, the meta will be an empty object.
If the test did not finish running yet, the meta will be an empty object, unless it has static meta:

```ts
test('the validation works correctly', { meta: { decorated: true } })
```

Since Vitest 4.1, Vitest inherits [`meta`](/api/advanced/test-suite#meta) property defined on the [suite](/api/advanced/test-suite).

## result

Expand Down
15 changes: 8 additions & 7 deletions docs/api/advanced/test-suite.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,24 +198,25 @@ Note that errors are serialized into simple objects: `instanceof Error` will alw
function meta(): TaskMeta
```

Custom [metadata](/api/advanced/metadata) that was attached to the suite during its execution or collection. The meta can be attached by assigning a property to the `suite.meta` object during a test run:
Custom [metadata](/api/advanced/metadata) that was attached to the suite during its execution or collection. Since Vitest 4.1, the meta can be attached by providing a `meta` object during test collection:

```ts {7,12}
```ts {7,10}
import { describe, test, TestRunner } from 'vitest'

describe('the validation works correctly', () => {
// assign "decorated" during collection
const { suite } = TestRunner.getCurrentSuite()
suite!.meta.decorated = true

describe('the validation works correctly', { meta: { decorated: true } }, () => {
test('some test', ({ task }) => {
// assign "decorated" during test run, it will be available
// only in onTestCaseReady hook
task.suite.meta.decorated = false

// tests inherit suite's metadata
task.meta.decorated === true
})
})
```

Note that suite metadata will be inherited by tests since Vitest 4.1.

:::tip
If metadata was attached during collection (outside of the `test` function), then it will be available in [`onTestModuleCollected`](./reporters#ontestmodulecollected) hook in the custom reporter.
:::
Expand Down
39 changes: 39 additions & 0 deletions docs/api/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,45 @@ it('user returns data from db', { tags: ['db', 'flaky'] }, () => {
})
```

### meta <Version>4.1.0</Version> {#meta}

- **Type:** `TaskMeta`

Attaches custom [metadata](/api/advanced/metadata) available in reporters.

::: warning
Vitest merges top-level properties inherited from suites or tags. However, it does not perform a deep merge of nested objects.

```ts
import { describe, test } from 'vitest'

describe(
'nested meta',
{
meta: {
nested: { object: true, array: false },
},
},
() => {
test(
'overrides part of meta',
{
meta: {
nested: { object: false }
},
},
({ task }) => {
// task.meta === { nested: { object: false } }
// notice array got lost because "nested" object was overriden
}
)
}
)
```

Prefer using non-nested meta, if possible.
:::

### concurrent

- **Type:** `boolean`
Expand Down
29 changes: 25 additions & 4 deletions packages/runner/src/suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,16 +331,32 @@ function createSuiteCollector(
// higher priority should be last, run 1, 2, 3, ... etc
.sort((tag1, tag2) => (tag2.priority ?? POSITIVE_INFINITY) - (tag1.priority ?? POSITIVE_INFINITY))
.reduce((acc, tag) => {
const { name, description, priority, ...options } = tag
const { name, description, priority, meta, ...options } = tag
Object.assign(acc, options)
if (meta) {
acc.meta = Object.assign(acc.meta ?? Object.create(null), meta)
}
return acc
}, {} as TestOptions)

const testOwnMeta = options.meta
options = {
...tagsOptions,
...options,
}
const timeout = options.timeout ?? runner.config.testTimeout
const parentMeta = currentSuite?.meta
const tagMeta = tagsOptions.meta
const testMeta = Object.create(null)
if (tagMeta) {
Object.assign(testMeta, tagMeta)
}
if (parentMeta) {
Object.assign(testMeta, parentMeta)
}
if (testOwnMeta) {
Object.assign(testMeta, testOwnMeta)
}
const task: Test = {
id: '',
name,
Expand All @@ -365,7 +381,7 @@ function createSuiteCollector(
: options.todo
? 'todo'
: 'run',
meta: options.meta ?? Object.create(null),
meta: testMeta,
annotations: [],
artifacts: [],
tags: testTags,
Expand Down Expand Up @@ -513,7 +529,7 @@ function createSuiteCollector(
file: (currentSuite?.file ?? collectorContext.currentSuite?.file)!,
shuffle: suiteOptions?.shuffle,
tasks: [],
meta: Object.create(null),
meta: suiteOptions?.meta ?? Object.create(null),
concurrent: suiteOptions?.concurrent,
tags: unique([...parentTask?.tags || [], ...suiteTags]),
}
Expand Down Expand Up @@ -604,9 +620,10 @@ function createSuite() {
const isConcurrentSpecified = options.concurrent || this.concurrent || options.sequential === false
const isSequentialSpecified = options.sequential || this.sequential || options.concurrent === false

const { meta: parentMeta, ...parentOptions } = currentSuite?.options || {}
// inherit options from current suite
options = {
...currentSuite?.options,
...parentOptions,
...options,
}

Expand Down Expand Up @@ -638,6 +655,10 @@ function createSuite() {
options.sequential = isSequential && !isConcurrent
}

if (parentMeta) {
options.meta = Object.assign(Object.create(null), parentMeta, options.meta)
}

return createSuiteCollector(
formatName(name),
factory,
Expand Down
8 changes: 4 additions & 4 deletions packages/runner/src/types/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,10 @@ export interface TestOptions {
tags?: keyof TestTags extends never
? string[] | string
: TestTags[keyof TestTags] | TestTags[keyof TestTags][]
/**
* Custom test metadata available to reporters.
*/
meta?: Partial<TaskMeta>
}

export interface TestTags {}
Expand Down Expand Up @@ -735,10 +739,6 @@ export interface TaskCustomOptions extends TestOptions {
* Whether the task was produced with `.each()` method.
*/
each?: boolean
/**
* Custom metadata for the task that will be assigned to `task.meta`.
*/
meta?: Record<string, unknown>
/**
* Task fixtures.
*/
Expand Down
Loading
Loading