Skip to content

Commit 2eedbce

Browse files
authored
fix(spy): reset spies if both restoreMocks and mockReset is set in the config (#8781)
1 parent d4c2b28 commit 2eedbce

File tree

3 files changed

+79
-4
lines changed

3 files changed

+79
-4
lines changed

packages/vitest/src/runtime/runners/test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,13 @@ function clearModuleMocks(config: SerializedConfig) {
227227
const { clearMocks, mockReset, restoreMocks, unstubEnvs, unstubGlobals }
228228
= config
229229

230-
// since each function calls another, we can just call one
231230
if (restoreMocks) {
232231
vi.restoreAllMocks()
233232
}
234-
else if (mockReset) {
233+
if (mockReset) {
235234
vi.resetAllMocks()
236235
}
237-
else if (clearMocks) {
236+
if (clearMocks) {
238237
vi.clearAllMocks()
239238
}
240239

test/cli/test/mocking.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { expect, test } from 'vitest'
2+
import { runInlineTests } from '../../test-utils'
3+
4+
test('setting resetMocks works if restoreMocks is also set', async () => {
5+
const { stderr, testTree } = await runInlineTests({
6+
'vitest.config.js': {
7+
test: {
8+
restoreMocks: true,
9+
mockReset: true,
10+
},
11+
},
12+
'./mocked.js': `
13+
export function spy() {}
14+
`,
15+
'./basic.test.js': `
16+
import { vi, test, expect } from 'vitest'
17+
import { spy } from './mocked.js'
18+
19+
vi.mock('./mocked.js', { spy: true })
20+
21+
test('spy is called here', () => {
22+
spy()
23+
expect(spy).toHaveBeenCalled()
24+
})
25+
26+
test('spy is not called here', () => {
27+
expect(spy).not.toHaveBeenCalled()
28+
})
29+
`,
30+
})
31+
32+
expect(stderr).toBe('')
33+
expect(testTree()).toMatchInlineSnapshot(`
34+
{
35+
"basic.test.js": {
36+
"spy is called here": "passed",
37+
"spy is not called here": "passed",
38+
},
39+
}
40+
`)
41+
})

test/test-utils/index.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Options } from 'tinyexec'
22
import type { UserConfig as ViteUserConfig } from 'vite'
33
import type { WorkerGlobalState } from 'vitest'
44
import type { TestProjectConfiguration } from 'vitest/config'
5-
import type { TestSpecification, TestUserConfig, Vitest, VitestRunMode } from 'vitest/node'
5+
import type { TestCollection, TestModule, TestSpecification, TestUserConfig, Vitest, VitestRunMode } from 'vitest/node'
66
import { webcrypto as crypto } from 'node:crypto'
77
import fs from 'node:fs'
88
import { Readable, Writable } from 'node:stream'
@@ -371,6 +371,9 @@ export async function runInlineTests(
371371
get results() {
372372
return vitest.ctx?.state.getTestModules() || []
373373
},
374+
testTree() {
375+
return buildTestTree(vitest.ctx?.state.getTestModules() || [])
376+
},
374377
}
375378
}
376379

@@ -385,3 +388,35 @@ export class StableTestFileOrderSorter {
385388
return files
386389
}
387390
}
391+
392+
function buildTestTree(testModules: TestModule[]) {
393+
type TestTree = Record<string, any>
394+
395+
function walkCollection(collection: TestCollection): TestTree {
396+
const node: TestTree = {}
397+
398+
for (const child of collection) {
399+
if (child.type === 'suite') {
400+
// Recursively walk suite children
401+
const suiteChildren = walkCollection(child.children)
402+
node[child.name] = suiteChildren
403+
}
404+
else if (child.type === 'test') {
405+
const result = child.result()
406+
node[child.name] = result.state
407+
}
408+
}
409+
410+
return node
411+
}
412+
413+
const tree: TestTree = {}
414+
415+
for (const module of testModules) {
416+
// Use relative module ID for cleaner output
417+
const key = module.relativeModuleId
418+
tree[key] = walkCollection(module.children)
419+
}
420+
421+
return tree
422+
}

0 commit comments

Comments
 (0)