Skip to content

Commit 398657e

Browse files
hi-ogawaclaude
andauthored
fix: fix suite hook throwing errors for unused auto test-scoped fixture (#10035)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 5a5fa49 commit 398657e

File tree

4 files changed

+135
-5
lines changed

4 files changed

+135
-5
lines changed

docs/guide/test-context.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,7 @@ This applies to all suite-level hooks: `beforeAll`, `afterAll`, and `aroundAll`.
891891
:::
892892

893893
::: tip
894-
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.
894+
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.
895895

896896
```ts
897897
const test = baseTest

packages/runner/src/fixture.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ export function withFixtures(fn: Function, options?: WithFixturesOptions) {
337337
const usedProps = getUsedProps(fn)
338338

339339
for (const fixture of registrations.values()) {
340-
if (fixture.auto || usedProps.has(fixture.name)) {
340+
if (isAutoFixture(fixture, options) || usedProps.has(fixture.name)) {
341341
usedFixtures.push(fixture)
342342
}
343343
}
@@ -421,6 +421,17 @@ export function withFixtures(fn: Function, options?: WithFixturesOptions) {
421421
}
422422
}
423423

424+
function isAutoFixture(fixture: TestFixtureItem, options?: WithFixturesOptions): boolean {
425+
if (!fixture.auto) {
426+
return false
427+
}
428+
// suite hook doesn't automatically trigger unused test-scoped fixtures.
429+
if (options?.suiteHook && fixture.scope === 'test') {
430+
return false
431+
}
432+
return true
433+
}
434+
424435
function isFixtureFunction(value: unknown): value is FixtureFn<any, any, any> {
425436
return typeof value === 'function'
426437
}

packages/runner/src/types/tasks.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,8 @@ export interface InternalTestContext extends Record<
808808

809809
export interface FixtureOptions {
810810
/**
811-
* Whether to automatically set up current fixture, even though it's not being used in tests.
811+
* Whether to automatically set up current fixture, even though it's not being used.
812+
* Test-scoped auto fixtures are not initialized in suite-level hooks (`beforeAll`/`afterAll`/`aroundAll`).
812813
* @default false
813814
*/
814815
auto?: boolean

test/cli/test/scoped-fixtures.test.ts

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -981,6 +981,56 @@ test('nested fixtures with different scopes work correctly in hooks', async () =
981981
`)
982982
})
983983

