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
11 changes: 7 additions & 4 deletions packages/runner/src/suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ function createSuiteCollector(
mode: RunMode,
each?: boolean,
suiteOptions?: TestOptions,
parentCollectorFixtures?: FixtureItem[],
) {
const tasks: (Test | Suite | SuiteCollector)[] = []

Expand Down Expand Up @@ -395,7 +396,7 @@ function createSuiteCollector(
test.type = 'test'
})

let collectorFixtures: FixtureItem[] | undefined
let collectorFixtures = parentCollectorFixtures

const collector: SuiteCollector = {
type: 'collector',
Expand Down Expand Up @@ -555,6 +556,7 @@ function createSuite() {
mode,
this.each,
options,
currentSuite?.fixtures(),
)
}

Expand Down Expand Up @@ -768,14 +770,15 @@ export function createTaskCollector(
) {
const collector = getCurrentSuite()
const scopedFixtures = collector.fixtures()
const context = { ...this }
if (scopedFixtures) {
this.fixtures = mergeScopedFixtures(
this.fixtures || [],
context.fixtures = mergeScopedFixtures(
context.fixtures || [],
Comment on lines +773 to +776
Copy link
Contributor Author

@hi-ogawa hi-ogawa Apr 9, 2025

Choose a reason for hiding this comment

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

this is shared between all extendedTest instance as per fn.apply(context, args) in

export function createChainable<T extends string, Args extends any[], R = any>(
keys: T[],
fn: (this: Record<T, any>, ...args: Args) => R,
): ChainableFunction<T, (...args: Args) => R> {
function create(context: Record<T, any>) {
const chain = function (this: any, ...args: Args) {
return fn.apply(context, args)
}

This was causing extendedTest to track fixtures in a stateful way instead of suite level inheritance. Existing tests were probably relying on that behavior and making inheritance look working.

Now I removed this stateful mutation logic, so proper suite level fixture inheritance is required and that's done via createSuiteCollector's parentCollectorFixtures.

scopedFixtures,
)
}
collector.test.fn.call(
this,
context,
formatName(name),
optionsOrFn as TestOptions,
optionsOrTest as TestFunction,
Expand Down
36 changes: 36 additions & 0 deletions test/core/test/test-extend.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -451,3 +451,39 @@ describe('scoping variables to suite', () => {
})
})
})

describe('test.scoped repro #7793', () => {
const extendedTest = test.extend<{ foo: boolean }>({
foo: false,
})

describe('top level', () => {
extendedTest.scoped({ foo: true })

describe('second level', () => {
extendedTest('foo is true', ({ foo }) => {
expect(foo).toBe(true)
})
})
})
})

describe('test.scoped repro #7813', () => {
const extendedTest = test.extend<{ foo?: boolean }>({
foo: false,
})

describe('foo is scoped to true', () => {
extendedTest.scoped({ foo: true })

extendedTest('foo is true', ({ foo }) => {
expect(foo).toBe(true)
})
})

describe('foo is left as default of false', () => {
extendedTest('foo is false', ({ foo }) => {
expect(foo).toBe(false)
})
})
})
Loading