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
2 changes: 1 addition & 1 deletion docs/guide/test-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -891,7 +891,7 @@ This applies to all suite-level hooks: `beforeAll`, `afterAll`, and `aroundAll`.
:::

::: tip
Suite-level hooks can only access [**file-scoped** and **worker-scoped** fixtures](#fixture-scopes). Test-scoped fixtures are not available in these hooks because they run outside the context of individual tests. If you try to access a test-scoped fixture in a suite-level hook, Vitest will throw an error.
Suite-level hooks can only access [**file-scoped** and **worker-scoped** fixtures](#fixture-scopes), including `auto` fixtures. Test-scoped fixtures are not available in these hooks because they run outside the context of individual tests. If you try to access a test-scoped fixture in a suite-level hook, Vitest will throw an error.

```ts
const test = baseTest
Expand Down
13 changes: 12 additions & 1 deletion packages/runner/src/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ export function withFixtures(fn: Function, options?: WithFixturesOptions) {
const usedProps = getUsedProps(fn)

for (const fixture of registrations.values()) {
if (fixture.auto || usedProps.has(fixture.name)) {
if (isAutoFixture(fixture, options) || usedProps.has(fixture.name)) {
usedFixtures.push(fixture)
}
}
Expand Down Expand Up @@ -421,6 +421,17 @@ export function withFixtures(fn: Function, options?: WithFixturesOptions) {
}
}

function isAutoFixture(fixture: TestFixtureItem, options?: WithFixturesOptions): boolean {
if (!fixture.auto) {
return false
}
// suite hook doesn't automatically trigger unused test-scoped fixtures.
if (options?.suiteHook && fixture.scope === 'test') {
return false
}
return true
}

function isFixtureFunction(value: unknown): value is FixtureFn<any, any, any> {
return typeof value === 'function'
}
Expand Down
3 changes: 2 additions & 1 deletion packages/runner/src/types/tasks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,8 @@ export interface InternalTestContext extends Record<

export interface FixtureOptions {
/**
* Whether to automatically set up current fixture, even though it's not being used in tests.
* Whether to automatically set up current fixture, even though it's not being used.
* Test-scoped auto fixtures are not initialized in suite-level hooks (`beforeAll`/`afterAll`/`aroundAll`).
* @default false
*/
auto?: boolean
Expand Down
122 changes: 120 additions & 2 deletions test/cli/test/scoped-fixtures.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -981,6 +981,56 @@ test('nested fixtures with different scopes work correctly in hooks', async () =
`)
})

test('auto worker fixture is available in beforeAll', async () => {
const { stderr, fixtures, tests } = await runFixtureTests(({ log }) => {
return it.extend('workerValue', { scope: 'worker', auto: true }, ({}, { onCleanup }) => {
log('workerValue init')
onCleanup(() => log('workerValue teardown'))
return 'worker'
})
}, {
'basic.test.ts': ({ extendedTest, log }) => {
extendedTest.beforeAll(({ workerValue }) => {
log('beforeAll | worker:', workerValue)
})
extendedTest('test1', ({}) => {})
},
})

expect(stderr).toMatchInlineSnapshot(`""`)
expect(fixtures).toMatchInlineSnapshot(`
">> fixture | workerValue init | undefined
>> fixture | beforeAll | worker: worker | undefined
>> fixture | workerValue teardown | test1"
`)
expect(tests).toMatchInlineSnapshot(`" ✓ basic.test.ts > test1 <time>"`)
})

test('auto file fixture is available in beforeAll', async () => {
const { stderr, fixtures, tests } = await runFixtureTests(({ log }) => {
return it.extend('fileValue', { scope: 'file', auto: true }, ({}, { onCleanup }) => {
log('fileValue init')
onCleanup(() => log('fileValue teardown'))
return 'file'
})
}, {
'basic.test.ts': ({ extendedTest, log }) => {
extendedTest.beforeAll(({ fileValue }) => {
log('beforeAll | file:', fileValue)
})
extendedTest('test1', ({}) => {})
},
})

expect(stderr).toMatchInlineSnapshot(`""`)
expect(fixtures).toMatchInlineSnapshot(`
">> fixture | fileValue init | undefined
>> fixture | beforeAll | file: file | undefined
>> fixture | fileValue teardown | test1"
`)
expect(tests).toMatchInlineSnapshot(`" ✓ basic.test.ts > test1 <time>"`)
})

