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 packages/runner/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export function withCancel<T extends (...args: any[]) => any>(

const abortControllers = new WeakMap<TestContext, AbortController>()

export function abortIfTimeout([context]: [TestContext?], error: Error): void {
export function abortIfTimeout([context]: [TestContext?, unknown?], error: Error): void {
if (context) {
abortContextSignal(context, error)
}
Expand Down
7 changes: 6 additions & 1 deletion packages/runner/src/fixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,13 +291,18 @@ export interface WithFixturesOptions {
* Current fixtures from the context.
*/
fixtures?: TestFixtures
/**
* The suite to use for fixture lookups.
* Used by beforeEach/afterEach/aroundEach hooks to pick up fixture overrides from the test's describe block.
*/
suite?: Suite
}

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

export function withFixtures(fn: Function, options?: WithFixturesOptions) {
const collector = getCurrentSuite()
const suite = collector.suite || collector.file
const suite = options?.suite || collector.suite || collector.file
return async (hookContext?: TestContext): Promise<any> => {
const context: (TestContext & { [key: string]: any }) | undefined = hookContext || options?.context as TestContext

Expand Down
17 changes: 14 additions & 3 deletions packages/runner/src/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,11 +157,17 @@ export function beforeEach<ExtraContext = object>(
): void {
assertTypes(fn, '"beforeEach" callback', ['function'])
const stackTraceError = new Error('STACK_TRACE_ERROR')

const wrapper: BeforeEachListener<ExtraContext> = (context, suite) => {
const fixtureResolver = withFixtures(fn, { suite })
return fixtureResolver(context)
}

return getCurrentSuite<ExtraContext>().on(
'beforeEach',
Object.assign(
withTimeout(
withFixtures(fn),
wrapper,
timeout ?? getDefaultHookTimeout(),
true,
stackTraceError,
Expand Down Expand Up @@ -197,10 +203,15 @@ export function afterEach<ExtraContext = object>(
timeout?: number,
): void {
assertTypes(fn, '"afterEach" callback', ['function'])
const wrapper: AfterEachListener<ExtraContext> = (context, suite) => {
const fixtureResolver = withFixtures(fn, { suite })
return fixtureResolver(context)
}

return getCurrentSuite<ExtraContext>().on(
'afterEach',
withTimeout(
withFixtures(fn),
wrapper,
timeout ?? getDefaultHookTimeout(),
true,
new Error('STACK_TRACE_ERROR'),
Expand Down Expand Up @@ -375,7 +386,7 @@ export function aroundEach<ExtraContext = object>(
const innerFn = (ctx: any) => fn(runTest, ctx, suite)
configureProps(innerFn, { index: 1, original: fn })

const fixtureResolver = withFixtures(innerFn)
const fixtureResolver = withFixtures(innerFn, { suite })
return fixtureResolver(context)
}

Expand Down
133 changes: 133 additions & 0 deletions test/core/test/test-extend.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -640,3 +640,136 @@ describe('builder pattern with non-function values', () => {
expect(chainedSync).toBe('HELLO WORLD')
})
})

// https://github.com/vitest-dev/vitest/issues/9810
describe('override auto fixture with outer beforeEach', () => {
const myTest = test
.extend('base', { auto: true }, () => 'base:default')
.extend('derived', { auto: true }, ({ base }) => {
return `derived:${base}`
})

beforeEach(({ task }) => {
expect(task).toBeTruthy()
})

describe('with override', () => {
myTest.override('base', 'base:override')

myTest('auto fixture sees overridden dependency', ({ base, derived }) => {
expect(base).toBe('base:override')
expect(derived).toBe('derived:base:override')
})
})
})

describe('override auto fixture with co-located beforeEach', () => {
const myTest = test
.extend('base', { auto: true }, () => 'base:default')
.extend('derived', { auto: true }, ({ base }) => {
return `derived:${base}`
})

myTest.override('base', 'base:override')

beforeEach(({ task }) => {
expect(task).toBeTruthy()
})

myTest('override applies when beforeEach is co-located', ({ base, derived }) => {
expect(base).toBe('base:override')
expect(derived).toBe('derived:base:override')
})
})

describe('override non-auto fixture with outer beforeEach', () => {
const myTest = test
.extend('base', () => 'base:default')
.extend('derived', ({ base }) => `derived:${base}`)

beforeEach(({ task }) => {
expect(task).toBeTruthy()
})

describe('with override', () => {
myTest.override('base', 'base:override')

myTest('override applies to non-auto dependency', ({ base, derived }) => {
expect(base).toBe('base:override')
expect(derived).toBe('derived:base:override')
})
})
})

describe('override fixture accessed in outer beforeEach', () => {
const myTest = test
.extend('base', () => 'base:default')
.extend('derived', ({ base }) => `derived:${base}`)

const hookValues: string[] = []

myTest.beforeEach(({ base }) => {
hookValues.push(base)
})

describe('with override', () => {
myTest.override('base', 'base:override')

myTest('beforeEach sees overridden fixture', ({ derived }) => {
expect(hookValues).toEqual(['base:override'])
expect(derived).toBe('derived:base:override')
})
})
})
Comment on lines +704 to +723
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue wasn't only auto: true case. This was also failing with the same reason that beforeEach outside of describe aren't picking up inner override.


describe('nested overrides with outer beforeEach', () => {
const myTest = test
.extend('base', { auto: true }, () => 'base:default')
.extend('derived', { auto: true }, ({ base }) => {
return `derived:${base}`
})

beforeEach(({ task }) => {
expect(task).toBeTruthy()
})

describe('outer', () => {
myTest.override('base', 'base:outer')

myTest('outer override', ({ base, derived }) => {
expect(base).toBe('base:outer')
expect(derived).toBe('derived:base:outer')
})

describe('inner', () => {
myTest.override('base', 'base:inner')

myTest('inner override wins', ({ base, derived }) => {
expect(base).toBe('base:inner')
expect(derived).toBe('derived:base:inner')
})
})
})
})

describe('override fixture accessed in aroundEach', () => {
const myTest = test
.extend('base', () => 'base:default')
.extend('derived', ({ base }) => `derived:${base}`)

const hookValues: string[] = []

myTest.aroundEach(async (runTest, { base }) => {
hookValues.push(base)
await runTest()
})

describe('with override', () => {
myTest.override('base', 'base:override')

myTest('aroundEach sees overridden fixture', ({ derived }) => {
expect(hookValues).toEqual(['base:override'])
expect(derived).toBe('derived:base:override')
})
})
})
Loading