Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: don't clear mocks with vi.resetModules, cache normalised mock paths #1726

Merged
merged 3 commits into from
Jul 27, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/vitest/src/integrations/vi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@ class VitestUtils {
}

public resetModules() {
resetModules()
const state = getWorkerState()
resetModules(state.moduleCache)
return this
}

Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/runtime/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export async function run(files: string[], config: ResolvedConfig): Promise<void
await withEnv(environment, config.environmentOptions || {}, async () => {
for (const file of files) {
workerState.mockMap.clear()
resetModules()
resetModules(workerState.moduleCache, true)

workerState.filepath = file

Expand Down
33 changes: 19 additions & 14 deletions packages/vitest/src/runtime/mocker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,17 +103,20 @@ export class VitestMocker {
}

private async callFunctionMock(dep: string, mock: () => any) {
const cacheName = `${dep}__mock`
const cached = this.moduleCache.get(cacheName)?.exports
const cached = this.moduleCache.get(dep)?.exports
if (cached)
return cached
const exports = await mock()
this.moduleCache.set(cacheName, { exports })
this.moduleCache.set(dep, { exports })
return exports
}

public getDependencyMock(dep: string) {
return this.getMocks()[this.normalizePath(dep)]
private getMockPath(dep: string) {
return `mock:${dep}`
}

public getDependencyMock(id: string) {
return this.getMocks()[id]
}

public normalizePath(path: string) {
Expand Down Expand Up @@ -263,7 +266,8 @@ export class VitestMocker {
const { path, external } = await this.resolvePath(id, importer)

const fsPath = this.getFsPath(path, external)
let mock = this.getDependencyMock(fsPath)
const normalizedId = this.normalizePath(fsPath)
let mock = this.getDependencyMock(normalizedId)

if (mock === undefined)
mock = this.resolveMockPath(fsPath, external)
Expand Down Expand Up @@ -291,25 +295,26 @@ export class VitestMocker {
this.resolveMocks(),
])

const mock = this.getDependencyMock(dep)
const id = this.normalizePath(dep)
const mock = this.getDependencyMock(id)

const callstack = this.request.callstack
const mockPath = this.getMockPath(id)

if (mock === null) {
const cacheName = `${dep}__mock`
const cache = this.moduleCache.get(cacheName)
const cache = this.moduleCache.get(mockPath)
if (cache?.exports)
return cache.exports
const cacheKey = toFilePath(dep, this.root)
const mod = this.moduleCache.get(cacheKey)?.exports || await this.request(dep)
const exports = this.mockObject(mod)
this.moduleCache.set(cacheName, { exports })
this.moduleCache.set(mockPath, { exports })
return exports
}
if (typeof mock === 'function' && !callstack.includes(`mock:${dep}`)) {
callstack.push(`mock:${dep}`)
const result = await this.callFunctionMock(dep, mock)
const indexMock = callstack.indexOf(`mock:${dep}`)
if (typeof mock === 'function' && !callstack.includes(mockPath)) {
callstack.push(mockPath)
const result = await this.callFunctionMock(mockPath, mock)
const indexMock = callstack.indexOf(mockPath)
callstack.splice(indexMock, 1)
return result
}
Expand Down
2 changes: 1 addition & 1 deletion packages/vitest/src/runtime/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ function init(ctx: WorkerContext) {
if (ctx.invalidates) {
ctx.invalidates.forEach((fsPath) => {
moduleCache.delete(fsPath)
moduleCache.delete(`${fsPath}__mock`)
moduleCache.delete(`mock:${fsPath}`)
})
}
ctx.files.forEach(i => moduleCache.delete(i))
Expand Down
11 changes: 6 additions & 5 deletions packages/vitest/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import { relative as relativeBrowser } from 'path'
import c from 'picocolors'
import { isPackageExists } from 'local-pkg'
import { relative as relativeNode } from 'pathe'
import type { ModuleCacheMap } from 'vite-node'
import type { Suite, Task } from '../types'
import { getWorkerState } from '../utils/global'
import { getNames } from './tasks'

export * from './tasks'
Expand Down Expand Up @@ -40,18 +40,19 @@ export function partitionSuiteChildren(suite: Suite) {
return tasksGroups
}

export function resetModules() {
const modules = getWorkerState().moduleCache
const vitestPaths = [
export function resetModules(modules: ModuleCacheMap, resetMocks = false) {
const skipPaths = [
// Vitest
/\/vitest\/dist\//,
// yarn's .store folder
/vitest-virtual-\w+\/dist/,
// cnpm
/@vitest\/dist/,
// don't clear mocks
...(!resetMocks ? [/^mock:/] : []),
]
modules.forEach((_, path) => {
if (vitestPaths.some(re => re.test(path)))
if (skipPaths.some(re => re.test(path)))
return
modules.delete(path)
})
Expand Down
2 changes: 1 addition & 1 deletion packages/web-worker/src/pure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export function defineWebWorker() {
invalidates.forEach((fsPath) => {
// worker should be new every time
moduleCache.delete(fsPath)
moduleCache.delete(`${fsPath}__mock`)
moduleCache.delete(`mock:${fsPath}`)
})
const q = this.messageQueue
this.messageQueue = null
Expand Down
37 changes: 36 additions & 1 deletion test/core/test/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, expect, test } from 'vitest'
import { assertTypes, deepClone, deepMerge, toArray } from '../../../packages/vitest/src/utils'
import { assertTypes, deepClone, deepMerge, resetModules, toArray } from '../../../packages/vitest/src/utils'
import { deepMergeSnapshot } from '../../../packages/vitest/src/integrations/snapshot/port/utils'
import type { ModuleCacheMap } from '../../../packages/vite-node/src/types'

describe('assertTypes', () => {
test('the type of value should be number', () => {
Expand Down Expand Up @@ -147,3 +148,37 @@ describe('deepClone', () => {
expect(deepClone(objD)).toEqual(objD)
})
})

describe('resetModules doesn\'t resets only user modules', () => {
test('resets user modules', () => {
const moduleCache = new Map() as ModuleCacheMap
moduleCache.set('/some-module.ts', {})
moduleCache.set('/@fs/some-path.ts', {})

resetModules(moduleCache)

expect(moduleCache.size).toBe(0)
})

test('doesn\'t reset vitest modules', () => {
const moduleCache = new Map() as ModuleCacheMap
moduleCache.set('/node_modules/vitest/dist/index.js', {})
moduleCache.set('/node_modules/vitest-virtual-da9876a/dist/index.js', {})
moduleCache.set('/node_modules/some-module@vitest/dist/index.js', {})
moduleCache.set('/packages/vitest/dist/index.js', {})

resetModules(moduleCache)

expect(moduleCache.size).toBe(4)
})

test('doesn\'t reset mocks', () => {
const moduleCache = new Map() as ModuleCacheMap
moduleCache.set('mock:/some-module.ts', {})
moduleCache.set('mock:/@fs/some-path.ts', {})

resetModules(moduleCache)

expect(moduleCache.size).toBe(2)
})
})