diff --git a/packages/browser/src/client/logger.ts b/packages/browser/src/client/logger.ts index c65eaa3f3290..8a4e615f0ea4 100644 --- a/packages/browser/src/client/logger.ts +++ b/packages/browser/src/client/logger.ts @@ -3,8 +3,8 @@ import { importId } from './utils' const { Date, console } = globalThis -export async function setupConsoleLogSpy() { - const { stringify, format, inspect } = await importId('vitest/utils') as typeof import('vitest/utils') +export async function setupConsoleLogSpy(basePath: string) { + const { stringify, format, inspect } = await importId('vitest/utils', basePath) as typeof import('vitest/utils') const { log, info, error, dir, dirxml, trace, time, timeEnd, timeLog, warn, debug, count, countReset } = console const formatInput = (input: unknown) => { if (input instanceof Node) diff --git a/packages/browser/src/client/main.ts b/packages/browser/src/client/main.ts index 1e8420e0867b..61ec052e4ec7 100644 --- a/packages/browser/src/client/main.ts +++ b/packages/browser/src/client/main.ts @@ -3,7 +3,7 @@ import type { ResolvedConfig } from 'vitest' import type { CancelReason, VitestRunner } from '@vitest/runner' import type { VitestExecutor } from '../../../vitest/src/runtime/execute' import { createBrowserRunner } from './runner' -import { importId } from './utils' +import { importId as importIdImpl } from './utils' import { setupConsoleLogSpy } from './logger' import { createSafeRpc, rpc, rpcDone } from './rpc' import { setupDialogsSpy } from './dialog' @@ -24,6 +24,10 @@ const url = new URL(location.href) const testId = url.searchParams.get('id') || 'unknown' const reloadTries = Number(url.searchParams.get('reloadTries') || '0') +const basePath = () => config!.base! || '/' +const importId = (id: string) => importIdImpl(id, basePath()) +const viteClientPath = () => `${basePath()}@vite/client` + function getQueryPaths() { return url.searchParams.getAll('path') } @@ -181,15 +185,14 @@ ws.addEventListener('open', async () => { const iFrame = document.getElementById('vitest-ui') as HTMLIFrameElement iFrame.setAttribute('src', '/__vitest__/') - await setupConsoleLogSpy() + await setupConsoleLogSpy(basePath()) setupDialogsSpy() await runTests(paths, config!) }) async function prepareTestEnvironment(config: ResolvedConfig) { // need to import it before any other import, otherwise Vite optimizer will hang - const viteClientPath = '/@vite/client' - await import(viteClientPath) + await import(viteClientPath()) const { startTests, @@ -204,7 +207,7 @@ async function prepareTestEnvironment(config: ResolvedConfig) { if (!runner) { const { VitestTestRunner } = await importId('vitest/runners') as typeof import('vitest/runners') - const BrowserRunner = createBrowserRunner(VitestTestRunner, { takeCoverage: () => takeCoverageInsideWorker(config.coverage, executor) }) + const BrowserRunner = createBrowserRunner(VitestTestRunner, { takeCoverage: () => takeCoverageInsideWorker(config.coverage, executor) }, basePath()) runner = new BrowserRunner({ config, browserHashMap }) } diff --git a/packages/browser/src/client/runner.ts b/packages/browser/src/client/runner.ts index cfa79438d840..4b00a5f24c97 100644 --- a/packages/browser/src/client/runner.ts +++ b/packages/browser/src/client/runner.ts @@ -14,6 +14,7 @@ interface CoverageHandler { export function createBrowserRunner( original: { new(config: ResolvedConfig): VitestRunner }, coverageModule: CoverageHandler | null, + basePath: string, ): { new(options: BrowserRunnerOptions): VitestRunner } { return class BrowserTestRunner extends original { public config: ResolvedConfig @@ -71,9 +72,9 @@ export function createBrowserRunner( } // on Windows we need the unit to resolve the test file - const importpath = /^\w:/.test(filepath) - ? `/@fs/${filepath}?${test ? 'browserv' : 'v'}=${hash}` - : `${filepath}?${test ? 'browserv' : 'v'}=${hash}` + const prefix = `${basePath}${/^\w:/.test(filepath) ? '@fs/' : ''}` + const query = `${test ? 'browserv' : 'v'}=${hash}` + const importpath = `${prefix}${filepath}?${query}`.replace(/\/+/g, '/') await import(importpath) } } diff --git a/packages/browser/src/client/utils.ts b/packages/browser/src/client/utils.ts index 27a390af601d..a4c1a856117f 100644 --- a/packages/browser/src/client/utils.ts +++ b/packages/browser/src/client/utils.ts @@ -1,5 +1,5 @@ -export async function importId(id: string) { - const name = `/@id/${id}` +export async function importId(id: string, basePath: string) { + const name = `${basePath}@id/${id}` // @ts-expect-error mocking vitest apis return __vi_wrap_module__(import(name)) } diff --git a/test/browser/specs/fix-4686.test.mjs b/test/browser/specs/fix-4686.test.mjs new file mode 100644 index 000000000000..9caf2d4169e0 --- /dev/null +++ b/test/browser/specs/fix-4686.test.mjs @@ -0,0 +1,18 @@ +import assert from 'node:assert' +import test from 'node:test' +import runVitest from './run-vitest.mjs' + +const { + stderr, + browserResultJson, + passedTests, + failedTests, +} = await runVitest(['--config', 'vitest.config-basepath.mts']) + +await test('tests run in presence of config.base', async () => { + assert.ok(browserResultJson.testResults.length === 8, 'Not all the tests have been run') + assert.ok(passedTests.length === 7, 'Some tests failed') + assert.ok(failedTests.length === 1, 'Some tests have passed but should fail') + + assert.doesNotMatch(stderr, /Unhandled Error/, 'doesn\'t have any unhandled errors') +}) diff --git a/test/browser/vitest.config-basepath.mts b/test/browser/vitest.config-basepath.mts new file mode 100644 index 000000000000..360753c0586d --- /dev/null +++ b/test/browser/vitest.config-basepath.mts @@ -0,0 +1,4 @@ +import { defineConfig, mergeConfig } from 'vitest/config' +import baseConfig from './vitest.config.mts' + +export default mergeConfig(baseConfig, defineConfig({ base: '/fix-4686' }))