Skip to content

Commit 175c752

Browse files
authored
fix(runner): test.extend doesn't work in hooks without test (#4065)
1 parent 1aee13a commit 175c752

File tree

5 files changed

+77
-28
lines changed

5 files changed

+77
-28
lines changed

packages/runner/src/fixture.ts

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getFixture } from './map'
12
import type { TestContext } from './types'
23

34
export interface FixtureItem {
@@ -43,32 +44,41 @@ export function mergeContextFixtures(fixtures: Record<string, any>, context: { f
4344
return context
4445
}
4546

46-
export function withFixtures(fn: Function, fixtures: FixtureItem[], context: TestContext & Record<string, any>) {
47-
if (!fixtures.length)
48-
return () => fn(context)
47+
export function withFixtures(fn: Function, testContext?: TestContext) {
48+
return (hookContext?: TestContext) => {
49+
const context: TestContext & { [key: string]: any } | undefined = hookContext || testContext
4950

50-
const usedProps = getUsedProps(fn)
51-
if (!usedProps.length)
52-
return () => fn(context)
51+
if (!context)
52+
return fn({})
5353

54-
const usedFixtures = fixtures.filter(({ prop }) => usedProps.includes(prop))
55-
const pendingFixtures = resolveDeps(usedFixtures)
56-
let cursor = 0
54+
const fixtures = getFixture(context)
55+
if (!fixtures?.length)
56+
return fn(context)
5757

58-
async function use(fixtureValue: any) {
59-
const { prop } = pendingFixtures[cursor++]
60-
context[prop] = fixtureValue
61-
if (cursor < pendingFixtures.length)
62-
await next()
63-
else await fn(context)
64-
}
58+
const usedProps = getUsedProps(fn)
59+
if (!usedProps.length)
60+
return fn(context)
6561

66-
async function next() {
67-
const { value } = pendingFixtures[cursor]
68-
typeof value === 'function' ? await value(context, use) : await use(value)
69-
}
62+
const usedFixtures = fixtures.filter(({ prop }) => usedProps.includes(prop))
63+
const pendingFixtures = resolveDeps(usedFixtures)
64+
let cursor = 0
65+
66+
async function use(fixtureValue: any) {
67+
const { prop } = pendingFixtures[cursor++]
68+
context![prop] = fixtureValue
69+
70+
if (cursor < pendingFixtures.length)
71+
await next()
72+
else await fn(context)
73+
}
74+
75+
async function next() {
76+
const { value } = pendingFixtures[cursor]
77+
typeof value === 'function' ? await value(context, use) : await use(value)
78+
}
7079

71-
return () => next()
80+
return next()
81+
}
7282
}
7383

7484
function resolveDeps(fixtures: FixtureItem[], depSet = new Set<FixtureItem>(), pendingFixtures: FixtureItem[] = []) {

packages/runner/src/hooks.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { OnTestFailedHandler, SuiteHooks, Test } from './types'
22
import { getCurrentSuite, getRunner } from './suite'
33
import { getCurrentTest } from './test-state'
44
import { withTimeout } from './context'
5+
import { withFixtures } from './fixture'
56

67
function getDefaultHookTimeout() {
78
return getRunner().config.hookTimeout
@@ -15,10 +16,10 @@ export function afterAll(fn: SuiteHooks['afterAll'][0], timeout?: number) {
1516
return getCurrentSuite().on('afterAll', withTimeout(fn, timeout ?? getDefaultHookTimeout(), true))
1617
}
1718
export function beforeEach<ExtraContext = {}>(fn: SuiteHooks<ExtraContext>['beforeEach'][0], timeout?: number) {
18-
return getCurrentSuite<ExtraContext>().on('beforeEach', withTimeout(fn, timeout ?? getDefaultHookTimeout(), true))
19+
return getCurrentSuite<ExtraContext>().on('beforeEach', withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true))
1920
}
2021
export function afterEach<ExtraContext = {}>(fn: SuiteHooks<ExtraContext>['afterEach'][0], timeout?: number) {
21-
return getCurrentSuite<ExtraContext>().on('afterEach', withTimeout(fn, timeout ?? getDefaultHookTimeout(), true))
22+
return getCurrentSuite<ExtraContext>().on('afterEach', withTimeout(withFixtures(fn), timeout ?? getDefaultHookTimeout(), true))
2223
}
2324

2425
export const onTestFailed = createTestHook<OnTestFailedHandler>('onTestFailed', (test, handler) => {

packages/runner/src/map.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import type { Awaitable } from '@vitest/utils'
2-
import type { Suite, SuiteHooks, Test } from './types'
2+
import type { Suite, SuiteHooks, Test, TestContext } from './types'
3+
import type { FixtureItem } from './fixture'
34

45
// use WeakMap here to make the Test and Suite object serializable
56
const fnMap = new WeakMap()
7+
const fixtureMap = new WeakMap()
68
const hooksMap = new WeakMap()
79

810
export function setFn(key: Test, fn: (() => Awaitable<void>)) {
@@ -13,6 +15,14 @@ export function getFn<Task = Test>(key: Task): (() => Awaitable<void>) {
1315
return fnMap.get(key as any)
1416
}
1517

18+
export function setFixture(key: TestContext, fixture: FixtureItem[] | undefined) {
19+
fixtureMap.set(key, fixture)
20+
}
21+
22+
export function getFixture<Context = TestContext>(key: Context): FixtureItem[] {
23+
return fixtureMap.get(key as any)
24+
}
25+
1626
export function setHooks(key: Suite, hooks: SuiteHooks) {
1727
hooksMap.set(key, hooks)
1828
}

packages/runner/src/suite.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { File, Fixtures, RunMode, Suite, SuiteAPI, SuiteCollector, SuiteFac
33
import type { VitestRunner } from './types/runner'
44
import { createChainable } from './utils/chain'
55
import { collectTask, collectorContext, createTestContext, runWithSuite, withTimeout } from './context'
6-
import { getHooks, setFn, setHooks } from './map'
6+
import { getHooks, setFixture, setFn, setHooks } from './map'
77
import type { FixtureItem } from './fixture'
88
import { mergeContextFixtures, withFixtures } from './fixture'
99

@@ -96,10 +96,9 @@ function createSuiteCollector(name: string, factory: SuiteFactory = () => { }, m
9696
enumerable: false,
9797
})
9898

99+
setFixture(context, this.fixtures)
99100
setFn(test, withTimeout(
100-
this.fixtures
101-
? withFixtures(fn, this.fixtures, context)
102-
: () => fn(context),
101+
withFixtures(fn, context),
103102
options?.timeout ?? runner.config.testTimeout,
104103
))
105104

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { afterEach, beforeEach, expect, test } from 'vitest'
2+
3+
interface Fixture { foo: number }
4+
5+
const test1 = test.extend<Fixture>({
6+
foo: 1,
7+
})
8+
9+
const test2 = test.extend<Fixture>({
10+
foo: 2,
11+
})
12+
13+
test1('the foo should be 1', ({ foo }) => {
14+
expect(foo).toBe(1)
15+
})
16+
17+
test2('the foo should be 2', ({ foo }) => {
18+
expect(foo).toBe(2)
19+
})
20+
21+
let nextFoo = 1
22+
beforeEach<Fixture>(({ foo }) => {
23+
expect(foo).toBe(nextFoo)
24+
})
25+
26+
afterEach<Fixture>(({ foo }) => {
27+
expect(foo).toBe(nextFoo)
28+
nextFoo++
29+
})

0 commit comments

Comments
 (0)