984+
test('auto worker fixture is available in beforeAll', async () => {
985+
const { stderr, fixtures, tests } = await runFixtureTests(({ log }) => {
986+
return it.extend('workerValue', { scope: 'worker', auto: true }, ({}, { onCleanup }) => {
987+
log('workerValue init')
988+
onCleanup(() => log('workerValue teardown'))
989+
return 'worker'
990+
})
991+
}, {
992+
'basic.test.ts': ({ extendedTest, log }) => {
993+
extendedTest.beforeAll(({ workerValue }) => {
994+
log('beforeAll | worker:', workerValue)
995+
})
996+
extendedTest('test1', ({}) => {})
997+
},
998+
})
999+
1000+
expect(stderr).toMatchInlineSnapshot(`""`)
1001+
expect(fixtures).toMatchInlineSnapshot(`
1002+
">> fixture | workerValue init | undefined
1003+
>> fixture | beforeAll | worker: worker | undefined
1004+
>> fixture | workerValue teardown | test1"
1005+
`)
1006+
expect(tests).toMatchInlineSnapshot(`" ✓ basic.test.ts > test1 <time>"`)
1007+
})
1008+
1009+
test('auto file fixture is available in beforeAll', async () => {
1010+
const { stderr, fixtures, tests } = await runFixtureTests(({ log }) => {
1011+
return it.extend('fileValue', { scope: 'file', auto: true }, ({}, { onCleanup }) => {
1012+
log('fileValue init')
1013+
onCleanup(() => log('fileValue teardown'))
1014+
return 'file'
1015+
})
1016+
}, {
1017+
'basic.test.ts': ({ extendedTest, log }) => {
1018+
extendedTest.beforeAll(({ fileValue }) => {
1019+
log('beforeAll | file:', fileValue)
1020+
})
1021+
extendedTest('test1', ({}) => {})
1022+
},
1023+
})
1024+
1025+
expect(stderr).toMatchInlineSnapshot(`""`)
1026+
expect(fixtures).toMatchInlineSnapshot(`
1027+
">> fixture | fileValue init | undefined
1028+
>> fixture | beforeAll | file: file | undefined
1029+
>> fixture | fileValue teardown | test1"
1030+
`)
1031+
expect(tests).toMatchInlineSnapshot(`" ✓ basic.test.ts > test1 <time>"`)
1032+
})
1033+
9841034
test('auto file fixture is initialised always before the first test', async () => {
9851035
const { stderr, fixtures, tests } = await runFixtureTests(({ log }) => it.extend<{
9861036
file: string
@@ -2339,6 +2389,72 @@ describe('builder pattern API with automatic type inference', () => {
23392389
`)
23402390
})
23412391

2392+
test('non-auto test fixture does works with beforeAll worker fixture', async () => {
2393+
const { stderr, fixtures, tests } = await runFixtureTests(({ log }) => {
2394+
return it
2395+
.extend('testValue', async () => {
2396+
log('testValue setup')
2397+
return 'test'
2398+
})
2399+
.extend('workerValue', { scope: 'worker' }, async () => {
2400+
log('workerValue setup')
2401+
return 'worker'
2402+
})
2403+
}, {
2404+
'basic.test.ts': ({ extendedTest, log }) => {
2405+
extendedTest.beforeAll(({ workerValue }) => {
2406+
log('beforeAll:', { workerValue })
2407+
})
2408+
2409+
extendedTest('test', ({ workerValue, testValue }) => {
2410+
log('test:', { workerValue, testValue })
2411+
})
2412+
},
2413+
})
2414+
2415+
expect(stderr).toMatchInlineSnapshot(`""`)
2416+
expect(fixtures).toMatchInlineSnapshot(`
2417+
">> fixture | workerValue setup | undefined
2418+
>> fixture | beforeAll: { workerValue: 'worker' } | undefined
2419+
>> fixture | testValue setup | test
2420+
>> fixture | test: { workerValue: 'worker', testValue: 'test' } | test"
2421+
`)
2422+
expect(tests).toMatchInlineSnapshot(`" ✓ basic.test.ts > test <time>"`)
2423+
})
2424+
2425+
test('auto test fixture works with beforeAll worker fixture', async () => {
2426+
const { stderr, fixtures, tests } = await runFixtureTests(({ log }) => {
2427+
return it
2428+
.extend('testValue', { auto: true }, async () => {
2429+
log('testValue setup')
2430+
return 'test'
2431+
})
2432+
.extend('workerValue', { scope: 'worker' }, async () => {
2433+
log('workerValue setup')
2434+
return 'worker'
2435+
})
2436+
}, {
2437+
'basic.test.ts': ({ extendedTest, log }) => {
2438+
extendedTest.beforeAll(({ workerValue }) => {
2439+
log('beforeAll:', { workerValue })
2440+
})
2441+
2442+
extendedTest('test', ({ workerValue, testValue }) => {
2443+
log('test:', { workerValue, testValue })
2444+
})
2445+
},
2446+
})
2447+
2448+
expect(stderr).toMatchInlineSnapshot(`""`)
2449+
expect(fixtures).toMatchInlineSnapshot(`
2450+
">> fixture | workerValue setup | undefined
2451+
>> fixture | beforeAll: { workerValue: 'worker' } | undefined
2452+
>> fixture | testValue setup | test
2453+
>> fixture | test: { workerValue: 'worker', testValue: 'test' } | test"
2454+
`)
2455+
expect(tests).toMatchInlineSnapshot(`" ✓ basic.test.ts > test <time>"`)
2456+
})
2457+
23422458
test('cleanup error is reported', async () => {
23432459
const { stderr, fixtures } = await runFixtureTests(({ log }) => {
23442460
return it.extend('resource', async ({}, { onCleanup }) => {
@@ -2882,6 +2998,7 @@ async function runFixtureTests<T>(
28822998
describe: SuiteAPI
28832999
beforeAll: typeof beforeAll
28843000
afterAll: typeof afterAll
3001+
log: typeof console.log
28853002
}) => unknown) | ViteUserConfig>,
28863003
config?: TestUserConfig,
28873004
) {
@@ -2893,17 +3010,18 @@ async function runFixtureTests<T>(
28933010
export const describe = globalThis.describe
28943011
export const expect = globalThis.expect
28953012
export const expectTypeOf = globalThis.expectTypeOf
2896-
export const extendedTest = (${stripIndent(extendedTest.toString())})({ log: (...args) => console.log('>> fixture |', ...args, '| ' + expect.getState().currentTestName), expectTypeOf })
3013+
export const extendedTest = (${stripIndent(extendedTest.toString())})({ log, expectTypeOf })
28973014
export const beforeAll = globalThis.beforeAll
28983015
export const afterAll = globalThis.afterAll
3016+
export function log(...args) { console.log('>> fixture |', ...args, '| ' + expect.getState().currentTestName) }
28993017
`,
29003018
'vitest.config.js': { test: { globals: true } },
29013019
...Object.entries(fs).reduce((acc, [key, value]) => {
29023020
if (typeof value === 'object' && !Array.isArray(value)) {
29033021
acc[key] = value
29043022
}
29053023
if (typeof value === 'function') {
2906-
acc[key] = [value, { imports: { './test.js': ['extendedTest', 'expect', 'expectTypeOf', 'describe', 'beforeAll', 'afterAll'] } }]
3024+
acc[key] = [value, { imports: { './test.js': ['extendedTest', 'expect', 'expectTypeOf', 'describe', 'beforeAll', 'afterAll', 'log'] } }]
29073025
}
29083026
return acc
29093027
}, {} as TestFsStructure),

0 commit comments

Comments
 (0)