Skip to content

Commit 057b4f3

Browse files
authoredJul 4, 2024··
fix(browser): correctly mock optimized cjs dependencies (#6035)
1 parent 42bd4a2 commit 057b4f3

File tree

6 files changed

+70
-11
lines changed

6 files changed

+70
-11
lines changed
 

Diff for: ‎packages/browser/src/client/tester/mocker.ts

+18-5
Original file line numberDiff line numberDiff line change
@@ -138,16 +138,24 @@ export class VitestBrowserClientMocker {
138138
public queueMock(id: string, importer: string, factory?: () => any) {
139139
const promise = rpc()
140140
.resolveMock(id, importer, !!factory)
141-
.then(async ({ mockPath, resolvedId }) => {
141+
.then(async ({ mockPath, resolvedId, needsInterop }) => {
142142
this.ids.add(resolvedId)
143-
const urlPaths = resolveMockPaths(resolvedId)
143+
const urlPaths = resolveMockPaths(cleanVersion(resolvedId))
144144
const resolvedMock
145145
= typeof mockPath === 'string'
146-
? new URL(resolvedMockedPath(mockPath), location.href).toString()
146+
? new URL(resolvedMockedPath(cleanVersion(mockPath)), location.href).toString()
147147
: mockPath
148+
const _factory = factory && needsInterop
149+
? async () => {
150+
const data = await factory()
151+
return { default: data }
152+
}
153+
: factory
148154
urlPaths.forEach((url) => {
149155
this.mocks[url] = resolvedMock
150-
this.factories[url] = factory!
156+
if (_factory) {
157+
this.factories[url] = _factory
158+
}
151159
})
152160
channel.postMessage({
153161
type: 'mock',
@@ -170,7 +178,7 @@ export class VitestBrowserClientMocker {
170178
return
171179
}
172180
this.ids.delete(resolved.id)
173-
const urlPaths = resolveMockPaths(resolved.id)
181+
const urlPaths = resolveMockPaths(cleanVersion(resolved.id))
174182
urlPaths.forEach((url) => {
175183
delete this.mocks[url]
176184
delete this.factories[url]
@@ -445,3 +453,8 @@ function resolveMockPaths(path: string) {
445453

446454
return paths
447455
}
456+
457+
const versionRegexp = /(\?|&)v=\w{8}/
458+
function cleanVersion(url: string) {
459+
return url.replace(versionRegexp, '')
460+
}

Diff for: ‎packages/browser/src/client/tester/msw.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export function createModuleMocker() {
1313

1414
const worker = setupWorker(
1515
http.get(/.+/, async ({ request }) => {
16-
const path = removeTimestamp(request.url.slice(location.origin.length))
16+
const path = cleanQuery(request.url.slice(location.origin.length))
1717
if (!mocks.has(path)) {
1818
return passthrough()
1919
}
@@ -112,9 +112,9 @@ function getFactoryExports(id: string) {
112112
}
113113

114114
const timestampRegexp = /(\?|&)t=\d{13}/
115-
116-
function removeTimestamp(url: string) {
117-
return url.replace(timestampRegexp, '')
115+
const versionRegexp = /(\?|&)v=\w{8}/
116+
function cleanQuery(url: string) {
117+
return url.replace(timestampRegexp, '').replace(versionRegexp, '')
118118
}
119119

120120
function passthrough() {

Diff for: ‎packages/browser/src/node/resolveMock.ts

+31-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import { existsSync, readdirSync } from 'node:fs'
1+
import { existsSync, readFileSync, readdirSync } from 'node:fs'
22
import { builtinModules } from 'node:module'
33
import { basename, dirname, extname, isAbsolute, join, resolve } from 'pathe'
44
import type { PartialResolvedId } from 'rollup'
55
import type { ResolvedConfig } from 'vitest'
6+
import type { ResolvedConfig as ViteConfig } from 'vite'
67
import type { WorkspaceProject } from 'vitest/node'
78

89
export async function resolveMock(
@@ -14,7 +15,9 @@ export async function resolveMock(
1415
const { id, fsPath, external } = await resolveId(project, rawId, importer)
1516

1617
if (hasFactory) {
17-
return { type: 'factory' as const, resolvedId: id }
18+
const needsInteropMap = viteDepsInteropMap(project.browser!.vite.config)
19+
const needsInterop = needsInteropMap?.get(fsPath) ?? false
20+
return { type: 'factory' as const, resolvedId: id, needsInterop }
1821
}
1922

2023
const mockPath = resolveMockPath(project.config.root, fsPath, external)
@@ -126,3 +129,29 @@ const postfixRE = /[?#].*$/
126129
export function cleanUrl(url: string): string {
127130
return url.replace(postfixRE, '')
128131
}
132+
133+
const metadata = new WeakMap<ViteConfig, Map<string, boolean>>()
134+
135+
function viteDepsInteropMap(config: ViteConfig) {
136+
if (metadata.has(config)) {
137+
return metadata.get(config)!
138+
}
139+
const cacheDirPath = getDepsCacheDir(config)
140+
const metadataPath = resolve(cacheDirPath, '_metadata.json')
141+
if (!existsSync(metadataPath)) {
142+
return null
143+
}
144+
const { optimized } = JSON.parse(readFileSync(metadataPath, 'utf-8'))
145+
const needsInteropMap = new Map()
146+
for (const name in optimized) {
147+
const dep = optimized[name]
148+
const file = resolve(cacheDirPath, dep.file)
149+
needsInteropMap.set(file, dep.needsInterop)
150+
}
151+
metadata.set(config, needsInteropMap)
152+
return needsInteropMap
153+
}
154+
155+
function getDepsCacheDir(config: ViteConfig): string {
156+
return resolve(config.cacheDir, 'deps')
157+
}

Diff for: ‎packages/browser/src/node/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export interface WebSocketBrowserHandlers {
3535
type: 'factory' | 'redirect' | 'automock'
3636
mockPath?: string | null
3737
resolvedId: string
38+
needsInterop?: boolean
3839
}>
3940
invalidate: (ids: string[]) => void
4041
getBrowserFileSourceMap: (

Diff for: ‎test/browser/fixtures/mocking/mocking-dep.test.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { a } from '@vitest/cjs-lib'
2+
import { expect, test, vi } from 'vitest'
3+
4+
vi.mock(import('@vitest/cjs-lib'), () => {
5+
return {
6+
a: 'mocked',
7+
}
8+
})
9+
10+
test('mocking works correctly', () => {
11+
expect(a).toBe('mocked')
12+
})

Diff for: ‎test/browser/fixtures/mocking/vitest.config.ts

+4
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ const name =
55
process.env.BROWSER || (provider === 'playwright' ? 'chromium' : 'chrome')
66

77
export default defineConfig({
8+
optimizeDeps: {
9+
include: ['@vitest/cjs-lib'],
10+
needsInterop: ['@vitest/cjs-lib'],
11+
},
812
test: {
913
browser: {
1014
enabled: true,

0 commit comments

Comments
 (0)
Please sign in to comment.