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: mocking works with base url #1581

Merged
merged 3 commits into from Jul 3, 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
12 changes: 2 additions & 10 deletions packages/vitest/src/runtime/execute.ts
@@ -1,5 +1,5 @@
import { ViteNodeRunner } from 'vite-node/client'
import type { ModuleCache, ViteNodeRunnerOptions } from 'vite-node'
import type { ViteNodeRunnerOptions } from 'vite-node'
import { normalizePath } from 'vite'
import type { MockMap } from '../types/mocker'
import { getWorkerState } from '../utils'
Expand All @@ -23,23 +23,15 @@ export async function executeInViteNode(options: ExecuteOptions & { files: strin
}

export class VitestRunner extends ViteNodeRunner {
mocker: VitestMocker
entries = new Set<string>()

constructor(public options: ExecuteOptions) {
super(options)
this.mocker = new VitestMocker(options, this.moduleCache)
}

prepareContext(context: Record<string, any>) {
const request = context.__vite_ssr_import__
const resolveId = context.__vitest_resolve_id__

const mocker = this.mocker.withRequest(request)

mocker.on('mocked', (dep: string, module: Partial<ModuleCache>) => {
this.moduleCache.set(dep, module)
})
const mocker = new VitestMocker(this.options, this.moduleCache, request)

const workerState = getWorkerState()

Expand Down
63 changes: 23 additions & 40 deletions packages/vitest/src/runtime/mocker.ts
Expand Up @@ -8,8 +8,6 @@ import { distDir } from '../constants'
import type { PendingSuiteMock } from '../types/mocker'
import type { ExecuteOptions } from './execute'

type Callback = (...args: any[]) => unknown

interface ViteRunnerRequest {
(dep: string): any
callstack: string[]
Expand All @@ -19,31 +17,22 @@ export class VitestMocker {
private static pendingIds: PendingSuiteMock[] = []
private static spyModule?: typeof import('../integrations/spy')

private request!: ViteRunnerRequest

private root: string
private callbacks: Record<string, ((...args: any[]) => unknown)[]> = {}

constructor(
public options: ExecuteOptions,
private moduleCache: ModuleCacheMap,
request?: ViteRunnerRequest,
) {
this.root = this.options.root
this.request = request!
}
private request: ViteRunnerRequest,
) {}

get mockMap() {
return this.options.mockMap
private get root() {
return this.options.root
}

public on(event: string, cb: Callback) {
this.callbacks[event] ??= []
this.callbacks[event].push(cb)
private get base() {
return this.options.base
}

private emit(event: string, ...args: any[]) {
(this.callbacks[event] ?? []).forEach(fn => fn(...args))
private get mockMap() {
return this.options.mockMap
}

public getSuiteFilepath(): string {
Expand Down Expand Up @@ -91,20 +80,16 @@ export class VitestMocker {
if (cached)
return cached
const exports = await mock()
this.emit('mocked', cacheName, { exports })
this.moduleCache.set(cacheName, { exports })
return exports
}

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

public resolveDependency(dep: string) {
return normalizeRequestId(dep.replace(this.root, '')).replace(/^\/@fs\//, isWindows ? '' : '/')
return this.getMocks()[this.normalizePath(dep)]
}

public normalizePath(path: string) {
return normalizeRequestId(path.replace(this.root, '')).replace(/^\/@fs\//, isWindows ? '' : '/')
return normalizeRequestId(path.replace(this.root, ''), this.base).replace(/^\/@fs\//, isWindows ? '' : '/')
}

public getFsPath(path: string, external: string | null) {
Expand Down Expand Up @@ -183,22 +168,22 @@ export class VitestMocker {
public unmockPath(path: string) {
const suitefile = this.getSuiteFilepath()

const fsPath = this.normalizePath(path)
const id = this.normalizePath(path)

const mock = this.mockMap.get(suitefile)
if (mock?.[fsPath])
delete mock[fsPath]
if (mock?.[id])
delete mock[id]
}

public mockPath(path: string, external: string | null, factory?: () => any) {
const suitefile = this.getSuiteFilepath()
const id = this.normalizePath(path)

const fsPath = this.normalizePath(path)
const mocks = this.mockMap.get(suitefile) || {}

if (!this.mockMap.has(suitefile))
this.mockMap.set(suitefile, {})
mocks[id] = factory || this.resolveMockPath(path, external)

this.mockMap.get(suitefile)![fsPath] = factory || this.resolveMockPath(path, external)
this.mockMap.set(suitefile, mocks)
}

public async importActual<T>(id: string, importer: string): Promise<T> {
Expand Down Expand Up @@ -234,8 +219,10 @@ export class VitestMocker {
}

public async requestWithMock(dep: string) {
await this.ensureSpy()
await this.resolveMocks()
await Promise.all([
this.ensureSpy(),
this.resolveMocks(),
])

const mock = this.getDependencyMock(dep)

Expand All @@ -249,7 +236,7 @@ export class VitestMocker {
const cacheKey = toFilePath(dep, this.root)
const mod = this.moduleCache.get(cacheKey)?.exports || await this.request(dep)
const exports = this.mockValue(mod)
this.emit('mocked', cacheName, { exports })
this.moduleCache.set(cacheName, { exports })
return exports
}
if (typeof mock === 'function' && !callstack.includes(`mock:${dep}`)) {
Expand All @@ -271,8 +258,4 @@ export class VitestMocker {
public queueUnmock(id: string, importer: string) {
VitestMocker.pendingIds.push({ type: 'unmock', id, importer })
}

public withRequest(request: ViteRunnerRequest) {
return new VitestMocker(this.options, this.moduleCache, request)
}
}
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions test/base/package.json
@@ -0,0 +1,11 @@
{
"name": "@vitest/test-base",
"private": true,
"scripts": {
"test": "vitest",
"coverage": "vitest run --coverage"
},
"devDependencies": {
"vitest": "workspace:*"
}
}
1 change: 1 addition & 0 deletions test/base/src/index.ts
@@ -0,0 +1 @@
export const foo = 'foo'
10 changes: 10 additions & 0 deletions test/base/test/mocking-with-base.spec.ts
@@ -0,0 +1,10 @@
import { expect, test, vi } from 'vitest'
import { foo } from '../src'

vi.mock('../src', () => ({
foo: 'baz',
}))

test('module is mocked', () => {
expect(foo).toBe('baz')
})
5 changes: 5 additions & 0 deletions test/base/vitest.config.ts
@@ -0,0 +1,5 @@
import { defineConfig } from 'vitest/config'

export default defineConfig({
base: '/some/base/url',
})