Skip to content

Commit 057b4f3

Browse files
authored
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

packages/browser/src/client/tester/mocker.ts

Lines changed: 18 additions & 5 deletions
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+
}

packages/browser/src/client/tester/msw.ts

Lines changed: 4 additions & 4 deletions
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() {

packages/browser/src/node/resolveMock.ts

Lines changed: 31 additions & 2 deletions
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+
}

packages/browser/src/node/types.ts

Lines changed: 1 addition & 0 deletions
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: (
Lines changed: 12 additions & 0 deletions
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+
})

test/browser/fixtures/mocking/vitest.config.ts

Lines changed: 4 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy