Skip to content

Commit 4ee66c0

Browse files
authored
fix(runner): don't cache failed fixture setups (fix #11237) (#11238)
1 parent af903c6 commit 4ee66c0

3 files changed

Lines changed: 148 additions & 7 deletions

File tree

‎packages/vitest/src/runtime/runner/fixture.ts‎

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -426,8 +426,12 @@ export function withFixtures(fn: Function, options?: WithFixturesOptions) {
426426
}
427427
cachedFixtures.add(fixture)
428428

429-
const resolvedValue = await resolveTestFixtureValue(fixture, context, cleanupFnArray)
430-
context[fixture.name] = resolvedValue
429+
try {
430+
context[fixture.name] = await resolveTestFixtureValue(fixture, context, cleanupFnArray)
431+
} catch (error) {
432+
cachedFixtures.delete(fixture)
433+
throw error
434+
}
431435

432436
cleanupFnArray.push(() => {
433437
cachedFixtures.delete(fixture)
@@ -503,11 +507,14 @@ async function resolveScopeFixtureValue(
503507
fixture.name,
504508
fixture.scope === 'file' ? { ...workerContext, ...fileContext } : fixtureContext,
505509
cleanupFnFileArray,
506-
).then((value) => {
507-
fixtureContext[fixture.name] = value
508-
scopedFixturePromiseCache.delete(fixture)
509-
return value
510-
})
510+
)
511+
.then((value) => {
512+
fixtureContext[fixture.name] = value
513+
return value
514+
})
515+
.finally(() => {
516+
scopedFixturePromiseCache.delete(fixture)
517+
})
511518
scopedFixturePromiseCache.set(fixture, promise)
512519
return promise
513520
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import { expect, test } from 'vitest'
2+
import { runInlineTests } from '../../test-utils'
3+
4+
test('file fixture whose setup failed is set up again on retry', async () => {
5+
const { stdout, errorTree } = await runInlineTests({
6+
'basic.test.ts': `
7+
import { expect, test } from 'vitest'
8+
9+
let attempts = 0
10+
11+
const extended = test.extend<{ file: string }>({
12+
file: [
13+
async ({}, use) => {
14+
attempts++
15+
console.log('>> init file', attempts)
16+
if (attempts === 1) {
17+
throw new Error('setup failed once')
18+
}
19+
await use('file')
20+
},
21+
{ scope: 'file' },
22+
],
23+
})
24+
25+
extended('test1', { retry: 1 }, ({ file }) => {
26+
expect(file).toBe('file')
27+
})
28+
`,
29+
})
30+
31+
expect(getFixtureLogs(stdout)).toMatchInlineSnapshot(`
32+
">> init file 1
33+
>> init file 2"
34+
`)
35+
expect(errorTree()).toMatchInlineSnapshot(`
36+
{
37+
"basic.test.ts": {
38+
"test1": "passed",
39+
},
40+
}
41+
`)
42+
})
43+
44+
test('worker fixture whose setup failed does not poison the next file', async () => {
45+
const { stdout, errorTree } = await runInlineTests(
46+
{
47+
'fixture.ts': `
48+
import { test } from 'vitest'
49+
50+
let attempts = 0
51+
52+
export const extended = test.extend<{ worker: string }>({
53+
worker: [
54+
async ({}, use) => {
55+
attempts++
56+
console.log('>> init worker', attempts)
57+
if (attempts === 1) {
58+
throw new Error('setup failed once')
59+
}
60+
await use('worker')
61+
},
62+
{ scope: 'worker' },
63+
],
64+
})
65+
`,
66+
'1-basic.test.ts': `
67+
import { extended } from './fixture'
68+
extended('test1', ({ worker: _worker }) => {})
69+
`,
70+
'2-basic.test.ts': `
71+
import { extended } from './fixture'
72+
extended('test1', ({ worker: _worker }) => {})
73+
`,
74+
},
75+
{
76+
isolate: false,
77+
maxWorkers: 1,
78+
pool: 'threads',
79+
sequence: { sequencer: StableTestFileOrderSorter },
80+
},
81+
)
82+
83+
expect(getFixtureLogs(stdout)).toMatchInlineSnapshot(`
84+
">> init worker 1
85+
>> init worker 2"
86+
`)
87+
expect(errorTree()).toMatchInlineSnapshot(`
88+
{
89+
"1-basic.test.ts": {
90+
"test1": [
91+
"setup failed once",
92+
],
93+
},
94+
"2-basic.test.ts": {
95+
"test1": "passed",
96+
},
97+
}
98+
`)
99+
})
100+
101+
function getFixtureLogs(stdout: string) {
102+
return stdout
103+
.split('\n')
104+
.filter((line) => line.startsWith('>> init'))
105+
.join('\n')
106+
}
107+
108+
class StableTestFileOrderSorter {
109+
sort(files: { moduleId: string }[]) {
110+
return files.sort((a, b) => a.moduleId.localeCompare(b.moduleId))
111+
}
112+
113+
shard(files: { moduleId: string }[]) {
114+
return files
115+
}
116+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { expect, test } from 'vitest'
2+
3+
let attempts = 0
4+
5+
const flaky = test.extend<{ value: string }>({
6+
value: async ({}, use) => {
7+
attempts++
8+
if (attempts === 1) {
9+
throw new Error('setup failed once')
10+
}
11+
await use('ready')
12+
},
13+
})
14+
15+
flaky('fixture setup runs again on retry', { retry: 1 }, ({ value }) => {
16+
expect(attempts).toBe(2)
17+
expect(value).toBe('ready')
18+
})

0 commit comments

Comments
 (0)