diff --git a/docs/src/api/class-browsertype.md b/docs/src/api/class-browsertype.md index 0cc7f37ba1..fe0c91fc1b 100644 --- a/docs/src/api/class-browsertype.md +++ b/docs/src/api/class-browsertype.md @@ -116,7 +116,7 @@ Connecting over the Chrome DevTools Protocol is only supported for Chromium-base ### param: BrowserType.connectOverCDP.params * langs: js - `params` <[Object]> - - `wsEndpoint` <[string]> A CDP websocket endpoint to connect to. + - `endpointURL` <[string]> A CDP websocket endpoint or http url to connect to. For example `http://localhost:9222/` or `ws://127.0.0.1:9222/devtools/browser/387adf4c-243f-4051-a181-46798f4a46f4`. - `slowMo` <[float]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. Defaults to 0. - `logger` <[Logger]> Logger sink for Playwright logging. Optional. diff --git a/docs/src/api/java.md b/docs/src/api/java.md index 17fc9045c1..8e51cd99c5 100644 --- a/docs/src/api/java.md +++ b/docs/src/api/java.md @@ -58,11 +58,11 @@ page.navigate("https://www.w3.org/"); playwright.close(); ``` -### param: BrowserType.connectOverCDP.wsEndpoint +### param: BrowserType.connectOverCDP.endpointURL * langs: java -- `wsEndpoint` <[string]> +- `endpointURL` <[string]> -A CDP websocket endpoint to connect to. +A CDP websocket endpoint or http url to connect to. For example `http://localhost:9222/` or `ws://127.0.0.1:9222/devtools/browser/387adf4c-243f-4051-a181-46798f4a46f4`. ### param: BrowserContext.waitForPage.callback = %%-java-wait-for-event-callback-%% diff --git a/src/client/browserType.ts b/src/client/browserType.ts index 7206a573a0..fbd01113fe 100644 --- a/src/client/browserType.ts +++ b/src/client/browserType.ts @@ -187,14 +187,16 @@ export class BrowserType extends ChannelOwner { + async connectOverCDP(params: api.ConnectOverCDPOptions): Promise + async connectOverCDP(params: api.ConnectOptions): Promise + async connectOverCDP(params: api.ConnectOverCDPOptions | api.ConnectOptions): Promise { if (this.name() !== 'chromium') throw new Error('Connecting over CDP is only supported in Chromium.'); const logger = params.logger; return this._wrapApiCall('browserType.connectOverCDP', async (channel: channels.BrowserTypeChannel) => { const result = await channel.connectOverCDP({ sdkLanguage: 'javascript', - wsEndpoint: params.wsEndpoint, + endpointURL: 'endpointURL' in params ? params.endpointURL : params.wsEndpoint, slowMo: params.slowMo, timeout: params.timeout }); diff --git a/src/dispatchers/browserTypeDispatcher.ts b/src/dispatchers/browserTypeDispatcher.ts index e865ee2a44..49eda39b8a 100644 --- a/src/dispatchers/browserTypeDispatcher.ts +++ b/src/dispatchers/browserTypeDispatcher.ts @@ -40,7 +40,7 @@ export class BrowserTypeDispatcher extends Dispatcher { - const browser = await this._object.connectOverCDP(metadata, params.wsEndpoint, params, params.timeout); + const browser = await this._object.connectOverCDP(metadata, params.endpointURL, params, params.timeout); return { browser: new BrowserDispatcher(this._scope, browser), defaultContext: browser._defaultContext ? new BrowserContextDispatcher(this._scope, browser._defaultContext) : undefined, diff --git a/src/protocol/channels.ts b/src/protocol/channels.ts index acdffef168..c419866920 100644 --- a/src/protocol/channels.ts +++ b/src/protocol/channels.ts @@ -408,7 +408,7 @@ export type BrowserTypeLaunchPersistentContextResult = { }; export type BrowserTypeConnectOverCDPParams = { sdkLanguage: string, - wsEndpoint: string, + endpointURL: string, slowMo?: number, timeout?: number, }; diff --git a/src/protocol/protocol.yml b/src/protocol/protocol.yml index 79da2fb11f..8b87a3a433 100644 --- a/src/protocol/protocol.yml +++ b/src/protocol/protocol.yml @@ -422,7 +422,7 @@ BrowserType: connectOverCDP: parameters: sdkLanguage: string - wsEndpoint: string + endpointURL: string slowMo: number? timeout: number? returns: diff --git a/src/protocol/validator.ts b/src/protocol/validator.ts index e1247f1c82..c07e8e78c7 100644 --- a/src/protocol/validator.ts +++ b/src/protocol/validator.ts @@ -249,7 +249,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { }); scheme.BrowserTypeConnectOverCDPParams = tObject({ sdkLanguage: tString, - wsEndpoint: tString, + endpointURL: tString, slowMo: tOptional(tNumber), timeout: tOptional(tNumber), }); diff --git a/src/server/browserType.ts b/src/server/browserType.ts index 8845267e51..539048578b 100644 --- a/src/server/browserType.ts +++ b/src/server/browserType.ts @@ -248,7 +248,7 @@ export abstract class BrowserType extends SdkObject { return { browserProcess, downloadsPath, transport }; } - async connectOverCDP(metadata: CallMetadata, wsEndpoint: string, options: { slowMo?: number, sdkLanguage: string }, timeout?: number): Promise { + async connectOverCDP(metadata: CallMetadata, endpointURL: string, options: { slowMo?: number, sdkLanguage: string }, timeout?: number): Promise { throw new Error('CDP connections are only supported by Chromium'); } diff --git a/src/server/chromium/chromium.ts b/src/server/chromium/chromium.ts index e091e55d2c..2531f29440 100644 --- a/src/server/chromium/chromium.ts +++ b/src/server/chromium/chromium.ts @@ -32,6 +32,7 @@ import { TimeoutSettings } from '../../utils/timeoutSettings'; import { helper } from '../helper'; import { CallMetadata } from '../instrumentation'; import { findChromiumChannel } from './findChromiumChannel'; +import http from 'http'; export class Chromium extends BrowserType { private _devtools: CRDevTools | undefined; @@ -49,12 +50,12 @@ export class Chromium extends BrowserType { return super.executablePath(options); } - async connectOverCDP(metadata: CallMetadata, wsEndpoint: string, options: { slowMo?: number, sdkLanguage: string }, timeout?: number) { + async connectOverCDP(metadata: CallMetadata, endpointURL: string, options: { slowMo?: number, sdkLanguage: string }, timeout?: number) { const controller = new ProgressController(metadata, this); controller.setLogName('browser'); const browserLogsCollector = new RecentLogsCollector(); return controller.run(async progress => { - const chromeTransport = await WebSocketTransport.connect(progress, wsEndpoint); + const chromeTransport = await WebSocketTransport.connect(progress, await urlToWSEndpoint(endpointURL)); const browserProcess: BrowserProcess = { close: async () => { await chromeTransport.closeAndWait(); @@ -192,3 +193,17 @@ const DEFAULT_ARGS = [ '--password-store=basic', '--use-mock-keychain', ]; + +async function urlToWSEndpoint(endpointURL: string) { + if (endpointURL.startsWith('ws')) + return endpointURL; + const httpURL = endpointURL.endsWith('/') ? `${endpointURL}json/version/` : `${endpointURL}/json/version/`; + const json = await new Promise((resolve, reject) => { + http.get(httpURL, resp => { + let data = ''; + resp.on('data', chunk => data += chunk); + resp.on('end', () => resolve(data)); + }).on('error', reject); + }); + return JSON.parse(json).webSocketDebuggerUrl; +} diff --git a/tests/browsertype-basic.spec.ts b/tests/browsertype-basic.spec.ts index a24242ee6d..d4890020b8 100644 --- a/tests/browsertype-basic.spec.ts +++ b/tests/browsertype-basic.spec.ts @@ -33,6 +33,6 @@ test('browserType.name should work', async ({browserType, browserName}) => { test('should throw when trying to connect with not-chromium', async ({ browserType, browserName }) => { test.skip(browserName === 'chromium'); - const error = await browserType.connectOverCDP({wsEndpoint: 'foo'}).catch(e => e); + const error = await browserType.connectOverCDP({endpointURL: 'ws://foo'}).catch(e => e); expect(error.message).toBe('Connecting over CDP is only supported in Chromium.'); }); diff --git a/tests/chromium/chromium.spec.ts b/tests/chromium/chromium.spec.ts index fe4f324d11..579238e647 100644 --- a/tests/chromium/chromium.spec.ts +++ b/tests/chromium/chromium.spec.ts @@ -112,15 +112,8 @@ playwrightTest.describe('chromium', () => { args: ['--remote-debugging-port=' + port] }); try { - const json = await new Promise((resolve, reject) => { - http.get(`http://localhost:${port}/json/version/`, resp => { - let data = ''; - resp.on('data', chunk => data += chunk); - resp.on('end', () => resolve(data)); - }).on('error', reject); - }); const cdpBrowser = await browserType.connectOverCDP({ - wsEndpoint: JSON.parse(json).webSocketDebuggerUrl, + endpointURL: `http://localhost:${port}/`, }); const contexts = cdpBrowser.contexts(); expect(contexts.length).toBe(1); @@ -137,18 +130,11 @@ playwrightTest.describe('chromium', () => { args: ['--remote-debugging-port=' + port] }); try { - const json = await new Promise((resolve, reject) => { - http.get(`http://localhost:${port}/json/version/`, resp => { - let data = ''; - resp.on('data', chunk => data += chunk); - resp.on('end', () => resolve(data)); - }).on('error', reject); - }); const cdpBrowser1 = await browserType.connectOverCDP({ - wsEndpoint: JSON.parse(json).webSocketDebuggerUrl, + endpointURL: `http://localhost:${port}/`, }); const cdpBrowser2 = await browserType.connectOverCDP({ - wsEndpoint: JSON.parse(json).webSocketDebuggerUrl, + endpointURL: `http://localhost:${port}/`, }); const contexts1 = cdpBrowser1.contexts(); expect(contexts1.length).toBe(1); @@ -179,15 +165,8 @@ playwrightTest.describe('chromium', () => { args: ['--remote-debugging-port=' + port] }); try { - const json = await new Promise((resolve, reject) => { - http.get(`http://localhost:${port}/json/version/`, resp => { - let data = ''; - resp.on('data', chunk => data += chunk); - resp.on('end', () => resolve(data)); - }).on('error', reject); - }); const cdpBrowser1 = await browserType.connectOverCDP({ - wsEndpoint: JSON.parse(json).webSocketDebuggerUrl, + endpointURL: `http://localhost:${port}`, }); const context = cdpBrowser1.contexts()[0]; const page = await cdpBrowser1.contexts()[0].newPage(); @@ -199,7 +178,7 @@ playwrightTest.describe('chromium', () => { await cdpBrowser1.close(); const cdpBrowser2 = await browserType.connectOverCDP({ - wsEndpoint: JSON.parse(json).webSocketDebuggerUrl, + endpointURL: `http://localhost:${port}`, }); const context2 = cdpBrowser2.contexts()[0]; expect(context2.serviceWorkers().length).toBe(1); @@ -208,4 +187,36 @@ playwrightTest.describe('chromium', () => { await browserServer.close(); } }); + playwrightTest('should connect over a ws endpoint', async ({browserType, browserOptions, server}, testInfo) => { + const port = 9339 + testInfo.workerIndex; + const browserServer = await browserType.launch({ + ...browserOptions, + args: ['--remote-debugging-port=' + port] + }); + try { + const json = await new Promise((resolve, reject) => { + http.get(`http://localhost:${port}/json/version/`, resp => { + let data = ''; + resp.on('data', chunk => data += chunk); + resp.on('end', () => resolve(data)); + }).on('error', reject); + }); + const cdpBrowser = await browserType.connectOverCDP({ + endpointURL: JSON.parse(json).webSocketDebuggerUrl, + }); + const contexts = cdpBrowser.contexts(); + expect(contexts.length).toBe(1); + await cdpBrowser.close(); + + // also connect with the depercreated wsEndpoint option + const cdpBrowser2 = await browserType.connectOverCDP({ + wsEndpoint: JSON.parse(json).webSocketDebuggerUrl, + }); + const contexts2 = cdpBrowser.contexts(); + expect(contexts2.length).toBe(1); + await cdpBrowser2.close(); + } finally { + await browserServer.close(); + } + }); }); diff --git a/types/types.d.ts b/types/types.d.ts index a9c6a14516..118f46490f 100644 --- a/types/types.d.ts +++ b/types/types.d.ts @@ -6460,13 +6460,6 @@ export interface ElementHandle extends JSHandle { * */ export interface BrowserType { - - /** - * This methods attaches Playwright to an existing browser instance. - * @param params - */ - connect(params: ConnectOptions): Promise; - /** * This methods attaches Playwright to an existing browser instance using the Chrome DevTools Protocol. * @@ -6476,29 +6469,17 @@ export interface BrowserType { * > NOTE: Connecting over the Chrome DevTools Protocol is only supported for Chromium-based browsers. * @param params */ - connectOverCDP(params: { - /** - * A CDP websocket endpoint to connect to. - */ - wsEndpoint: string; - - /** - * Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. - * Defaults to 0. - */ - slowMo?: number; - - /** - * Logger sink for Playwright logging. Optional. - */ - logger?: Logger; - - /** - * Maximum time in milliseconds to wait for the connection to be established. Defaults to `30000` (30 seconds). Pass `0` to - * disable timeout. - */ - timeout?: number; - }): Promise; + connectOverCDP(options: ConnectOverCDPOptions): Promise; + /** + * Option `wsEndpoint` is deprecated. Instead use `endpointURL`. + * @deprecated + */ + connectOverCDP(options: ConnectOptions): Promise; + /** + * This methods attaches Playwright to an existing browser instance. + * @param params + */ + connect(params: ConnectOptions): Promise; /** * A path where Playwright expects to find a bundled browser executable. @@ -10687,6 +10668,31 @@ export interface LaunchOptions { timeout?: number; } +export interface ConnectOverCDPOptions { + /** + * A CDP websocket endpoint or http url to connect to. For example `http://localhost:9222/` or + * `ws://127.0.0.1:9222/devtools/browser/387adf4c-243f-4051-a181-46798f4a46f4`. + */ + endpointURL: string; + + /** + * Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on. + * Defaults to 0. + */ + slowMo?: number; + + /** + * Logger sink for Playwright logging. Optional. + */ + logger?: Logger; + + /** + * Maximum time in milliseconds to wait for the connection to be established. Defaults to `30000` (30 seconds). Pass `0` to + * disable timeout. + */ + timeout?: number; +} + export interface ConnectOptions { /** * A browser websocket endpoint to connect to. diff --git a/utils/generate_types/exported.json b/utils/generate_types/exported.json index 436ad21730..0fe766c915 100644 --- a/utils/generate_types/exported.json +++ b/utils/generate_types/exported.json @@ -1,6 +1,7 @@ { "BrowserTypeLaunchOptions": "LaunchOptions", "BrowserTypeConnectParams": "ConnectOptions", + "BrowserTypeConnectOverCDPParams": "ConnectOverCDPOptions", "BrowserContextCookies": "Cookie", "BrowserNewContextOptions": "BrowserContextOptions", "BrowserNewContextOptionsViewport": "ViewportSize", diff --git a/utils/generate_types/overrides.d.ts b/utils/generate_types/overrides.d.ts index c9ba48c7f0..fa80db64a5 100644 --- a/utils/generate_types/overrides.d.ts +++ b/utils/generate_types/overrides.d.ts @@ -141,7 +141,12 @@ export interface ElementHandle extends JSHandle { } export interface BrowserType { - + connectOverCDP(options: ConnectOverCDPOptions): Promise; + /** + * Option `wsEndpoint` is deprecated. Instead use `endpointURL`. + * @deprecated + */ + connectOverCDP(options: ConnectOptions): Promise; } export interface CDPSession {