Skip to content

Commit 8e2108d

Browse files
fix: stale mock metadata breaks automocking with isolate:false (fix #10145) (#10541)
1 parent 9f23f8e commit 8e2108d

2 files changed

Lines changed: 78 additions & 10 deletions

File tree

‎packages/vitest/src/runtime/moduleRunner/moduleRunner.ts‎

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -167,23 +167,29 @@ export class VitestModuleRunner
167167
if (mod.meta && 'mockedModule' in mod.meta) {
168168
const mockedModule = mod.meta.mockedModule as MockedModule
169169
const mockId = this.mocker.getMockPath(mod.id)
170+
const currentMock = this.mocker.getDependencyMock(mod.id)
170171
// bypass mock and force "importActual" behavior when:
171172
// - mock was removed by doUnmock (stale mockedModule in meta)
173+
if (!currentMock) {
174+
const node = await this.fetchModule(injectQuery(url, '_vitest_original'))
175+
return this._cachedRequest(node.url, node, callstack, metadata)
176+
}
172177
// - self-import: mock factory/file is importing the module it's mocking
173-
const isStale = !this.mocker.getDependencyMock(mod.id)
174178
const isSelfImport = callstack.includes(mockId)
175179
|| callstack.includes(url)
176-
|| ('redirect' in mockedModule && callstack.includes(mockedModule.redirect))
177-
if (isStale || isSelfImport) {
180+
|| ('redirect' in currentMock && callstack.includes(currentMock.redirect))
181+
if (isSelfImport) {
178182
const node = await this.fetchModule(injectQuery(url, '_vitest_original'))
179183
return this._cachedRequest(node.url, node, callstack, metadata)
180184
}
181-
mocked = await this.mocker.requestWithMockedModule(
182-
url,
183-
mod,
184-
callstack,
185-
mockedModule,
186-
)
185+
const isAutoMock = currentMock.type === 'automock' || currentMock.type === 'autospy'
186+
if (isAutoMock && currentMock !== mockedModule) {
187+
const freshNode = await this.fetchModule(injectQuery(url, '_vitest_original'))
188+
mocked = await this.mocker.requestWithMockedModule(url, freshNode, callstack, currentMock)
189+
}
190+
else {
191+
mocked = await this.mocker.requestWithMockedModule(url, mod, callstack, currentMock)
192+
}
187193
}
188194
else {
189195
mocked = await this.mocker.mockedRequest(url, mod, callstack)

‎test/e2e/test/mocking.test.ts‎

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import path from 'node:path'
33
import { playwright } from '@vitest/browser-playwright'
44
import { expect, test } from 'vitest'
55
import { rolldownVersion } from 'vitest/node'
6-
import { runInlineTests, runVitest } from '../../test-utils'
6+
import { runInlineTests, runVitest, StableTestFileOrderSorter } from '../../test-utils'
77

88
test('setting resetMocks works if restoreMocks is also set', async () => {
99
const { stderr, testTree } = await runInlineTests({
@@ -510,3 +510,65 @@ test("local", async () => {
510510
}
511511
`)
512512
})
513+
514+
test('automocking works with isolate:false when factory mock runs first (resolve alias)', async () => {
515+
const { stderr, testTree } = await runInlineTests({
516+
'vitest.config.js': `
517+
import path from 'node:path'
518+
import { defineConfig } from 'vitest/config'
519+
520+
export default defineConfig({
521+
resolve: {
522+
alias: {
523+
'~': path.resolve(import.meta.dirname, 'src'),
524+
},
525+
},
526+
test: {
527+
isolate: false,
528+
},
529+
})
530+
`,
531+
'./src/dep.ts': `
532+
export function useDep(): string { return 'real' }
533+
export function helperDep(): number { return 42 }
534+
`,
535+
'./a-factory.test.ts': `
536+
import { vi, test, expect } from 'vitest'
537+
import { useDep } from '~/dep'
538+
vi.mock(import('~/dep'), () => ({
539+
useDep: () => 'factory',
540+
helperDep: () => 0,
541+
}))
542+
test('factory mock', () => {
543+
expect(useDep()).toBe('factory')
544+
})
545+
`,
546+
'./b-automock.test.ts': `
547+
import { vi, test, expect } from 'vitest'
548+
import { useDep } from '~/dep'
549+
vi.mock(import('~/dep'))
550+
test('automock exports are mock functions', () => {
551+
expect(vi.isMockFunction(useDep)).toBe(true)
552+
})
553+
test('automock mockReturnValue works', () => {
554+
vi.mocked(useDep).mockReturnValue('mocked')
555+
expect(useDep()).toBe('mocked')
556+
})
557+
`,
558+
}, {
559+
sequence: { sequencer: StableTestFileOrderSorter },
560+
})
561+
562+
expect(stderr).toBe('')
563+
expect(testTree()).toMatchInlineSnapshot(`
564+
{
565+
"a-factory.test.ts": {
566+
"factory mock": "passed",
567+
},
568+
"b-automock.test.ts": {
569+
"automock exports are mock functions": "passed",
570+
"automock mockReturnValue works": "passed",
571+
},
572+
}
573+
`)
574+
})

0 commit comments

Comments
 (0)