Skip to content
Open
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
24 changes: 15 additions & 9 deletions packages/vitest/src/runtime/moduleRunner/moduleRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,23 +168,29 @@ export class VitestModuleRunner
if (mod.meta && 'mockedModule' in mod.meta) {
const mockedModule = mod.meta.mockedModule as MockedModule
const mockId = this.mocker.getMockPath(mod.id)
const currentMock = this.mocker.getDependencyMock(mod.id)
// bypass mock and force "importActual" behavior when:
// - mock was removed by doUnmock (stale mockedModule in meta)
if (!currentMock) {
const node = await this.fetchModule(injectQuery(url, '_vitest_original'))
return this._cachedRequest(node.url, node, callstack, metadata)
}
// - self-import: mock factory/file is importing the module it's mocking
const isStale = !this.mocker.getDependencyMock(mod.id)
const isSelfImport = callstack.includes(mockId)
|| callstack.includes(url)
|| ('redirect' in mockedModule && callstack.includes(mockedModule.redirect))
if (isStale || isSelfImport) {
|| ('redirect' in currentMock && callstack.includes(currentMock.redirect))
if (isSelfImport) {
const node = await this.fetchModule(injectQuery(url, '_vitest_original'))
return this._cachedRequest(node.url, node, callstack, metadata)
}
mocked = await this.mocker.requestWithMockedModule(
url,
mod,
callstack,
mockedModule,
)
const isAutoMock = currentMock.type === 'automock' || currentMock.type === 'autospy'
if (isAutoMock && currentMock !== mockedModule) {
const freshNode = await this.fetchModule(injectQuery(url, '_vitest_original'))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If it's a manual or redirect mock, then this is not needed because it won't be used to autospy/automock

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

If I understand correctly I believe f394cb4 addresses this

mocked = await this.mocker.requestWithMockedModule(url, freshNode, callstack, currentMock)
}
else {
mocked = await this.mocker.requestWithMockedModule(url, mod, callstack, currentMock)
}
}
else {
mocked = await this.mocker.mockedRequest(url, mod, callstack)
Expand Down
64 changes: 63 additions & 1 deletion test/e2e/test/mocking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import path from 'node:path'
import { playwright } from '@vitest/browser-playwright'
import { expect, test } from 'vitest'
import { rolldownVersion } from 'vitest/node'
import { runInlineTests, runVitest } from '../../test-utils'
import { runInlineTests, runVitest, StableTestFileOrderSorter } from '../../test-utils'

test('setting resetMocks works if restoreMocks is also set', async () => {
const { stderr, testTree } = await runInlineTests({
Expand Down Expand Up @@ -510,3 +510,65 @@ test("local", async () => {
}
`)
})

test('automocking works with isolate:false when factory mock runs first (resolve alias)', async () => {
const { stderr, testTree } = await runInlineTests({
'vitest.config.js': `
import path from 'node:path'
import { defineConfig } from 'vitest/config'

export default defineConfig({
resolve: {
alias: {
'~': path.resolve(import.meta.dirname, 'src'),
},
},
test: {
isolate: false,
},
})
`,
'./src/dep.ts': `
export function useDep(): string { return 'real' }
export function helperDep(): number { return 42 }
`,
'./a-factory.test.ts': `
import { vi, test, expect } from 'vitest'
import { useDep } from '~/dep'
vi.mock(import('~/dep'), () => ({
useDep: () => 'factory',
helperDep: () => 0,
}))
test('factory mock', () => {
expect(useDep()).toBe('factory')
})
`,
'./b-automock.test.ts': `
import { vi, test, expect } from 'vitest'
import { useDep } from '~/dep'
vi.mock(import('~/dep'))
test('automock exports are mock functions', () => {
expect(vi.isMockFunction(useDep)).toBe(true)
})
test('automock mockReturnValue works', () => {
vi.mocked(useDep).mockReturnValue('mocked')
expect(useDep()).toBe('mocked')
})
`,
}, {
sequence: { sequencer: StableTestFileOrderSorter },
})

expect(stderr).toBe('')
expect(testTree()).toMatchInlineSnapshot(`
{
"a-factory.test.ts": {
"factory mock": "passed",
},
"b-automock.test.ts": {
"automock exports are mock functions": "passed",
"automock mockReturnValue works": "passed",
},
}
`)
})
Loading