-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat: Add meta to TestOptions
#8405
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -190,6 +190,28 @@ function assert(condition: any, message: string) { | |
| } | ||
| } | ||
|
|
||
| function collectAncestorMeta(suite: Suite | undefined): Record<string, unknown> { | ||
| const ancestorMeta = Object.create(null) | ||
| let current = suite | ||
|
|
||
| // Walk up the suite hierarchy and collect metadata | ||
| // We'll collect from root to child so that closer ancestors override distant ones | ||
| const suites: Suite[] = [] | ||
| while (current) { | ||
| if (current.meta) { | ||
| suites.unshift(current) // Add to beginning so we process from root to child | ||
| } | ||
| current = current.suite | ||
| } | ||
|
|
||
| // Merge metadata with closer ancestors having higher priority | ||
| for (const s of suites) { | ||
| Object.assign(ancestorMeta, s.meta) | ||
| } | ||
|
|
||
| return ancestorMeta | ||
| } | ||
|
|
||
| export function getDefaultSuite(): SuiteCollector<object> { | ||
| assert(defaultSuite, 'the default suite') | ||
| return defaultSuite | ||
|
|
@@ -325,7 +347,11 @@ function createSuiteCollector( | |
| : options.todo | ||
| ? 'todo' | ||
| : 'run', | ||
| meta: options.meta ?? Object.create(null), | ||
| meta: { | ||
| ...Object.create(null), | ||
|
rebasecase marked this conversation as resolved.
Outdated
|
||
| ...collectAncestorMeta(collectorContext.currentSuite?.suite), | ||
| ...(options.meta || {}), | ||
| }, | ||
| annotations: [], | ||
| } | ||
| const handler = options.handler | ||
|
|
@@ -457,7 +483,10 @@ function createSuiteCollector( | |
| file: undefined!, | ||
| shuffle: suiteOptions?.shuffle, | ||
| tasks: [], | ||
| meta: Object.create(null), | ||
| meta: { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here |
||
| ...Object.create(null), | ||
|
rebasecase marked this conversation as resolved.
Outdated
|
||
| ...(suiteOptions?.meta || {}), | ||
| }, | ||
| concurrent: suiteOptions?.concurrent, | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
|
|
@@ -485,6 +485,10 @@ export interface TestOptions { | |||
| * Whether the test is expected to fail. If it does, the test will pass, otherwise it will fail. | ||||
| */ | ||||
| fails?: boolean | ||||
| /** | ||||
| * Custom metadata for the task. This will be merged with any meta property defined in the test. | ||||
| */ | ||||
| meta?: Record<string, unknown> | ||||
|
rebasecase marked this conversation as resolved.
Outdated
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This needs to be
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some places it is
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. e.g. vitest/packages/vite-node/src/types.ts Line 99 in c1ac15c
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't know what to do about this. Setting I don't understand complex typing much yet. |
||||
| } | ||||
|
|
||||
| interface ExtendedAPI<ExtraContext> { | ||||
|
|
||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| import { beforeEach, describe, expect, test } from 'vitest' | ||
|
|
||
| describe('TestOptions meta property functionality', { meta: { suiteLevel: 'test-suite', priority: 'medium' } }, () => { | ||
| let beforeEachMeta: Record<string, unknown> | ||
|
|
||
| beforeEach(({ task }) => { | ||
| beforeEachMeta = { ...task.meta } | ||
| }) | ||
|
|
||
| test('should merge suite and test meta properties', { meta: { testLevel: 'individual-test', priority: 'high' } }, ({ task }) => { | ||
| // Test should have both suite and test meta | ||
| expect(task.meta).toMatchObject({ | ||
| suiteLevel: 'test-suite', | ||
| testLevel: 'individual-test', | ||
| priority: 'high', // test meta should override suite meta | ||
| }) | ||
|
|
||
| // beforeEach should have access to merged meta | ||
| expect(beforeEachMeta).toMatchObject({ | ||
| suiteLevel: 'test-suite', | ||
| testLevel: 'individual-test', | ||
| priority: 'high', | ||
| }) | ||
| }) | ||
|
|
||
| test('should inherit suite meta when no test meta provided', ({ task }) => { | ||
| // Test should only have suite meta | ||
| expect(task.meta).toMatchObject({ | ||
| suiteLevel: 'test-suite', | ||
| priority: 'medium', | ||
| }) | ||
|
|
||
| // beforeEach should have access to suite meta | ||
| expect(beforeEachMeta).toMatchObject({ | ||
| suiteLevel: 'test-suite', | ||
| priority: 'medium', | ||
| }) | ||
| }) | ||
|
|
||
| test('should allow adding meta at runtime', { meta: { testLevel: 'runtime-test' } }, ({ task }) => { | ||
| // Add meta at runtime | ||
| (task.meta as any).runtimeAdded = 'added-during-test' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's add a local type instead of assigning
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With meta now being
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was wrong 😿 |
||
|
|
||
| expect(task.meta).toMatchObject({ | ||
| suiteLevel: 'test-suite', | ||
| testLevel: 'runtime-test', | ||
| priority: 'medium', | ||
| runtimeAdded: 'added-during-test', | ||
| }) | ||
| }) | ||
|
|
||
| test('should differentiate between task.meta and task.suite.meta', { meta: { testLevel: 'child-test', priority: 'high' } }, ({ task }) => { | ||
| // task.meta should contain merged metadata (suite + test) | ||
| expect(task.meta).toMatchObject({ | ||
| suiteLevel: 'test-suite', | ||
| testLevel: 'child-test', | ||
| priority: 'high', // test overrides suite | ||
| }) | ||
|
|
||
| // task.suite.meta should contain only suite's own metadata | ||
| expect(task.suite?.meta).toMatchObject({ | ||
| suiteLevel: 'test-suite', | ||
| priority: 'medium', // original suite priority | ||
| }) | ||
|
|
||
| // They should be different objects | ||
| expect(task.meta).not.toBe(task.suite?.meta) | ||
|
|
||
| // task.suite.meta should NOT have test-specific metadata | ||
| expect(task.suite?.meta).not.toHaveProperty('testLevel') | ||
| }) | ||
| }) | ||
|
|
||
| describe('Suite without meta', () => { | ||
| let beforeEachMeta: Record<string, unknown> | ||
|
|
||
| beforeEach(({ task }) => { | ||
| beforeEachMeta = { ...task.meta } | ||
| }) | ||
|
|
||
| test('should only have test meta when suite has no meta', { meta: { testOnly: 'test-meta' } }, ({ task }) => { | ||
| expect(task.meta).toMatchObject({ | ||
| testOnly: 'test-meta', | ||
| }) | ||
|
|
||
| expect(beforeEachMeta).toMatchObject({ | ||
| testOnly: 'test-meta', | ||
| }) | ||
| }) | ||
| }) | ||
|
|
||
| describe('Nested describes metadata cascading', { meta: { grandparent: 'top-level', priority: 'low' } }, () => { | ||
| describe('Middle suite', { meta: { parent: 'middle-level', priority: 'medium' } }, () => { | ||
| test('should cascade metadata from all ancestor suites', ({ task }) => { | ||
| // Should now get metadata from all ancestors: grandparent + parent | ||
| expect(task.meta).toMatchObject({ | ||
| grandparent: 'top-level', // from grandparent suite | ||
| parent: 'middle-level', // from parent suite | ||
| priority: 'medium', // parent overrides grandparent | ||
| }) | ||
|
|
||
| // Original suite metadata should be preserved | ||
| expect(task.suite?.meta).toMatchObject({ | ||
| parent: 'middle-level', | ||
| priority: 'medium', | ||
| }) | ||
|
|
||
| // Grandparent suite metadata should also be preserved | ||
| expect(task.suite?.suite?.meta).toMatchObject({ | ||
| grandparent: 'top-level', | ||
| priority: 'low', | ||
| }) | ||
| }) | ||
|
|
||
| test('test metadata should override cascaded suite metadata', { meta: { testLevel: 'child', priority: 'highest' } }, ({ task }) => { | ||
| // Should get metadata from all ancestors plus test metadata | ||
| expect(task.meta).toMatchObject({ | ||
| grandparent: 'top-level', // from grandparent suite | ||
| parent: 'middle-level', // from parent suite | ||
| testLevel: 'child', // from test | ||
| priority: 'highest', // test overrides all ancestors | ||
| }) | ||
| }) | ||
| }) | ||
| }) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep the meta with a null prototype. Just assign
options.metato meta collected withcollectAncestorMeta: