From 66d1ad316d98a10639101550745d506c5d2fa194 Mon Sep 17 00:00:00 2001 From: Pavel Feldman Date: Fri, 6 Mar 2026 11:14:25 -0800 Subject: [PATCH] chore: normalize remote pipe name handling --- docs/src/api/class-android.md | 4 ++-- docs/src/api/class-browsertype.md | 4 ++-- packages/playwright-client/types/types.d.ts | 20 +++++++++---------- .../playwright-core/src/client/android.ts | 4 ++-- .../playwright-core/src/client/browserType.ts | 18 ++++++++--------- packages/playwright-core/src/client/types.ts | 12 +++++------ .../playwright-core/src/client/webSocket.ts | 2 +- .../playwright-core/src/protocol/validator.ts | 3 +-- .../dispatchers/localUtilsDispatcher.ts | 10 +++++----- packages/playwright-core/types/types.d.ts | 20 +++++++++---------- packages/playwright/src/index.ts | 4 ++-- packages/protocol/src/channels.d.ts | 5 +---- packages/protocol/src/protocol.yml | 3 +-- tests/config/remoteServer.ts | 2 +- tests/library/browser-server.spec.ts | 2 +- tests/library/browsertype-connect.spec.ts | 5 ++--- .../library/browsertype-launch-server.spec.ts | 2 +- 17 files changed, 55 insertions(+), 65 deletions(-) diff --git a/docs/src/api/class-android.md b/docs/src/api/class-android.md index c976c9bba80ca..ffef68509dc6d 100644 --- a/docs/src/api/class-android.md +++ b/docs/src/api/class-android.md @@ -81,9 +81,9 @@ const { _android: android } = require('playwright'); This methods attaches Playwright to an existing Android device. Use [`method: Android.launchServer`] to launch a new Android server instance. -### param: Android.connect.wsEndpoint +### param: Android.connect.endpoint * since: v1.28 -- `wsEndpoint` <[string]> +- `endpoint` <[string]> A browser websocket endpoint to connect to. diff --git a/docs/src/api/class-browsertype.md b/docs/src/api/class-browsertype.md index ccb2af7cb72ef..fa7a0b05f7f9e 100644 --- a/docs/src/api/class-browsertype.md +++ b/docs/src/api/class-browsertype.md @@ -95,9 +95,9 @@ This method attaches Playwright to an existing browser instance created via `Bro The major and minor version of the Playwright instance that connects needs to match the version of Playwright that launches the browser (1.2.3 → is compatible with 1.2.x). ::: -### param: BrowserType.connect.wsEndpoint +### param: BrowserType.connect.endpoint * since: v1.10 -- `wsEndpoint` <[string]> +- `endpoint` <[string]> A Playwright browser websocket endpoint to connect to. You obtain this endpoint via `BrowserServer.wsEndpoint`. diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 3089d164b44a1..815eb462afc7f 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -15169,9 +15169,9 @@ export interface BrowserType { * **NOTE** Connecting over the Chrome DevTools Protocol is only supported for Chromium-based browsers. * * **NOTE** This connection is significantly lower fidelity than the Playwright protocol connection via - * [browserType.connect(wsEndpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). + * [browserType.connect(endpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). * If you are experiencing issues or attempting to use advanced functionality, you probably want to use - * [browserType.connect(wsEndpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). + * [browserType.connect(endpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). * * **Usage** * @@ -15199,9 +15199,9 @@ export interface BrowserType { * **NOTE** Connecting over the Chrome DevTools Protocol is only supported for Chromium-based browsers. * * **NOTE** This connection is significantly lower fidelity than the Playwright protocol connection via - * [browserType.connect(wsEndpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). + * [browserType.connect(endpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). * If you are experiencing issues or attempting to use advanced functionality, you probably want to use - * [browserType.connect(wsEndpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). + * [browserType.connect(endpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). * * **Usage** * @@ -15222,7 +15222,7 @@ export interface BrowserType { * **NOTE** The major and minor version of the Playwright instance that connects needs to match the version of * Playwright that launches the browser (1.2.3 → is compatible with 1.2.x). * - * @param wsEndpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via `BrowserServer.wsEndpoint`. + * @param endpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via `BrowserServer.wsEndpoint`. * @param options */ connect(wsEndpoint: string, options?: ConnectOptions): Promise; @@ -15238,7 +15238,7 @@ export interface BrowserType { * **NOTE** The major and minor version of the Playwright instance that connects needs to match the version of * Playwright that launches the browser (1.2.3 → is compatible with 1.2.x). * - * @param wsEndpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via `BrowserServer.wsEndpoint`. + * @param endpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via `BrowserServer.wsEndpoint`. * @param options */ connect(options: ConnectOptions & { wsEndpoint?: string }): Promise; @@ -15819,7 +15819,7 @@ export interface BrowserType { /** * Returns the browser app instance. You can connect to it via - * [browserType.connect(wsEndpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect), + * [browserType.connect(endpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect), * which requires the major/minor client/server version to match (1.2.3 → is compatible with 1.2.x). * * **Usage** @@ -16818,10 +16818,10 @@ export interface Android { * This methods attaches Playwright to an existing Android device. Use * [android.launchServer([options])](https://playwright.dev/docs/api/class-android#android-launch-server) to launch a * new Android server instance. - * @param wsEndpoint A browser websocket endpoint to connect to. + * @param endpoint A browser websocket endpoint to connect to. * @param options */ - connect(wsEndpoint: string, options?: { + connect(endpoint: string, options?: { /** * Additional HTTP headers to be sent with web socket connect request. Optional. */ @@ -19103,7 +19103,7 @@ export interface BrowserServer { * Browser websocket url. * * Browser websocket endpoint which can be used as an argument to - * [browserType.connect(wsEndpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect) + * [browserType.connect(endpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect) * to establish connection to the browser. * * Note that if the listen `host` option in `launchServer` options is not specified, localhost will be output anyway, diff --git a/packages/playwright-core/src/client/android.ts b/packages/playwright-core/src/client/android.ts index 834a30bbe11d7..8e4c900b512b7 100644 --- a/packages/playwright-core/src/client/android.ts +++ b/packages/playwright-core/src/client/android.ts @@ -66,11 +66,11 @@ export class Android extends ChannelOwner implements ap return await this._serverLauncher.launchServer(options); } - async connect(wsEndpoint: string, options: Parameters[1] = {}): Promise { + async connect(endpoint: string, options: Parameters[1] = {}): Promise { return await this._wrapApiCall(async () => { const deadline = options.timeout ? monotonicTime() + options.timeout : 0; const headers = { 'x-playwright-browser': 'android', ...options.headers }; - const connectParams: channels.LocalUtilsConnectParams = { wsEndpoint, headers, slowMo: options.slowMo, timeout: options.timeout || 0 }; + const connectParams: channels.LocalUtilsConnectParams = { endpoint, headers, slowMo: options.slowMo, timeout: options.timeout || 0 }; const connection = await connectOverWebSocket(this._connection, connectParams); let device: AndroidDevice; diff --git a/packages/playwright-core/src/client/browserType.ts b/packages/playwright-core/src/client/browserType.ts index e6ae29ca8c0a2..44da774a51690 100644 --- a/packages/playwright-core/src/client/browserType.ts +++ b/packages/playwright-core/src/client/browserType.ts @@ -122,13 +122,12 @@ export class BrowserType extends ChannelOwner imple } connect(options: api.ConnectOptions & { wsEndpoint: string }): Promise; - connect(options: api.ConnectOptions & { pipeName: string }): Promise; - connect(wsEndpoint: string, options?: api.ConnectOptions): Promise; - async connect(optionsOrWsEndpoint: string | (api.ConnectOptions & { wsEndpoint?: string, pipeName?: string }), options?: api.ConnectOptions): Promise{ - if (typeof optionsOrWsEndpoint === 'string') - return await this._connect({ ...options, wsEndpoint: optionsOrWsEndpoint }); - assert(optionsOrWsEndpoint.wsEndpoint || optionsOrWsEndpoint.pipeName, 'Either options.wsEndpoint or options.pipeName is required'); - return await this._connect(optionsOrWsEndpoint); + connect(endpoint: string, options?: api.ConnectOptions): Promise; + async connect(optionsOrEndpoint: string | (api.ConnectOptions & { wsEndpoint?: string, pipeName?: string }), options?: api.ConnectOptions): Promise{ + if (typeof optionsOrEndpoint === 'string') + return await this._connect({ ...options, endpoint: optionsOrEndpoint }); + assert(optionsOrEndpoint.wsEndpoint, 'options.wsEndpoint is required'); + return await this._connect(optionsOrEndpoint); } async _connect(params: ConnectOptions): Promise { @@ -137,10 +136,9 @@ export class BrowserType extends ChannelOwner imple const deadline = params.timeout ? monotonicTime() + params.timeout : 0; const headers = { 'x-playwright-browser': this.name(), ...params.headers }; const connectParams: channels.LocalUtilsConnectParams = { - wsEndpoint: params.wsEndpoint, - pipeName: params.pipeName, + endpoint: params.endpoint!, headers, - exposeNetwork: params.exposeNetwork ?? params._exposeNetwork, + exposeNetwork: params.exposeNetwork, slowMo: params.slowMo, timeout: params.timeout || 0, }; diff --git a/packages/playwright-core/src/client/types.ts b/packages/playwright-core/src/client/types.ts index 920cc3ea98fff..d926e9f8ccf2d 100644 --- a/packages/playwright-core/src/client/types.ts +++ b/packages/playwright-core/src/client/types.ts @@ -96,14 +96,12 @@ export type LaunchOptions = Omit; export type ConnectOptions = { - wsEndpoint?: string, - pipeName?: string, + endpoint?: string; headers?: { [key: string]: string; }; - exposeNetwork?: string, - _exposeNetwork?: string, - slowMo?: number, - timeout?: number, - logger?: Logger, + exposeNetwork?: string; + slowMo?: number; + timeout?: number; + logger?: Logger; }; export type LaunchServerOptions = LaunchOptions & { host?: string, diff --git a/packages/playwright-core/src/client/webSocket.ts b/packages/playwright-core/src/client/webSocket.ts index 0e56a2401bab6..7afc749573b21 100644 --- a/packages/playwright-core/src/client/webSocket.ts +++ b/packages/playwright-core/src/client/webSocket.ts @@ -88,7 +88,7 @@ class WebSocketTransport implements Transport { private _ws: WebSocket | undefined; async connect(params: channels.LocalUtilsConnectParams) { - this._ws = new window.WebSocket(params.wsEndpoint!); + this._ws = new window.WebSocket(params.endpoint); return []; } diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index 5a0f5b36bed94..2a31b6c52183d 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -314,8 +314,7 @@ scheme.LocalUtilsHarUnzipParams = tObject({ }); scheme.LocalUtilsHarUnzipResult = tOptional(tObject({})); scheme.LocalUtilsConnectParams = tObject({ - wsEndpoint: tOptional(tString), - pipeName: tOptional(tString), + endpoint: tString, headers: tOptional(tAny), exposeNetwork: tOptional(tString), slowMo: tOptional(tFloat), diff --git a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts index 655aa5df1cfba..b52b0dd33a575 100644 --- a/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/localUtilsDispatcher.ts @@ -84,9 +84,9 @@ export class LocalUtilsDispatcher extends Dispatcher { - if (params.pipeName) - return await this._connectOverPipe(params, progress); - return await this._connectOverWebSocket(params, progress); + if (URL.canParse(params.endpoint)) + return await this._connectOverWebSocket(params, progress); + return await this._connectOverPipe(params, progress); } private async _connectOverWebSocket(params: channels.LocalUtilsConnectParams, progress: Progress): Promise { @@ -95,7 +95,7 @@ export class LocalUtilsDispatcher extends Dispatcher { const socket = await new Promise((resolve, reject) => { - const conn = net.connect(params.pipeName!, () => resolve(conn)); + const conn = net.connect(params.endpoint, () => resolve(conn)); conn.on('error', reject); }); const transport = new PipeTransport(socket, socket); diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 3089d164b44a1..815eb462afc7f 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -15169,9 +15169,9 @@ export interface BrowserType { * **NOTE** Connecting over the Chrome DevTools Protocol is only supported for Chromium-based browsers. * * **NOTE** This connection is significantly lower fidelity than the Playwright protocol connection via - * [browserType.connect(wsEndpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). + * [browserType.connect(endpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). * If you are experiencing issues or attempting to use advanced functionality, you probably want to use - * [browserType.connect(wsEndpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). + * [browserType.connect(endpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). * * **Usage** * @@ -15199,9 +15199,9 @@ export interface BrowserType { * **NOTE** Connecting over the Chrome DevTools Protocol is only supported for Chromium-based browsers. * * **NOTE** This connection is significantly lower fidelity than the Playwright protocol connection via - * [browserType.connect(wsEndpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). + * [browserType.connect(endpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). * If you are experiencing issues or attempting to use advanced functionality, you probably want to use - * [browserType.connect(wsEndpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). + * [browserType.connect(endpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect). * * **Usage** * @@ -15222,7 +15222,7 @@ export interface BrowserType { * **NOTE** The major and minor version of the Playwright instance that connects needs to match the version of * Playwright that launches the browser (1.2.3 → is compatible with 1.2.x). * - * @param wsEndpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via `BrowserServer.wsEndpoint`. + * @param endpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via `BrowserServer.wsEndpoint`. * @param options */ connect(wsEndpoint: string, options?: ConnectOptions): Promise; @@ -15238,7 +15238,7 @@ export interface BrowserType { * **NOTE** The major and minor version of the Playwright instance that connects needs to match the version of * Playwright that launches the browser (1.2.3 → is compatible with 1.2.x). * - * @param wsEndpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via `BrowserServer.wsEndpoint`. + * @param endpoint A Playwright browser websocket endpoint to connect to. You obtain this endpoint via `BrowserServer.wsEndpoint`. * @param options */ connect(options: ConnectOptions & { wsEndpoint?: string }): Promise; @@ -15819,7 +15819,7 @@ export interface BrowserType { /** * Returns the browser app instance. You can connect to it via - * [browserType.connect(wsEndpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect), + * [browserType.connect(endpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect), * which requires the major/minor client/server version to match (1.2.3 → is compatible with 1.2.x). * * **Usage** @@ -16818,10 +16818,10 @@ export interface Android { * This methods attaches Playwright to an existing Android device. Use * [android.launchServer([options])](https://playwright.dev/docs/api/class-android#android-launch-server) to launch a * new Android server instance. - * @param wsEndpoint A browser websocket endpoint to connect to. + * @param endpoint A browser websocket endpoint to connect to. * @param options */ - connect(wsEndpoint: string, options?: { + connect(endpoint: string, options?: { /** * Additional HTTP headers to be sent with web socket connect request. Optional. */ @@ -19103,7 +19103,7 @@ export interface BrowserServer { * Browser websocket url. * * Browser websocket endpoint which can be used as an argument to - * [browserType.connect(wsEndpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect) + * [browserType.connect(endpoint[, options])](https://playwright.dev/docs/api/class-browsertype#browser-type-connect) * to establish connection to the browser. * * Note that if the listen `host` option in `launchServer` options is not specified, localhost will be output anyway, diff --git a/packages/playwright/src/index.ts b/packages/playwright/src/index.ts index 3f4356d15f779..72582d06fb96c 100644 --- a/packages/playwright/src/index.ts +++ b/packages/playwright/src/index.ts @@ -106,9 +106,9 @@ const playwrightFixtures: Fixtures = ({ throw new Error(`Unexpected browserName "${browserName}", must be one of "chromium", "firefox" or "webkit"`); if (connectOptions) { - const browser = await playwright[browserName].connect({ + const browser = await playwright[browserName].connect(connectOptions.wsEndpoint, { ...connectOptions, - exposeNetwork: connectOptions.exposeNetwork ?? (connectOptions as any)._exposeNetwork, + exposeNetwork: connectOptions.exposeNetwork, headers: { // HTTP headers are ASCII only (not UTF-8). 'x-playwright-launch-options': jsonStringifyForceASCII(_browserOptions), diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index 5e8c20cf200d8..ed98799521805 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -532,8 +532,7 @@ export type LocalUtilsHarUnzipOptions = { }; export type LocalUtilsHarUnzipResult = void; export type LocalUtilsConnectParams = { - wsEndpoint?: string, - pipeName?: string, + endpoint: string, headers?: any, exposeNetwork?: string, slowMo?: number, @@ -541,8 +540,6 @@ export type LocalUtilsConnectParams = { socksProxyRedirectPortForTest?: number, }; export type LocalUtilsConnectOptions = { - wsEndpoint?: string, - pipeName?: string, headers?: any, exposeNetwork?: string, slowMo?: number, diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index 344101d69e5e6..a812775aee472 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -706,8 +706,7 @@ LocalUtils: connect: internal: true parameters: - wsEndpoint: string? - pipeName: string? + endpoint: string headers: json? exposeNetwork: string? slowMo: float? diff --git a/tests/config/remoteServer.ts b/tests/config/remoteServer.ts index 1e04c0c8db610..6d5f0cc4528ee 100644 --- a/tests/config/remoteServer.ts +++ b/tests/config/remoteServer.ts @@ -126,7 +126,7 @@ export class RemoteServer implements PlaywrightServer { this._wsEndpoint = await this.out('wsEndpoint'); if (remoteServerOptions.url) { - this._browser = await this._browserType.connect({ wsEndpoint: this._wsEndpoint }); + this._browser = await this._browserType.connect(this._wsEndpoint); const page = await this._browser.newPage(); await page.goto(remoteServerOptions.url); } diff --git a/tests/library/browser-server.spec.ts b/tests/library/browser-server.spec.ts index 84b2bacc1551d..d34e5cdc617e1 100644 --- a/tests/library/browser-server.spec.ts +++ b/tests/library/browser-server.spec.ts @@ -32,7 +32,7 @@ it('should start and stop pipe server', async ({ browserType, browser }) => { pipeName: expect.stringMatching(/browser@.*\.sock/), })); - const browser2 = await (browserType as any).connect(serverInfo); + const browser2 = await (browserType as any).connect(serverInfo.pipeName); const page = await browser2.newPage(); await page.goto('data:text/html,

