From 4c47fdceb58a7890eb12d89c4fdac0442903ac8f Mon Sep 17 00:00:00 2001 From: Dokyung Kim Date: Sun, 25 Jan 2026 22:56:57 +0900 Subject: [PATCH 1/4] fix: correctly encode project name in browser client URL (#9242) This fixes an issue where project names containing special characters (like '&') were not correctly encoded in the WebSocket connection URL, causing connection failures in browser mode. --- packages/browser/src/client/client.ts | 2 +- test/browser/specs/client-url.test.ts | 50 +++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 test/browser/specs/client-url.test.ts diff --git a/packages/browser/src/client/client.ts b/packages/browser/src/client/client.ts index 84e6a260e3c3..b489d6046625 100644 --- a/packages/browser/src/client/client.ts +++ b/packages/browser/src/client/client.ts @@ -18,7 +18,7 @@ export const RPC_ID: string const METHOD = getBrowserState().method export const ENTRY_URL: string = `${ location.protocol === 'https:' ? 'wss:' : 'ws:' -}//${HOST}/__vitest_browser_api__?type=${PAGE_TYPE}&rpcId=${RPC_ID}&sessionId=${getBrowserState().sessionId}&projectName=${getBrowserState().config.name || ''}&method=${METHOD}&token=${(window as any).VITEST_API_TOKEN || '0'}` +}//${HOST}/__vitest_browser_api__?type=${PAGE_TYPE}&rpcId=${RPC_ID}&sessionId=${getBrowserState().sessionId}&projectName=${encodeURIComponent(getBrowserState().config.name || '')}&method=${METHOD}&token=${(window as any).VITEST_API_TOKEN || '0'}` const onCancelCallbacks: ((reason: CancelReason) => void)[] = [] diff --git a/test/browser/specs/client-url.test.ts b/test/browser/specs/client-url.test.ts new file mode 100644 index 000000000000..627f4518e490 --- /dev/null +++ b/test/browser/specs/client-url.test.ts @@ -0,0 +1,50 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' + +describe('Browser Client URL Encoding', () => { + beforeEach(() => { + vi.resetModules() + + vi.stubGlobal('window', { + __vitest_browser_runner__: { + type: 'tester', + sessionId: 'session-123', + testerId: 'tester-456', + method: 'run', + config: { + name: 'Components & Hooks', + }, + }, + VITEST_API_TOKEN: 'token-789', + }) + + vi.stubGlobal('location', { + protocol: 'http:', + hostname: 'localhost', + port: '5173', + }) + + vi.stubGlobal('WebSocket', class MockWebSocket { + constructor(public url: string) {} + addEventListener() {} + send() {} + close() {} + }) + }) + + afterEach(() => { + vi.unstubAllGlobals() + }) + + it('should encode project name with ampersand in ENTRY_URL', async () => { + const { ENTRY_URL } = await import('../../../packages/browser/src/client/client') + + const url = new URL(ENTRY_URL) + + expect(ENTRY_URL).toContain('projectName=Components%20%26%20Hooks') + + expect(url.searchParams.get('projectName')).toBe('Components & Hooks') + + expect(url.searchParams.get('type')).toBe('tester') + expect(url.searchParams.get('sessionId')).toBe('session-123') + }) +}) From f533cc81ab9eaa392c7af6cac9b3b5c305643bcd Mon Sep 17 00:00:00 2001 From: Dokyung Kim Date: Mon, 26 Jan 2026 18:30:35 +0900 Subject: [PATCH 2/4] test(browser): add integration test for project name encoding - Replace unit test mock with a real browser integration test - Add fixture with project name containing an ampersand ('&') --- .../project-name-encoding/basic.test.ts | 5 ++ .../project-name-encoding/vitest.config.ts | 14 ++++++ test/browser/specs/client-url.test.ts | 50 ------------------- .../specs/project-name-encoding.test.ts | 13 +++++ 4 files changed, 32 insertions(+), 50 deletions(-) create mode 100644 test/browser/fixtures/project-name-encoding/basic.test.ts create mode 100644 test/browser/fixtures/project-name-encoding/vitest.config.ts delete mode 100644 test/browser/specs/client-url.test.ts create mode 100644 test/browser/specs/project-name-encoding.test.ts diff --git a/test/browser/fixtures/project-name-encoding/basic.test.ts b/test/browser/fixtures/project-name-encoding/basic.test.ts new file mode 100644 index 000000000000..29093bc5f5ab --- /dev/null +++ b/test/browser/fixtures/project-name-encoding/basic.test.ts @@ -0,0 +1,5 @@ +import { expect, test } from 'vitest' + +test('basic test', () => { + expect(true).toBe(true) +}) diff --git a/test/browser/fixtures/project-name-encoding/vitest.config.ts b/test/browser/fixtures/project-name-encoding/vitest.config.ts new file mode 100644 index 000000000000..edc2ddfd1c46 --- /dev/null +++ b/test/browser/fixtures/project-name-encoding/vitest.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vitest/config' +import { provider } from '../../settings' + +export default defineConfig({ + test: { + name: 'Components & Hooks', + browser: { + enabled: true, + provider, + instances: [{ browser: 'chromium' }], + headless: true, + }, + }, +}) diff --git a/test/browser/specs/client-url.test.ts b/test/browser/specs/client-url.test.ts deleted file mode 100644 index 627f4518e490..000000000000 --- a/test/browser/specs/client-url.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' - -describe('Browser Client URL Encoding', () => { - beforeEach(() => { - vi.resetModules() - - vi.stubGlobal('window', { - __vitest_browser_runner__: { - type: 'tester', - sessionId: 'session-123', - testerId: 'tester-456', - method: 'run', - config: { - name: 'Components & Hooks', - }, - }, - VITEST_API_TOKEN: 'token-789', - }) - - vi.stubGlobal('location', { - protocol: 'http:', - hostname: 'localhost', - port: '5173', - }) - - vi.stubGlobal('WebSocket', class MockWebSocket { - constructor(public url: string) {} - addEventListener() {} - send() {} - close() {} - }) - }) - - afterEach(() => { - vi.unstubAllGlobals() - }) - - it('should encode project name with ampersand in ENTRY_URL', async () => { - const { ENTRY_URL } = await import('../../../packages/browser/src/client/client') - - const url = new URL(ENTRY_URL) - - expect(ENTRY_URL).toContain('projectName=Components%20%26%20Hooks') - - expect(url.searchParams.get('projectName')).toBe('Components & Hooks') - - expect(url.searchParams.get('type')).toBe('tester') - expect(url.searchParams.get('sessionId')).toBe('session-123') - }) -}) diff --git a/test/browser/specs/project-name-encoding.test.ts b/test/browser/specs/project-name-encoding.test.ts new file mode 100644 index 000000000000..477bf1eaae38 --- /dev/null +++ b/test/browser/specs/project-name-encoding.test.ts @@ -0,0 +1,13 @@ +import { expect, test } from 'vitest' +import { runBrowserTests } from './utils' + +test('runs tests correctly when project name contains special characters', async () => { + const { stderr, stdout, exitCode } = await runBrowserTests({ + root: 'test/browser/fixtures/project-name-encoding', + }) + + expect(stderr).toBe('') + expect(exitCode).toBe(0) + expect(stdout).toContain('Test Files 1 passed') + expect(stdout).toContain('Components & Hooks') +}) From c56b9b6764b4fc1a9f92a7782f0822f25a4ebd08 Mon Sep 17 00:00:00 2001 From: Dokyung Kim Date: Wed, 28 Jan 2026 18:56:30 +0900 Subject: [PATCH 3/4] test(browser): improve project name encoding integration test --- .../fixtures/project-name-encoding/vitest.config.ts | 6 +++--- test/browser/specs/project-name-encoding.test.ts | 13 ++++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/test/browser/fixtures/project-name-encoding/vitest.config.ts b/test/browser/fixtures/project-name-encoding/vitest.config.ts index edc2ddfd1c46..38d21858f8c3 100644 --- a/test/browser/fixtures/project-name-encoding/vitest.config.ts +++ b/test/browser/fixtures/project-name-encoding/vitest.config.ts @@ -1,13 +1,13 @@ import { defineConfig } from 'vitest/config' -import { provider } from '../../settings' +import { playwright } from '@vitest/browser-playwright' export default defineConfig({ test: { name: 'Components & Hooks', browser: { enabled: true, - provider, - instances: [{ browser: 'chromium' }], + provider: playwright(), + instances: [{ browser: 'chromium' }, { browser: 'firefox' }, { browser: 'webkit' }], headless: true, }, }, diff --git a/test/browser/specs/project-name-encoding.test.ts b/test/browser/specs/project-name-encoding.test.ts index 477bf1eaae38..7318b26d2d97 100644 --- a/test/browser/specs/project-name-encoding.test.ts +++ b/test/browser/specs/project-name-encoding.test.ts @@ -1,13 +1,16 @@ import { expect, test } from 'vitest' -import { runBrowserTests } from './utils' +import { instances, runBrowserTests } from './utils' test('runs tests correctly when project name contains special characters', async () => { - const { stderr, stdout, exitCode } = await runBrowserTests({ - root: 'test/browser/fixtures/project-name-encoding', + const { stderr, stdout, exitCode, ctx } = await runBrowserTests({ + root: './fixtures/project-name-encoding', }) expect(stderr).toBe('') expect(exitCode).toBe(0) - expect(stdout).toContain('Test Files 1 passed') - expect(stdout).toContain('Components & Hooks') + + const projectName = ctx.config.name + instances.forEach(({ browser }) => { + expect(stdout).toReportPassedTest('basic.test.ts', `${projectName} (${browser})`) + }) }) From ef3223d14f8202ec57d2f515a0ed2757a009ceec Mon Sep 17 00:00:00 2001 From: Dokyung Kim Date: Wed, 28 Jan 2026 21:24:24 +0900 Subject: [PATCH 4/4] test(browser): refactor project name encoding test to be provider-agnostic --- .../fixtures/project-name-encoding/vitest.config.ts | 3 --- test/browser/specs/project-name-encoding.test.ts | 7 ++++++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/test/browser/fixtures/project-name-encoding/vitest.config.ts b/test/browser/fixtures/project-name-encoding/vitest.config.ts index 38d21858f8c3..0afbb6b84a76 100644 --- a/test/browser/fixtures/project-name-encoding/vitest.config.ts +++ b/test/browser/fixtures/project-name-encoding/vitest.config.ts @@ -1,13 +1,10 @@ import { defineConfig } from 'vitest/config' -import { playwright } from '@vitest/browser-playwright' export default defineConfig({ test: { name: 'Components & Hooks', browser: { enabled: true, - provider: playwright(), - instances: [{ browser: 'chromium' }, { browser: 'firefox' }, { browser: 'webkit' }], headless: true, }, }, diff --git a/test/browser/specs/project-name-encoding.test.ts b/test/browser/specs/project-name-encoding.test.ts index 7318b26d2d97..3c6dcea1a39f 100644 --- a/test/browser/specs/project-name-encoding.test.ts +++ b/test/browser/specs/project-name-encoding.test.ts @@ -1,15 +1,20 @@ import { expect, test } from 'vitest' -import { instances, runBrowserTests } from './utils' +import { instances, provider, runBrowserTests } from './utils' test('runs tests correctly when project name contains special characters', async () => { const { stderr, stdout, exitCode, ctx } = await runBrowserTests({ root: './fixtures/project-name-encoding', + browser: { + provider, + instances, + }, }) expect(stderr).toBe('') expect(exitCode).toBe(0) const projectName = ctx.config.name + instances.forEach(({ browser }) => { expect(stdout).toReportPassedTest('basic.test.ts', `${projectName} (${browser})`) })