test('auto file fixture is initialised always before the first test', async () => {
const { stderr, fixtures, tests } = await runFixtureTests(({ log }) => it.extend<{
file: string
Expand Down Expand Up @@ -2339,6 +2389,72 @@ describe('builder pattern API with automatic type inference', () => {
`)
})

test('non-auto test fixture does works with beforeAll worker fixture', async () => {
const { stderr, fixtures, tests } = await runFixtureTests(({ log }) => {
return it
.extend('testValue', async () => {
log('testValue setup')
return 'test'
})
.extend('workerValue', { scope: 'worker' }, async () => {
log('workerValue setup')
return 'worker'
})
}, {
'basic.test.ts': ({ extendedTest, log }) => {
extendedTest.beforeAll(({ workerValue }) => {
log('beforeAll:', { workerValue })
})

extendedTest('test', ({ workerValue, testValue }) => {
log('test:', { workerValue, testValue })
})
},
})

expect(stderr).toMatchInlineSnapshot(`""`)
expect(fixtures).toMatchInlineSnapshot(`
">> fixture | workerValue setup | undefined
>> fixture | beforeAll: { workerValue: 'worker' } | undefined
>> fixture | testValue setup | test
>> fixture | test: { workerValue: 'worker', testValue: 'test' } | test"
`)
expect(tests).toMatchInlineSnapshot(`" ✓ basic.test.ts > test <time>"`)
})

test('auto test fixture works with beforeAll worker fixture', async () => {
const { stderr, fixtures, tests } = await runFixtureTests(({ log }) => {
return it
.extend('testValue', { auto: true }, async () => {
log('testValue setup')
return 'test'
})
.extend('workerValue', { scope: 'worker' }, async () => {
log('workerValue setup')
return 'worker'
})
}, {
'basic.test.ts': ({ extendedTest, log }) => {
extendedTest.beforeAll(({ workerValue }) => {
log('beforeAll:', { workerValue })
})

extendedTest('test', ({ workerValue, testValue }) => {
log('test:', { workerValue, testValue })
})
},
})

expect(stderr).toMatchInlineSnapshot(`""`)
expect(fixtures).toMatchInlineSnapshot(`
">> fixture | workerValue setup | undefined
>> fixture | beforeAll: { workerValue: 'worker' } | undefined
>> fixture | testValue setup | test
>> fixture | test: { workerValue: 'worker', testValue: 'test' } | test"
`)
expect(tests).toMatchInlineSnapshot(`" ✓ basic.test.ts > test <time>"`)
})

test('cleanup error is reported', async () => {
const { stderr, fixtures } = await runFixtureTests(({ log }) => {
return it.extend('resource', async ({}, { onCleanup }) => {
Expand Down Expand Up @@ -2882,6 +2998,7 @@ async function runFixtureTests<T>(
describe: SuiteAPI
beforeAll: typeof beforeAll
afterAll: typeof afterAll
log: typeof console.log
}) => unknown) | ViteUserConfig>,
config?: TestUserConfig,
) {
Expand All @@ -2893,17 +3010,18 @@ async function runFixtureTests<T>(
export const describe = globalThis.describe
export const expect = globalThis.expect
export const expectTypeOf = globalThis.expectTypeOf
export const extendedTest = (${stripIndent(extendedTest.toString())})({ log: (...args) => console.log('>> fixture |', ...args, '| ' + expect.getState().currentTestName), expectTypeOf })
export const extendedTest = (${stripIndent(extendedTest.toString())})({ log, expectTypeOf })
export const beforeAll = globalThis.beforeAll
export const afterAll = globalThis.afterAll
export function log(...args) { console.log('>> fixture |', ...args, '| ' + expect.getState().currentTestName) }
`,
'vitest.config.js': { test: { globals: true } },
...Object.entries(fs).reduce((acc, [key, value]) => {
if (typeof value === 'object' && !Array.isArray(value)) {
acc[key] = value
}
if (typeof value === 'function') {
acc[key] = [value, { imports: { './test.js': ['extendedTest', 'expect', 'expectTypeOf', 'describe', 'beforeAll', 'afterAll'] } }]
acc[key] = [value, { imports: { './test.js': ['extendedTest', 'expect', 'expectTypeOf', 'describe', 'beforeAll', 'afterAll', 'log'] } }]
}
return acc
}, {} as TestFsStructure),
Expand Down
Loading