Skip to content

Commit

Permalink
fix(mocker): set cache before mocking to allow circular dependencies (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
danez committed Dec 2, 2022
1 parent a6a94cc commit fd8292a
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 6 deletions.
14 changes: 8 additions & 6 deletions packages/vitest/src/runtime/mocker.ts
@@ -1,7 +1,7 @@
import { existsSync, readdirSync } from 'fs'
import { isNodeBuiltin } from 'mlly'
import { basename, dirname, extname, join, resolve } from 'pathe'
import { normalizeRequestId, pathFromRoot, toFilePath } from 'vite-node/utils'
import { normalizeRequestId, pathFromRoot } from 'vite-node/utils'
import type { ModuleCacheMap } from 'vite-node/client'
import { getAllMockableProperties, getType, getWorkerState, mergeSlashes, slash } from '../utils'
import { distDir } from '../constants'
Expand Down Expand Up @@ -195,7 +195,7 @@ export class VitestMocker {
return existsSync(fullPath) ? fullPath : null
}

public mockObject(object: Record<string | symbol, any>) {
public mockObject(object: Record<Key, any>, mockExports: Record<Key, any> = {}) {
if (!VitestMocker.spyModule) {
throw new Error(
'Error: Spy module is not defined. '
Expand Down Expand Up @@ -275,7 +275,7 @@ export class VitestMocker {
}
}

const mockedObject: Record<Key, any> = {}
const mockedObject: Record<Key, any> = mockExports
mockPropertiesOf(object, mockedObject)

// Plug together refs
Expand Down Expand Up @@ -360,10 +360,12 @@ export class VitestMocker {
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)

const exports = {}
// Assign the empty exports object early to allow for cycles to work. The object will be filled by mockObject()
this.moduleCache.set(mockPath, { exports })
const mod = await this.request(dep)
this.mockObject(mod, exports)
return exports
}
if (typeof mock === 'function' && !callstack.includes(mockPath)) {
Expand Down
12 changes: 12 additions & 0 deletions test/core/test/mocked-circular.test.ts
@@ -0,0 +1,12 @@
import { expect, it, vi } from 'vitest'
// The order of the two imports here matters: B before A
import { circularB } from '../src/circularB'
import { circularA } from '../src/circularA'

vi.mock('../src/circularB')

it('circular', () => {
circularA()

expect(circularB).toHaveBeenCalledOnce()
})

0 comments on commit fd8292a

Please sign in to comment.