feat(cdp): replace wsEndpoint with protocol neutral endpointURL (#6141)

This commit is contained in:
Joel Einbinder 2021-04-08 14:55:28 -07:00 committed by GitHub
parent 53d50f9b72
commit 63d0d466e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 111 additions and 71 deletions

View File

@ -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.

View File

@ -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-%%

View File

@ -187,14 +187,16 @@ export class BrowserType extends ChannelOwner<channels.BrowserTypeChannel, chann
}, logger);
}
async connectOverCDP(params: ConnectOptions): Promise<Browser> {
async connectOverCDP(params: api.ConnectOverCDPOptions): Promise<Browser>
async connectOverCDP(params: api.ConnectOptions): Promise<Browser>
async connectOverCDP(params: api.ConnectOverCDPOptions | api.ConnectOptions): Promise<Browser> {
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
});

View File

@ -40,7 +40,7 @@ export class BrowserTypeDispatcher extends Dispatcher<BrowserType, channels.Brow
}
async connectOverCDP(params: channels.BrowserTypeConnectOverCDPParams, metadata: CallMetadata): Promise<channels.BrowserTypeConnectOverCDPResult> {
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,

View File

@ -408,7 +408,7 @@ export type BrowserTypeLaunchPersistentContextResult = {
};
export type BrowserTypeConnectOverCDPParams = {
sdkLanguage: string,
wsEndpoint: string,
endpointURL: string,
slowMo?: number,
timeout?: number,
};

View File

@ -422,7 +422,7 @@ BrowserType:
connectOverCDP:
parameters:
sdkLanguage: string
wsEndpoint: string
endpointURL: string
slowMo: number?
timeout: number?
returns:

View File

@ -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),
});

View File

@ -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<Browser> {
async connectOverCDP(metadata: CallMetadata, endpointURL: string, options: { slowMo?: number, sdkLanguage: string }, timeout?: number): Promise<Browser> {
throw new Error('CDP connections are only supported by Chromium');
}

View File

@ -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<string>((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;
}

View File

@ -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.');
});

View File

@ -112,15 +112,8 @@ playwrightTest.describe('chromium', () => {
args: ['--remote-debugging-port=' + port]
});
try {
const json = await new Promise<string>((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<string>((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<string>((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<string>((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();
}
});
});

66
types/types.d.ts vendored
View File

@ -6460,13 +6460,6 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
*
*/
export interface BrowserType<Unused = {}> {
/**
* This methods attaches Playwright to an existing browser instance.
* @param params
*/
connect(params: ConnectOptions): Promise<Browser>;
/**
* This methods attaches Playwright to an existing browser instance using the Chrome DevTools Protocol.
*
@ -6476,29 +6469,17 @@ export interface BrowserType<Unused = {}> {
* > 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<Browser>;
connectOverCDP(options: ConnectOverCDPOptions): Promise<Browser>;
/**
* Option `wsEndpoint` is deprecated. Instead use `endpointURL`.
* @deprecated
*/
connectOverCDP(options: ConnectOptions): Promise<Browser>;
/**
* This methods attaches Playwright to an existing browser instance.
* @param params
*/
connect(params: ConnectOptions): Promise<Browser>;
/**
* 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.

View File

@ -1,6 +1,7 @@
{
"BrowserTypeLaunchOptions": "LaunchOptions",
"BrowserTypeConnectParams": "ConnectOptions",
"BrowserTypeConnectOverCDPParams": "ConnectOverCDPOptions",
"BrowserContextCookies": "Cookie",
"BrowserNewContextOptions": "BrowserContextOptions",
"BrowserNewContextOptionsViewport": "ViewportSize",

View File

@ -141,7 +141,12 @@ export interface ElementHandle<T=Node> extends JSHandle<T> {
}
export interface BrowserType<Unused = {}> {
connectOverCDP(options: ConnectOverCDPOptions): Promise<Browser>;
/**
* Option `wsEndpoint` is deprecated. Instead use `endpointURL`.
* @deprecated
*/
connectOverCDP(options: ConnectOptions): Promise<Browser>;
}
export interface CDPSession {