diff --git a/packages/browser/src/node/rpc.ts b/packages/browser/src/node/rpc.ts index cc69e7814..a252a9819 100644 --- a/packages/browser/src/node/rpc.ts +++ b/packages/browser/src/node/rpc.ts @@ -32,6 +32,7 @@ import { isValidApiRequest, } from 'vitest/node' import { WebSocketServer } from 'ws' +import { slash } from './utils' const debug = createDebugger('vitest:browser:api') @@ -293,7 +294,7 @@ export function setupBrowserRpc( for (const attachment of attachments) { const path = attachment.path if (path && !path.startsWith('http://') && !path.startsWith('https://')) { - checkFileAccess(resolve(project.config.root, path)) + checkFileAccess(resolve(project.config.root, slash(path))) } } } @@ -304,7 +305,7 @@ export function setupBrowserRpc( return vitest._testRun.recordBenchmark(testId, benchmark) }, async readBenchmarkResult(relativePath) { - checkFileAccess(project.benchmark.resolve(relativePath)) + checkFileAccess(project.benchmark.resolve(slash(relativePath))) return project.benchmark.readResult(relativePath) }, async writeBenchmarkResult(relativePath, data) { @@ -314,7 +315,7 @@ export function setupBrowserRpc( ) return } - checkFileAccess(project.benchmark.resolve(relativePath)) + checkFileAccess(project.benchmark.resolve(slash(relativePath))) return project.benchmark.writeResult(relativePath, data) }, async onTaskUpdate(method, packs, events) { @@ -479,8 +480,9 @@ export function setupBrowserRpc( } else { if (module.type === 'redirect') { const redirectUrl = new URL(module.redirect) - module.redirect = join(vite.config.root, redirectUrl.pathname) - checkFileAccess(module.redirect) + const redirect = join(vite.config.root, slash(redirectUrl.pathname)) + checkFileAccess(redirect) + module.redirect = redirect } defaultMockerRegistry.register(module) } diff --git a/packages/mocker/src/node/interceptorPlugin.ts b/packages/mocker/src/node/interceptorPlugin.ts index 3319823db..65150373f 100644 --- a/packages/mocker/src/node/interceptorPlugin.ts +++ b/packages/mocker/src/node/interceptorPlugin.ts @@ -2,6 +2,7 @@ import type { Plugin } from 'vite' import type { MockedModuleSerialized } from '../registry' import { readFile } from 'node:fs/promises' import { join } from 'node:path/posix' +import { slash } from '@vitest/utils/helpers' import { isFileLoadingAllowed } from 'vite' import { ManualMockedModule, MockerRegistry } from '../registry' import { cleanUrl, createManualModuleSource } from '../utils' @@ -24,7 +25,7 @@ export interface InterceptorPluginOptions { export function interceptorPlugin(options: InterceptorPluginOptions = {}): Plugin { const registry = options.registry || new MockerRegistry() - return { + const plugin: Plugin = { name: 'vitest:mocks:interceptor', enforce: 'pre', load: { @@ -63,10 +64,10 @@ export function interceptorPlugin(options: InterceptorPluginOptions = {}): Plugi } }, }, - configureServer(server) { - if (options.registerWebSocketEvents === false) { - return - } + } + + if (options.registerWebSocketEvents !== false) { + plugin.configureServer = (server) => { server.ws.on('vitest:interceptor:register', (event: MockedModuleSerialized) => { if (event.type === 'manual') { const module = ManualMockedModule.fromJSON(event, async () => { @@ -77,7 +78,7 @@ export function interceptorPlugin(options: InterceptorPluginOptions = {}): Plugi } else { if (event.type === 'redirect') { const redirectUrl = new URL(event.redirect) - const redirect = join(server.config.root, redirectUrl.pathname) + const redirect = join(server.config.root, slash(redirectUrl.pathname)) // the redirect is served through the `load` hook below, so it must // stay inside the file-serving allowlist and never escape the root if (!isFileLoadingAllowed(server.config, redirect)) { @@ -117,6 +118,8 @@ export function interceptorPlugin(options: InterceptorPluginOptions = {}): Plugi ) }) } - }, + } } + + return plugin } diff --git a/packages/mocker/src/utils.ts b/packages/mocker/src/utils.ts index 26143ba6f..43bdaa9c1 100644 --- a/packages/mocker/src/utils.ts +++ b/packages/mocker/src/utils.ts @@ -9,12 +9,12 @@ export function createManualModuleSource( globalAccessor = '"__vitest_mocker__"', ): string { const source = ` -const __factoryModule__ = await globalThis[${globalAccessor}].getFactoryModule("${moduleUrl}"); +const __factoryModule__ = await globalThis[${globalAccessor}].getFactoryModule(${JSON.stringify(moduleUrl)}); ` const keys = exports .map((name, index) => { - return `let __${index} = __factoryModule__["${name}"] -export { __${index} as "${name}" }` + return `let __${index} = __factoryModule__[${JSON.stringify(name)}] +export { __${index} as ${JSON.stringify(name)} }` }) .join('\n') let code = `${source}\n${keys}` @@ -24,7 +24,7 @@ if (__factoryModule__.__factoryPromise != null) { __factoryModule__.__factoryPromise.then((resolvedModule) => { ${exports .map((name, index) => { - return `__${index} = resolvedModule["${name}"];` + return `__${index} = resolvedModule[${JSON.stringify(name)}];` }) .join('\n')} })