Skip to content

Commit d3acbd8

Browse files
authored
fix(browser): fix mocking modules out of root (#7415)
1 parent 1154662 commit d3acbd8

File tree

6 files changed

+61
-11
lines changed

6 files changed

+61
-11
lines changed

packages/mocker/src/browser/mocker.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,13 @@ export class ModuleMocker {
8282

8383
public async importMock<T>(rawId: string, importer: string): Promise<T> {
8484
await this.prepare()
85-
const { resolvedId, redirectUrl } = await this.rpc.resolveMock(
85+
const { resolvedId, resolvedUrl, redirectUrl } = await this.rpc.resolveMock(
8686
rawId,
8787
importer,
8888
{ mock: 'auto' },
8989
)
9090

91-
const mockUrl = this.resolveMockPath(cleanVersion(resolvedId))
91+
const mockUrl = this.resolveMockPath(cleanVersion(resolvedUrl))
9292
let mock = this.registry.get(mockUrl)
9393

9494
if (!mock) {
@@ -139,8 +139,8 @@ export class ModuleMocker {
139139
? 'factory'
140140
: factoryOrOptions?.spy ? 'spy' : 'auto',
141141
})
142-
.then(async ({ redirectUrl, resolvedId, needsInterop, mockType }) => {
143-
const mockUrl = this.resolveMockPath(cleanVersion(resolvedId))
142+
.then(async ({ redirectUrl, resolvedId, resolvedUrl, needsInterop, mockType }) => {
143+
const mockUrl = this.resolveMockPath(cleanVersion(resolvedUrl))
144144
this.mockedIds.add(resolvedId)
145145
const factory = typeof factoryOrOptions === 'function'
146146
? async () => {
@@ -185,7 +185,7 @@ export class ModuleMocker {
185185
if (!resolved) {
186186
return
187187
}
188-
const mockUrl = this.resolveMockPath(cleanVersion(resolved.id))
188+
const mockUrl = this.resolveMockPath(cleanVersion(resolved.url))
189189
this.mockedIds.add(resolved.id)
190190
this.registry.delete(mockUrl)
191191
await this.interceptor.delete(mockUrl)
@@ -241,6 +241,7 @@ export interface ResolveIdResult {
241241
export interface ResolveMockResult {
242242
mockType: MockedModuleType
243243
resolvedId: string
244+
resolvedUrl: string
244245
redirectUrl?: string | null
245246
needsInterop?: boolean
246247
}

packages/mocker/src/node/resolver.ts

+13-6
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,16 @@ export class ServerMockResolver {
2626
options: { mock: 'spy' | 'factory' | 'auto' },
2727
): Promise<ServerMockResolution> {
2828
const { id, fsPath, external } = await this.resolveMockId(rawId, importer)
29+
const resolvedUrl = this.normalizeResolveIdToUrl({ id }).url
2930

3031
if (options.mock === 'factory') {
3132
const manifest = getViteDepsManifest(this.server.config)
3233
const needsInterop = manifest?.[fsPath]?.needsInterop ?? false
33-
return { mockType: 'manual', resolvedId: id, needsInterop }
34+
return { mockType: 'manual', resolvedId: id, resolvedUrl, needsInterop }
3435
}
3536

3637
if (options.mock === 'spy') {
37-
return { mockType: 'autospy', resolvedId: id }
38+
return { mockType: 'autospy', resolvedId: id, resolvedUrl }
3839
}
3940

4041
const redirectUrl = findMockRedirect(this.server.config.root, fsPath, external)
@@ -43,6 +44,7 @@ export class ServerMockResolver {
4344
mockType: redirectUrl === null ? 'automock' : 'redirect',
4445
redirectUrl,
4546
resolvedId: id,
47+
resolvedUrl,
4648
}
4749
}
4850

@@ -67,10 +69,14 @@ export class ServerMockResolver {
6769
if (!resolved) {
6870
return null
6971
}
72+
return this.normalizeResolveIdToUrl(resolved)
73+
}
74+
75+
private normalizeResolveIdToUrl(resolved: { id: string }) {
7076
const isOptimized = resolved.id.startsWith(withTrailingSlash(this.server.config.cacheDir))
7177
let url: string
7278
// normalise the URL to be acceptable by the browser
73-
// https://github.com/vitejs/vite/blob/e833edf026d495609558fd4fb471cf46809dc369/packages/vite/src/node/plugins/importAnalysis.ts#L335
79+
// https://github.com/vitejs/vite/blob/14027b0f2a9b01c14815c38aab22baf5b29594bb/packages/vite/src/node/plugins/importAnalysis.ts#L103
7480
const root = this.server.config.root
7581
if (resolved.id.startsWith(withTrailingSlash(root))) {
7682
url = resolved.id.slice(root.length)
@@ -86,9 +92,9 @@ export class ServerMockResolver {
8692
url = resolved.id
8793
}
8894
if (url[0] !== '.' && url[0] !== '/') {
89-
url = id.startsWith(VALID_ID_PREFIX)
90-
? id
91-
: VALID_ID_PREFIX + id.replace('\0', '__x00__')
95+
url = resolved.id.startsWith(VALID_ID_PREFIX)
96+
? resolved.id
97+
: VALID_ID_PREFIX + resolved.id.replace('\0', '__x00__')
9298
}
9399
return {
94100
id: resolved.id,
@@ -177,6 +183,7 @@ function withTrailingSlash(path: string): string {
177183
export interface ServerMockResolution {
178184
mockType: 'manual' | 'redirect' | 'automock' | 'autospy'
179185
resolvedId: string
186+
resolvedUrl: string
180187
needsInterop?: boolean
181188
redirectUrl?: string | null
182189
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { test, expect, vi } from 'vitest';
2+
import project2 from "../project2/index.js"
3+
4+
vi.mock("../project2/index.js", () => ({
5+
default: 'project2-mocked'
6+
}))
7+
8+
test("basic", () => {
9+
expect(project2).toMatchInlineSnapshot(`"project2-mocked"`)
10+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { defineConfig } from 'vitest/config';
2+
import { fileURLToPath } from 'node:url'
3+
import { instances, provider } from '../../../settings'
4+
5+
// BROWSER=chromium pnpm -C test/browser test-fixtures --root fixtures/mocking-out-of-root/project1
6+
7+
export default defineConfig({
8+
cacheDir: fileURLToPath(new URL("./node_modules/.vite", import.meta.url)),
9+
root: import.meta.dirname,
10+
test: {
11+
browser: {
12+
enabled: true,
13+
provider: provider,
14+
screenshotFailures: false,
15+
instances,
16+
},
17+
},
18+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default "project2"

test/browser/specs/mocking.test.ts

+13
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,16 @@ test('mocking dependency correctly invalidates it on rerun', async () => {
6363
expect(vitest.stdout).not.toReportPassedTest('2_not-mocked-import.test.ts', browser)
6464
})
6565
})
66+
67+
test('mocking out of root', async () => {
68+
const { vitest, ctx } = await runVitest({
69+
root: 'fixtures/mocking-out-of-root/project1',
70+
})
71+
onTestFinished(async () => {
72+
await ctx.close()
73+
})
74+
expect(vitest.stderr).toReportNoErrors()
75+
instances.forEach(({ browser }) => {
76+
expect(vitest.stdout).toReportPassedTest('basic.test.js', browser)
77+
})
78+
})

0 commit comments

Comments
 (0)