Skip to content

Commit 5f3c003

Browse files
committed
refactor: cleanup, review
1 parent a85eea4 commit 5f3c003

5 files changed

Lines changed: 41 additions & 43 deletions

File tree

docs/guide/test-context.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,10 @@ export default defineConfig({
702702

703703
You can override fixture values for a specific suite and its children using `test.override`. This is useful when you need different fixture values for different test scenarios.
704704

705+
::: tip
706+
Vitest will automatically inherit the options, if they are not provided when overriding. Note that you cannot override fixture's `scope` or `auto` options.
707+
:::
708+
705709
#### Builder Pattern (Recommended)
706710

707711
```ts

packages/runner/src/fixture.ts

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,24 +2,25 @@ import type { FixtureFn, Suite, VitestRunner } from './types'
22
import type { File, FixtureOptions, TestContext } from './types/tasks'
33
import { createDefer, filterOutComments, isObject } from '@vitest/utils/helpers'
44
import { FixtureDependencyError } from './errors'
5-
import { getTestFixturesManager } from './map'
5+
import { getTestFixtures } from './map'
66
import { getCurrentSuite } from './suite'
77

88
export interface TestFixtureItem extends FixtureOptions {
99
name: string
1010
value: unknown
1111
scope: 'test' | 'file' | 'worker'
1212
deps: Set<string>
13-
// so it's possible to call { parent } inside the same fixture
13+
// so it's possible to call base fixture inside ({ a: ({ a }, use) => {} })
1414
parent?: TestFixtureItem
1515
}
1616

17-
export type TestFixtureRecord = Record<string, TestFixtureItem>
17+
export type UserFixtures = Record<string, unknown>
18+
export type FixtureRegistrations = Map<string, TestFixtureItem>
1819

1920
export class TestFixtures {
20-
private _suiteContexts: WeakMap<Suite | symbol, Record<string, any>>
21-
private _overrides = new WeakMap<Suite, Map<string, TestFixtureItem>>()
22-
private _registrations: Map<string, TestFixtureItem>
21+
private _suiteContexts: WeakMap<Suite | symbol, /* context object */ Record<string, unknown>>
22+
private _overrides = new WeakMap<Suite, FixtureRegistrations>()
23+
private _registrations: FixtureRegistrations
2324

2425
private static _definitions: TestFixtures[] = []
2526
private static _builtinFixtures: string[] = [
@@ -47,22 +48,20 @@ export class TestFixtures {
4748
return TestFixtures._definitions.map(f => f.getFileContext(file))
4849
}
4950

50-
constructor(
51-
registrations?: Map<string, TestFixtureItem>,
52-
) {
51+
constructor(registrations?: FixtureRegistrations) {
5352
this._registrations = registrations ?? new Map()
5453
this._suiteContexts = new WeakMap()
5554
TestFixtures._definitions.push(this)
5655
}
5756

58-
extend(runner: VitestRunner, userFixtures: Record<string, any>): TestFixtures {
57+
extend(runner: VitestRunner, userFixtures: UserFixtures): TestFixtures {
5958
const { suite } = getCurrentSuite()
6059
const isTopLevel = !suite || suite.file === suite
6160
const registrations = this.parseUserFixtures(runner, userFixtures, isTopLevel)
6261
return new TestFixtures(registrations)
6362
}
6463

65-
get(suite: Suite): Map<string, TestFixtureItem> {
64+
get(suite: Suite): FixtureRegistrations {
6665
let currentSuite: Suite | undefined = suite
6766
while (currentSuite) {
6867
const overrides = this._overrides.get(currentSuite)
@@ -78,16 +77,16 @@ export class TestFixtures {
7877
return this._registrations
7978
}
8079

81-
override(runner: VitestRunner, userFixtures: Record<string, any>): void {
80+
override(runner: VitestRunner, userFixtures: UserFixtures): void {
8281
const { suite: currentSuite, file } = getCurrentSuite()
8382
const suite = currentSuite || file
8483
const isTopLevel = !currentSuite || currentSuite.file === currentSuite
8584
// Create a copy of the closest parent's registrations to avoid modifying them
8685
// For chained calls, this.get(suite) returns this suite's overrides; for first call, returns parent's
8786
const suiteRegistrations = new Map(this.get(suite))
8887
const registrations = this.parseUserFixtures(runner, userFixtures, isTopLevel, suiteRegistrations)
89-
// if defined in top-level, just override all registrations,
90-
// we don't support overriding suite-level fixtures anyway (it will throw an error)
88+
// If defined in top-level, just override all registrations
89+
// We don't support overriding suite-level fixtures anyway (it will throw an error)
9190
if (isTopLevel) {
9291
this._registrations = registrations
9392
}
@@ -112,7 +111,7 @@ export class TestFixtures {
112111

113112
private parseUserFixtures(
114113
runner: VitestRunner,
115-
userFixtures: Record<string, any>,
114+
userFixtures: UserFixtures,
116115
supportNonTest: boolean,
117116
registrations = new Map<string, TestFixtureItem>(this._registrations),
118117
) {
@@ -272,7 +271,7 @@ export async function callFixtureCleanupFrom(context: object, fromIndex: number)
272271

273272
const contextHasFixturesCache = new WeakMap<TestContext, WeakSet<TestFixtureItem>>()
274273

275-
export function withFixtures(runner: VitestRunner, fn: Function, testContext?: TestContext) {
274+
export function withFixtures(fn: Function, testContext?: TestContext) {
276275
const collector = getCurrentSuite()
277276
const suite = collector.suite || collector.file
278277
return async (hookContext?: TestContext): Promise<any> => {
@@ -282,20 +281,20 @@ export function withFixtures(runner: VitestRunner, fn: Function, testContext?: T
282281
return fn({})
283282
}
284283

285-
const fixturesManager = getTestFixturesManager(context)
286-
if (!fixturesManager) {
284+
const fixtures = getTestFixtures(context)
285+
if (!fixtures) {
287286
return fn(context)
288287
}
289288

290-
const fixtures = fixturesManager.get(suite)
291-
if (!fixtures.size) {
289+
const registrations = fixtures.get(suite)
290+
if (!registrations.size) {
292291
return fn(context)
293292
}
294293

295294
const usedFixtures: TestFixtureItem[] = []
296295
const usedProps = getUsedProps(fn)
297296

298-
for (const fixture of fixtures.values()) {
297+
for (const fixture of registrations.values()) {
299298
if (fixture.auto || usedProps.has(fixture.name)) {
300299
usedFixtures.push(fixture)
301300
}
@@ -310,7 +309,7 @@ export function withFixtures(runner: VitestRunner, fn: Function, testContext?: T
310309
}
311310
const cleanupFnArray = cleanupFnArrayMap.get(context)!
312311

313-
const pendingFixtures = resolveDeps(usedFixtures, fixtures)
312+
const pendingFixtures = resolveDeps(usedFixtures, registrations)
314313

315314
if (!pendingFixtures.length) {
316315
return fn(context)
@@ -344,7 +343,7 @@ export function withFixtures(runner: VitestRunner, fn: Function, testContext?: T
344343
}
345344
else {
346345
const resolvedValue = await resolveScopeFixtureValue(
347-
fixturesManager,
346+
fixtures,
348347
suite,
349348
fixture,
350349
)
@@ -459,7 +458,7 @@ async function resolveFixtureFunction(
459458

460459
function resolveDeps(
461460
usedFixtures: TestFixtureItem[],
462-
fixtures: Map<string, TestFixtureItem>,
461+
registrations: FixtureRegistrations,
463462
depSet = new Set<TestFixtureItem>(),
464463
pendingFixtures: TestFixtureItem[] = [],
465464
) {
@@ -487,8 +486,8 @@ function resolveDeps(
487486

488487
depSet.add(fixture)
489488
resolveDeps(
490-
[...fixture.deps].map(n => n === fixture.name ? fixture.parent : fixtures.get(n)).filter(n => !!n),
491-
fixtures,
489+
[...fixture.deps].map(n => n === fixture.name ? fixture.parent : registrations.get(n)).filter(n => !!n),
490+
registrations,
492491
depSet,
493492
pendingFixtures,
494493
)

packages/runner/src/hooks.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { VitestRunner } from './types'
21
import type {
32
AfterAllListener,
43
AfterEachListener,
@@ -144,12 +143,11 @@ export function beforeEach<ExtraContext = object>(
144143
): void {
145144
assertTypes(fn, '"beforeEach" callback', ['function'])
146145
const stackTraceError = new Error('STACK_TRACE_ERROR')
147-
const runner = getRunner()
148146
return getCurrentSuite<ExtraContext>().on(
149147
'beforeEach',
150148
Object.assign(
151149
withTimeout(
152-
withFixtures(runner, fn),
150+
withFixtures(fn),
153151
timeout ?? getDefaultHookTimeout(),
154152
true,
155153
stackTraceError,
@@ -185,11 +183,10 @@ export function afterEach<ExtraContext = object>(
185183
timeout?: number,
186184
): void {
187185
assertTypes(fn, '"afterEach" callback', ['function'])
188-
const runner = getRunner()
189186
return getCurrentSuite<ExtraContext>().on(
190187
'afterEach',
191188
withTimeout(
192-
withFixtures(runner, fn),
189+
withFixtures(fn),
193190
timeout ?? getDefaultHookTimeout(),
194191
true,
195192
new Error('STACK_TRACE_ERROR'),
@@ -351,11 +348,10 @@ export function aroundEach<ExtraContext = object>(
351348
assertTypes(fn, '"aroundEach" callback', ['function'])
352349
const stackTraceError = new Error('STACK_TRACE_ERROR')
353350
const resolvedTimeout = timeout ?? getDefaultHookTimeout()
354-
const runner = getRunner()
355351

356352
// Create a wrapper function that supports fixtures in the second argument (context)
357353
// withFixtures resolves fixtures into context, then we call fn with all 3 args
358-
const wrappedFn: AroundEachListener<ExtraContext> = withAroundEachFixtures(runner, fn)
354+
const wrappedFn: AroundEachListener<ExtraContext> = withAroundEachFixtures(fn)
359355

360356
// Store timeout and stack trace on the function for use in callAroundEachHooks
361357
// Setup and teardown phases will each have their own timeout
@@ -376,7 +372,6 @@ export function aroundEach<ExtraContext = object>(
376372
* - Third arg is suite
377373
*/
378374
function withAroundEachFixtures<ExtraContext>(
379-
runner: VitestRunner,
380375
fn: AroundEachListener<ExtraContext>,
381376
): AroundEachListener<ExtraContext> {
382377
// Create the wrapper that will be returned
@@ -390,7 +385,7 @@ function withAroundEachFixtures<ExtraContext>(
390385
;(innerFn as any).toString = () => fn.toString()
391386

392387
// Use withFixtures to resolve fixtures, passing context as the hook context
393-
const fixtureResolver = withFixtures(runner, innerFn)
388+
const fixtureResolver = withFixtures(innerFn)
394389
return fixtureResolver(context)
395390
}
396391

packages/runner/src/map.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function setTestFixture(
2222
testFixtureMap.set(key, fixture)
2323
}
2424

25-
export function getTestFixturesManager<Context = TestContext>(key: Context): TestFixtures {
25+
export function getTestFixtures<Context = TestContext>(key: Context): TestFixtures {
2626
return testFixtureMap.get(key as any)
2727
}
2828

packages/runner/src/suite.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { TestFixtureRecord } from './fixture'
1+
import type { UserFixtures } from './fixture'
22
import type { VitestRunner } from './types/runner'
33
import type {
44
File,
@@ -413,7 +413,7 @@ function createSuiteCollector(
413413
setFn(
414414
task,
415415
withTimeout(
416-
withAwaitAsyncAssertions(withFixtures(runner, handler, context), task),
416+
withAwaitAsyncAssertions(withFixtures(handler, context), task),
417417
timeout,
418418
false,
419419
stackTraceError,
@@ -840,10 +840,10 @@ export function createTaskCollector(
840840
* Handles both builder pattern (name, options?, value) and object syntax.
841841
*/
842842
function parseBuilderFixtures(
843-
fixturesOrName: TestFixtureRecord | string,
843+
fixturesOrName: UserFixtures | string,
844844
optionsOrFn?: object | ((...args: any[]) => any),
845845
maybeFn?: (...args: any[]) => any,
846-
): TestFixtureRecord {
846+
): UserFixtures {
847847
// Object syntax: just return as-is
848848
if (typeof fixturesOrName !== 'string') {
849849
return fixturesOrName
@@ -917,7 +917,7 @@ export function createTaskCollector(
917917

918918
taskFn.override = function (
919919
this: TestAPI,
920-
fixturesOrName: TestFixtureRecord | string,
920+
fixturesOrName: UserFixtures | string,
921921
optionsOrFn?: object | ((...args: any[]) => any),
922922
maybeFn?: (...args: any[]) => any,
923923
) {
@@ -926,14 +926,14 @@ export function createTaskCollector(
926926
return this
927927
}
928928

929-
taskFn.scoped = function (fixtures: TestFixtureRecord) {
929+
taskFn.scoped = function (fixtures: UserFixtures) {
930930
console.warn(`test.scoped() is deprecated and will be removed in future versions. Please use test.override() instead.`)
931931
return this.override(fixtures)
932932
}
933933

934934
taskFn.extend = function (
935935
this: TestAPI,
936-
fixturesOrName: TestFixtureRecord | string,
936+
fixturesOrName: UserFixtures | string,
937937
optionsOrFn?: object | ((...args: any[]) => any),
938938
maybeFn?: (...args: any[]) => any,
939939
) {

0 commit comments

Comments
 (0)