Hello via pipe

'); expect(await page.locator('h1').textContent()).toBe('Hello via pipe'); diff --git a/tests/library/browsertype-connect.spec.ts b/tests/library/browsertype-connect.spec.ts index b3ccee17ab55c..e146492aad382 100644 --- a/tests/library/browsertype-connect.spec.ts +++ b/tests/library/browsertype-connect.spec.ts @@ -620,8 +620,7 @@ for (const kind of ['launchServer', 'run-server'] as const) { test('should be able to connect when the wsEndpoint is passed as an option', async ({ browserType, startRemoteServer }) => { const remoteServer = await startRemoteServer(kind); - const browser = await browserType.connect({ - wsEndpoint: remoteServer.wsEndpoint(), + const browser = await browserType.connect(remoteServer.wsEndpoint(), { headers: { 'x-playwright-launch-options': JSON.stringify((browserType as any)._playwright._defaultLaunchOptions || {}), }, @@ -850,7 +849,7 @@ for (const kind of ['launchServer', 'run-server'] as const) { }); const examplePort = 20_000 + testInfo.workerIndex * 3; const remoteServer = await startRemoteServer(kind); - const browser = await connect(remoteServer.wsEndpoint(), { _exposeNetwork: '*' } as any, dummyServerPort); + const browser = await connect(remoteServer.wsEndpoint(), { exposeNetwork: '*' } as any, dummyServerPort); const page = await browser.newPage(); { await page.setContent('empty'); diff --git a/tests/library/browsertype-launch-server.spec.ts b/tests/library/browsertype-launch-server.spec.ts index fed973dde57d1..cc6490fb270fd 100644 --- a/tests/library/browsertype-launch-server.spec.ts +++ b/tests/library/browsertype-launch-server.spec.ts @@ -66,7 +66,7 @@ it.describe('launch server', () => { it('should provide an error when ws endpoint is incorrect', async ({ browserType }) => { const browserServer = await browserType.launchServer(); - const error = await browserType.connect({ wsEndpoint: browserServer.wsEndpoint() + '-foo' }).catch(e => e); + const error = await browserType.connect(browserServer.wsEndpoint() + '-foo').catch(e => e); await browserServer.close(); expect(error.message).toContain('400 Bad Request'); });