mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(api): make pipe connection the default, expose webSocket launch option (#562)
This commit is contained in:
parent
b4b81bae02
commit
056fbbd53e
22
docs/api.md
22
docs/api.md
@ -132,7 +132,7 @@ An example of launching a browser executable and connecting to a [Browser] later
|
|||||||
const playwright = require('playwright').webkit; // Or 'chromium' or 'firefox'.
|
const playwright = require('playwright').webkit; // Or 'chromium' or 'firefox'.
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browserApp = await playwright.launchBrowserApp();
|
const browserApp = await playwright.launchBrowserApp({ webSocket: true });
|
||||||
const connectOptions = browserApp.connectOptions();
|
const connectOptions = browserApp.connectOptions();
|
||||||
// Use connect options later to establish a connection.
|
// Use connect options later to establish a connection.
|
||||||
const browser = await playwright.connect(connectOptions);
|
const browser = await playwright.connect(connectOptions);
|
||||||
@ -229,7 +229,7 @@ Closes the browser gracefully and makes sure the process is terminated.
|
|||||||
|
|
||||||
#### browserApp.connectOptions()
|
#### browserApp.connectOptions()
|
||||||
- returns: <[Object]>
|
- returns: <[Object]>
|
||||||
- `browserWSEndpoint` <?[string]> a [browser websocket endpoint](#browserwsendpoint) to connect to.
|
- `browserWSEndpoint` <?[string]> a browser websocket endpoint to connect to.
|
||||||
- `slowMo` <[number]>
|
- `slowMo` <[number]>
|
||||||
- `transport` <[ConnectionTransport]> **Experimental** A custom transport object which should be used to connect.
|
- `transport` <[ConnectionTransport]> **Experimental** A custom transport object which should be used to connect.
|
||||||
|
|
||||||
@ -243,8 +243,6 @@ This options object can be passed to [chromiumPlaywright.connect(options)](#chro
|
|||||||
|
|
||||||
Browser websocket endpoint which can be used as an argument to [chromiumPlaywright.connect(options)](#chromiumplaywrightconnectoptions), [firefoxPlaywright.connect(options)](#firefoxplaywrightconnectoptions) or [webkitPlaywright.connect(options)](#webkitplaywrightconnectoptions) to establish connection to the browser.
|
Browser websocket endpoint which can be used as an argument to [chromiumPlaywright.connect(options)](#chromiumplaywrightconnectoptions), [firefoxPlaywright.connect(options)](#firefoxplaywrightconnectoptions) or [webkitPlaywright.connect(options)](#webkitplaywrightconnectoptions) to establish connection to the browser.
|
||||||
|
|
||||||
Learn more about [Chromium devtools protocol](https://chromedevtools.github.io/devtools-protocol) and the [browser endpoint](https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target).
|
|
||||||
|
|
||||||
### class: BrowserContext
|
### class: BrowserContext
|
||||||
|
|
||||||
* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
|
* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
|
||||||
@ -3294,7 +3292,7 @@ If the function passed to the `worker.evaluateHandle` returns a [Promise], then
|
|||||||
|
|
||||||
#### chromiumPlaywright.connect(options)
|
#### chromiumPlaywright.connect(options)
|
||||||
- `options` <[Object]>
|
- `options` <[Object]>
|
||||||
- `browserWSEndpoint` <?[string]> a [browser websocket endpoint](#browserwsendpoint) to connect to.
|
- `browserWSEndpoint` <?[string]> a browser websocket endpoint to connect to.
|
||||||
- `browserURL` <?[string]> a browser url to connect to, in format `http://${host}:${port}`. Use interchangeably with `browserWSEndpoint` to let Playwright fetch it from [metadata endpoint](https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target).
|
- `browserURL` <?[string]> a browser url to connect to, in format `http://${host}:${port}`. Use interchangeably with `browserWSEndpoint` to let Playwright fetch it from [metadata endpoint](https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target).
|
||||||
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||||
- `transport` <[ConnectionTransport]> **Experimental** Specify a custom transport object for Playwright to use.
|
- `transport` <[ConnectionTransport]> **Experimental** Specify a custom transport object for Playwright to use.
|
||||||
@ -3327,7 +3325,7 @@ The default flags that Chromium will be launched with.
|
|||||||
- `userDataDir` <[string]> Path to a [User Data Directory](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md).
|
- `userDataDir` <[string]> Path to a [User Data Directory](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md).
|
||||||
- `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
|
- `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
|
||||||
- `devtools` <[boolean]> Whether to auto-open a DevTools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
|
- `devtools` <[boolean]> Whether to auto-open a DevTools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
|
||||||
- `pipe` <[boolean]> Connects to the browser over a pipe instead of a WebSocket. Defaults to `false`.
|
- `webSocket` <[boolean]> Connects to the browser over a WebSocket instead of a pipe. Defaults to `false`.
|
||||||
- returns: <[Promise]<[ChromiumBrowser]>> Promise which resolves to browser instance.
|
- returns: <[Promise]<[ChromiumBrowser]>> Promise which resolves to browser instance.
|
||||||
|
|
||||||
|
|
||||||
@ -3361,7 +3359,7 @@ const browser = await playwright.launch({
|
|||||||
- `userDataDir` <[string]> Path to a [User Data Directory](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md).
|
- `userDataDir` <[string]> Path to a [User Data Directory](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md).
|
||||||
- `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
|
- `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
|
||||||
- `devtools` <[boolean]> Whether to auto-open a DevTools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
|
- `devtools` <[boolean]> Whether to auto-open a DevTools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
|
||||||
- `pipe` <[boolean]> Connects to the browser over a pipe instead of a WebSocket. Defaults to `false`.
|
- `webSocket` <[boolean]> Connects to the browser over a WebSocket instead of a pipe. Defaults to `false`.
|
||||||
- returns: <[Promise]<[BrowserApp]>> Promise which resolves to browser server instance.
|
- returns: <[Promise]<[BrowserApp]>> Promise which resolves to browser server instance.
|
||||||
|
|
||||||
### class: ChromiumBrowser
|
### class: ChromiumBrowser
|
||||||
@ -3554,7 +3552,7 @@ Identifies what kind of target this is. Can be `"page"`, [`"background_page"`](h
|
|||||||
|
|
||||||
#### firefoxPlaywright.connect(options)
|
#### firefoxPlaywright.connect(options)
|
||||||
- `options` <[Object]>
|
- `options` <[Object]>
|
||||||
- `browserWSEndpoint` <?[string]> a [browser websocket endpoint](#browserwsendpoint) to connect to.
|
- `browserWSEndpoint` <?[string]> a browser websocket endpoint to connect to.
|
||||||
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||||
- `transport` <[ConnectionTransport]> **Experimental** Specify a custom transport object for Playwright to use.
|
- `transport` <[ConnectionTransport]> **Experimental** Specify a custom transport object for Playwright to use.
|
||||||
- returns: <[Promise]<[FirefoxBrowser]>>
|
- returns: <[Promise]<[FirefoxBrowser]>>
|
||||||
@ -3584,6 +3582,7 @@ The default flags that Firefox will be launched with.
|
|||||||
- `dumpio` <[boolean]> Whether to pipe the browser process stdout and stderr into `process.stdout` and `process.stderr`. Defaults to `false`.
|
- `dumpio` <[boolean]> Whether to pipe the browser process stdout and stderr into `process.stdout` and `process.stderr`. Defaults to `false`.
|
||||||
- `userDataDir` <[string]> Path to a [User Data Directory](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile).
|
- `userDataDir` <[string]> Path to a [User Data Directory](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile).
|
||||||
- `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
|
- `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
|
||||||
|
- `webSocket` <[boolean]> Connects to the browser over a WebSocket instead of a pipe. Defaults to `false`.
|
||||||
- returns: <[Promise]<[FirefoxBrowser]>> Promise which resolves to browser instance.
|
- returns: <[Promise]<[FirefoxBrowser]>> Promise which resolves to browser instance.
|
||||||
|
|
||||||
|
|
||||||
@ -3608,6 +3607,7 @@ const browser = await playwright.launch({
|
|||||||
- `dumpio` <[boolean]> Whether to pipe the browser process stdout and stderr into `process.stdout` and `process.stderr`. Defaults to `false`.
|
- `dumpio` <[boolean]> Whether to pipe the browser process stdout and stderr into `process.stdout` and `process.stderr`. Defaults to `false`.
|
||||||
- `userDataDir` <[string]> Path to a [User Data Directory](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile).
|
- `userDataDir` <[string]> Path to a [User Data Directory](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile).
|
||||||
- `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
|
- `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
|
||||||
|
- `webSocket` <[boolean]> Connects to the browser over a WebSocket instead of a pipe. Defaults to `false`.
|
||||||
- returns: <[Promise]<[BrowserApp]>> Promise which resolves to browser server instance.
|
- returns: <[Promise]<[BrowserApp]>> Promise which resolves to browser server instance.
|
||||||
|
|
||||||
### class: FirefoxBrowser
|
### class: FirefoxBrowser
|
||||||
@ -3630,7 +3630,7 @@ Firefox browser instance does not expose Firefox-specific features.
|
|||||||
|
|
||||||
#### webkitPlaywright.connect(options)
|
#### webkitPlaywright.connect(options)
|
||||||
- `options` <[Object]>
|
- `options` <[Object]>
|
||||||
- `browserWSEndpoint` <?[string]> a [browser websocket endpoint](#browserwsendpoint) to connect to.
|
- `browserWSEndpoint` <?[string]> a browser websocket endpoint to connect to.
|
||||||
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||||
- `transport` <[ConnectionTransport]> **Experimental** Specify a custom transport object for Playwright to use.
|
- `transport` <[ConnectionTransport]> **Experimental** Specify a custom transport object for Playwright to use.
|
||||||
- returns: <[Promise]<[WebKitBrowser]>>
|
- returns: <[Promise]<[WebKitBrowser]>>
|
||||||
@ -3660,7 +3660,7 @@ The default flags that WebKit will be launched with.
|
|||||||
- `timeout` <[number]> Maximum time in milliseconds to wait for the browser instance to start. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.
|
- `timeout` <[number]> Maximum time in milliseconds to wait for the browser instance to start. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.
|
||||||
- `dumpio` <[boolean]> Whether to pipe the browser process stdout and stderr into `process.stdout` and `process.stderr`. Defaults to `false`.
|
- `dumpio` <[boolean]> Whether to pipe the browser process stdout and stderr into `process.stdout` and `process.stderr`. Defaults to `false`.
|
||||||
- `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
|
- `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
|
||||||
- `pipe` <[boolean]> Connects to the browser over a pipe instead of a WebSocket. Defaults to `false`.
|
- `webSocket` <[boolean]> Connects to the browser over a WebSocket instead of a pipe. Defaults to `false`.
|
||||||
- returns: <[Promise]<[WebKitBrowser]>> Promise which resolves to browser instance.
|
- returns: <[Promise]<[WebKitBrowser]>> Promise which resolves to browser instance.
|
||||||
|
|
||||||
|
|
||||||
@ -3685,7 +3685,7 @@ const browser = await playwright.launch({
|
|||||||
- `timeout` <[number]> Maximum time in milliseconds to wait for the browser instance to start. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.
|
- `timeout` <[number]> Maximum time in milliseconds to wait for the browser instance to start. Defaults to `30000` (30 seconds). Pass `0` to disable timeout.
|
||||||
- `dumpio` <[boolean]> Whether to pipe the browser process stdout and stderr into `process.stdout` and `process.stderr`. Defaults to `false`.
|
- `dumpio` <[boolean]> Whether to pipe the browser process stdout and stderr into `process.stdout` and `process.stderr`. Defaults to `false`.
|
||||||
- `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
|
- `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
|
||||||
- `pipe` <[boolean]> Connects to the browser over a pipe instead of a WebSocket. Defaults to `false`.
|
- `webSocket` <[boolean]> Connects to the browser over a WebSocket instead of a pipe. Defaults to `false`.
|
||||||
- returns: <[Promise]<[BrowserApp]>> Promise which resolves to browser server instance.
|
- returns: <[Promise]<[BrowserApp]>> Promise which resolves to browser server instance.
|
||||||
|
|
||||||
### class: WebKitBrowser
|
### class: WebKitBrowser
|
||||||
|
@ -26,6 +26,10 @@ export const ConnectionEvents = {
|
|||||||
Disconnected: Symbol('ConnectionEvents.Disconnected')
|
Disconnected: Symbol('ConnectionEvents.Disconnected')
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// CRPlaywright uses this special id to issue Browser.close command which we
|
||||||
|
// should ignore.
|
||||||
|
export const kBrowserCloseMessageId = -9999;
|
||||||
|
|
||||||
export class CRConnection extends platform.EventEmitter {
|
export class CRConnection extends platform.EventEmitter {
|
||||||
private _lastId = 0;
|
private _lastId = 0;
|
||||||
private _transport: ConnectionTransport;
|
private _transport: ConnectionTransport;
|
||||||
@ -64,6 +68,8 @@ export class CRConnection extends platform.EventEmitter {
|
|||||||
async _onMessage(message: string) {
|
async _onMessage(message: string) {
|
||||||
debugProtocol('◀ RECV ' + message);
|
debugProtocol('◀ RECV ' + message);
|
||||||
const object = JSON.parse(message);
|
const object = JSON.parse(message);
|
||||||
|
if (object.id === kBrowserCloseMessageId)
|
||||||
|
return;
|
||||||
if (object.method === 'Target.attachedToTarget') {
|
if (object.method === 'Target.attachedToTarget') {
|
||||||
const sessionId = object.params.sessionId;
|
const sessionId = object.params.sessionId;
|
||||||
const session = new CRSession(this, object.params.targetInfo.type, sessionId);
|
const session = new CRSession(this, object.params.targetInfo.type, sessionId);
|
||||||
|
@ -26,6 +26,10 @@ export const ConnectionEvents = {
|
|||||||
Disconnected: Symbol('Disconnected'),
|
Disconnected: Symbol('Disconnected'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// FFPlaywright uses this special id to issue Browser.close command which we
|
||||||
|
// should ignore.
|
||||||
|
export const kBrowserCloseMessageId = -9999;
|
||||||
|
|
||||||
export class FFConnection extends platform.EventEmitter {
|
export class FFConnection extends platform.EventEmitter {
|
||||||
private _lastId: number;
|
private _lastId: number;
|
||||||
private _callbacks: Map<number, {resolve: Function, reject: Function, error: Error, method: string}>;
|
private _callbacks: Map<number, {resolve: Function, reject: Function, error: Error, method: string}>;
|
||||||
@ -89,6 +93,8 @@ export class FFConnection extends platform.EventEmitter {
|
|||||||
async _onMessage(message: string) {
|
async _onMessage(message: string) {
|
||||||
debugProtocol('◀ RECV ' + message);
|
debugProtocol('◀ RECV ' + message);
|
||||||
const object = JSON.parse(message);
|
const object = JSON.parse(message);
|
||||||
|
if (object.id === kBrowserCloseMessageId)
|
||||||
|
return;
|
||||||
if (object.method === 'Target.attachedToTarget') {
|
if (object.method === 'Target.attachedToTarget') {
|
||||||
const sessionId = object.params.sessionId;
|
const sessionId = object.params.sessionId;
|
||||||
const session = new FFSession(this, object.params.targetInfo.type, sessionId, message => this._rawSend({...message, sessionId}));
|
const session = new FFSession(this, object.params.targetInfo.type, sessionId, message => this._rawSend({...message, sessionId}));
|
||||||
|
@ -27,7 +27,7 @@ import { CRBrowser } from '../chromium/crBrowser';
|
|||||||
import * as platform from '../platform';
|
import * as platform from '../platform';
|
||||||
import { TimeoutError } from '../errors';
|
import { TimeoutError } from '../errors';
|
||||||
import { launchProcess, waitForLine } from '../server/processLauncher';
|
import { launchProcess, waitForLine } from '../server/processLauncher';
|
||||||
import { CRConnection } from '../chromium/crConnection';
|
import { kBrowserCloseMessageId } from '../chromium/crConnection';
|
||||||
import { PipeTransport } from './pipeTransport';
|
import { PipeTransport } from './pipeTransport';
|
||||||
import { Playwright } from './playwright';
|
import { Playwright } from './playwright';
|
||||||
import { createTransport, ConnectOptions } from '../browser';
|
import { createTransport, ConnectOptions } from '../browser';
|
||||||
@ -53,7 +53,7 @@ export type LaunchOptions = ChromiumArgOptions & SlowMoOptions & {
|
|||||||
timeout?: number,
|
timeout?: number,
|
||||||
dumpio?: boolean,
|
dumpio?: boolean,
|
||||||
env?: {[key: string]: string} | undefined,
|
env?: {[key: string]: string} | undefined,
|
||||||
pipe?: boolean,
|
webSocket?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class CRPlaywright implements Playwright {
|
export class CRPlaywright implements Playwright {
|
||||||
@ -79,7 +79,7 @@ export class CRPlaywright implements Playwright {
|
|||||||
args = [],
|
args = [],
|
||||||
dumpio = false,
|
dumpio = false,
|
||||||
executablePath = null,
|
executablePath = null,
|
||||||
pipe = false,
|
webSocket = false,
|
||||||
env = process.env,
|
env = process.env,
|
||||||
handleSIGINT = true,
|
handleSIGINT = true,
|
||||||
handleSIGTERM = true,
|
handleSIGTERM = true,
|
||||||
@ -99,7 +99,7 @@ export class CRPlaywright implements Playwright {
|
|||||||
let temporaryUserDataDir: string | null = null;
|
let temporaryUserDataDir: string | null = null;
|
||||||
|
|
||||||
if (!chromeArguments.some(argument => argument.startsWith('--remote-debugging-')))
|
if (!chromeArguments.some(argument => argument.startsWith('--remote-debugging-')))
|
||||||
chromeArguments.push(pipe ? '--remote-debugging-pipe' : '--remote-debugging-port=0');
|
chromeArguments.push(webSocket ? '--remote-debugging-port=0' : '--remote-debugging-pipe');
|
||||||
if (!chromeArguments.some(arg => arg.startsWith('--user-data-dir'))) {
|
if (!chromeArguments.some(arg => arg.startsWith('--user-data-dir'))) {
|
||||||
temporaryUserDataDir = await mkdtempAsync(CHROMIUM_PROFILE_PATH);
|
temporaryUserDataDir = await mkdtempAsync(CHROMIUM_PROFILE_PATH);
|
||||||
chromeArguments.push(`--user-data-dir=${temporaryUserDataDir}`);
|
chromeArguments.push(`--user-data-dir=${temporaryUserDataDir}`);
|
||||||
@ -114,6 +114,8 @@ export class CRPlaywright implements Playwright {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const usePipe = chromeArguments.includes('--remote-debugging-pipe');
|
const usePipe = chromeArguments.includes('--remote-debugging-pipe');
|
||||||
|
if (usePipe && webSocket)
|
||||||
|
throw new Error(`Argument "--remote-debugging-pipe" is not compatible with "webSocket" launch option.`);
|
||||||
|
|
||||||
const { launchedProcess, gracefullyClose } = await launchProcess({
|
const { launchedProcess, gracefullyClose } = await launchProcess({
|
||||||
executablePath: chromeExecutable!,
|
executablePath: chromeExecutable!,
|
||||||
@ -131,10 +133,10 @@ export class CRPlaywright implements Playwright {
|
|||||||
|
|
||||||
// We try to gracefully close to prevent crash reporting and core dumps.
|
// We try to gracefully close to prevent crash reporting and core dumps.
|
||||||
// Note that it's fine to reuse the pipe transport, since
|
// Note that it's fine to reuse the pipe transport, since
|
||||||
// our connection is tolerant to unknown responses.
|
// our connection ignores kBrowserCloseMessageId.
|
||||||
const transport = await createTransport(connectOptions);
|
const transport = await createTransport(connectOptions);
|
||||||
const connection = new CRConnection(transport);
|
const message = { method: 'Browser.close', id: kBrowserCloseMessageId };
|
||||||
connection.rootSession.send('Browser.close');
|
transport.send(JSON.stringify(message));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -152,6 +154,8 @@ export class CRPlaywright implements Playwright {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async connect(options: ConnectOptions & { browserURL?: string }): Promise<CRBrowser> {
|
async connect(options: ConnectOptions & { browserURL?: string }): Promise<CRBrowser> {
|
||||||
|
if (options.transport && options.transport.onmessage)
|
||||||
|
throw new Error('Transport is already in use');
|
||||||
if (options.browserURL) {
|
if (options.browserURL) {
|
||||||
assert(!options.browserWSEndpoint && !options.transport, 'Exactly one of browserWSEndpoint, browserURL or transport must be passed to connect');
|
assert(!options.browserWSEndpoint && !options.transport, 'Exactly one of browserWSEndpoint, browserURL or transport must be passed to connect');
|
||||||
let connectionURL: string;
|
let connectionURL: string;
|
||||||
|
@ -21,7 +21,7 @@ import { DeviceDescriptors } from '../deviceDescriptors';
|
|||||||
import { launchProcess, waitForLine } from './processLauncher';
|
import { launchProcess, waitForLine } from './processLauncher';
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import * as platform from '../platform';
|
import * as platform from '../platform';
|
||||||
import { FFConnection } from '../firefox/ffConnection';
|
import { kBrowserCloseMessageId } from '../firefox/ffConnection';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
@ -51,6 +51,7 @@ export type LaunchOptions = FirefoxArgOptions & SlowMoOptions & {
|
|||||||
timeout?: number,
|
timeout?: number,
|
||||||
dumpio?: boolean,
|
dumpio?: boolean,
|
||||||
env?: {[key: string]: string} | undefined,
|
env?: {[key: string]: string} | undefined,
|
||||||
|
webSocket?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class FFPlaywright implements Playwright {
|
export class FFPlaywright implements Playwright {
|
||||||
@ -82,6 +83,7 @@ export class FFPlaywright implements Playwright {
|
|||||||
handleSIGTERM = true,
|
handleSIGTERM = true,
|
||||||
slowMo = 0,
|
slowMo = 0,
|
||||||
timeout = 30000,
|
timeout = 30000,
|
||||||
|
webSocket = false,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const firefoxArguments = [];
|
const firefoxArguments = [];
|
||||||
@ -129,22 +131,29 @@ export class FFPlaywright implements Playwright {
|
|||||||
if (!connectOptions)
|
if (!connectOptions)
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
// We try to gracefully close to prevent crash reporting and core dumps.
|
// We try to gracefully close to prevent crash reporting and core dumps.
|
||||||
// Note that we don't support pipe yet, so there is no issue
|
// Note that it's fine to reuse the pipe transport, since
|
||||||
// with reusing the same connection - we can always create a new one.
|
// our connection ignores kBrowserCloseMessageId.
|
||||||
const transport = await createTransport(connectOptions);
|
const transport = await createTransport(connectOptions);
|
||||||
const connection = new FFConnection(transport);
|
const message = { method: 'Browser.close', params: {}, id: kBrowserCloseMessageId };
|
||||||
connection.send('Browser.close');
|
transport.send(JSON.stringify(message));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const timeoutError = new TimeoutError(`Timed out after ${timeout} ms while trying to connect to Firefox!`);
|
const timeoutError = new TimeoutError(`Timed out after ${timeout} ms while trying to connect to Firefox!`);
|
||||||
const match = await waitForLine(launchedProcess, launchedProcess.stdout, /^Juggler listening on (ws:\/\/.*)$/, timeout, timeoutError);
|
const match = await waitForLine(launchedProcess, launchedProcess.stdout, /^Juggler listening on (ws:\/\/.*)$/, timeout, timeoutError);
|
||||||
const url = match[1];
|
const browserWSEndpoint = match[1];
|
||||||
connectOptions = { browserWSEndpoint: url, slowMo };
|
if (webSocket) {
|
||||||
|
connectOptions = { browserWSEndpoint, slowMo };
|
||||||
|
} else {
|
||||||
|
const transport = await platform.createWebSocketTransport(browserWSEndpoint);
|
||||||
|
connectOptions = { transport, slowMo };
|
||||||
|
}
|
||||||
return new BrowserApp(launchedProcess, gracefullyClose, connectOptions);
|
return new BrowserApp(launchedProcess, gracefullyClose, connectOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(options: ConnectOptions): Promise<FFBrowser> {
|
async connect(options: ConnectOptions): Promise<FFBrowser> {
|
||||||
|
if (options.transport && options.transport.onmessage)
|
||||||
|
throw new Error('Transport is already in use');
|
||||||
return FFBrowser.connect(options);
|
return FFBrowser.connect(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ export type LaunchOptions = WebKitArgOptions & SlowMoOptions & {
|
|||||||
timeout?: number,
|
timeout?: number,
|
||||||
dumpio?: boolean,
|
dumpio?: boolean,
|
||||||
env?: {[key: string]: string} | undefined,
|
env?: {[key: string]: string} | undefined,
|
||||||
pipe?: boolean,
|
webSocket?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class WKPlaywright implements Playwright {
|
export class WKPlaywright implements Playwright {
|
||||||
@ -87,7 +87,7 @@ export class WKPlaywright implements Playwright {
|
|||||||
handleSIGTERM = true,
|
handleSIGTERM = true,
|
||||||
handleSIGHUP = true,
|
handleSIGHUP = true,
|
||||||
slowMo = 0,
|
slowMo = 0,
|
||||||
pipe = false,
|
webSocket = false,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const webkitArguments = [];
|
const webkitArguments = [];
|
||||||
@ -132,6 +132,8 @@ export class WKPlaywright implements Playwright {
|
|||||||
if (!transport)
|
if (!transport)
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
// We try to gracefully close to prevent crash reporting and core dumps.
|
// We try to gracefully close to prevent crash reporting and core dumps.
|
||||||
|
// Note that it's fine to reuse the pipe transport, since
|
||||||
|
// our connection ignores kBrowserCloseMessageId.
|
||||||
const message = JSON.stringify({method: 'Browser.close', params: {}, id: kBrowserCloseMessageId});
|
const message = JSON.stringify({method: 'Browser.close', params: {}, id: kBrowserCloseMessageId});
|
||||||
transport.send(message);
|
transport.send(message);
|
||||||
},
|
},
|
||||||
@ -140,7 +142,7 @@ export class WKPlaywright implements Playwright {
|
|||||||
transport = new PipeTransport(launchedProcess.stdio[3] as NodeJS.WritableStream, launchedProcess.stdio[4] as NodeJS.ReadableStream);
|
transport = new PipeTransport(launchedProcess.stdio[3] as NodeJS.WritableStream, launchedProcess.stdio[4] as NodeJS.ReadableStream);
|
||||||
|
|
||||||
let connectOptions: ConnectOptions;
|
let connectOptions: ConnectOptions;
|
||||||
if (!pipe) {
|
if (webSocket) {
|
||||||
const browserWSEndpoint = wrapTransportWithWebSocket(transport);
|
const browserWSEndpoint = wrapTransportWithWebSocket(transport);
|
||||||
connectOptions = { browserWSEndpoint, slowMo };
|
connectOptions = { browserWSEndpoint, slowMo };
|
||||||
} else {
|
} else {
|
||||||
@ -150,6 +152,8 @@ export class WKPlaywright implements Playwright {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async connect(options: ConnectOptions): Promise<WKBrowser> {
|
async connect(options: ConnectOptions): Promise<WKBrowser> {
|
||||||
|
if (options.transport && options.transport.onmessage)
|
||||||
|
throw new Error('Transport is already in use');
|
||||||
return WKBrowser.connect(options);
|
return WKBrowser.connect(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
const options = Object.assign({}, defaultBrowserOptions, {
|
const options = Object.assign({}, defaultBrowserOptions, {
|
||||||
// Disable DUMPIO to cleanly read stdout.
|
// Disable DUMPIO to cleanly read stdout.
|
||||||
dumpio: false,
|
dumpio: false,
|
||||||
|
webSocket: true,
|
||||||
});
|
});
|
||||||
const res = spawn('node', [path.join(__dirname, '..', 'fixtures', 'closeme.js'), playwrightPath, product, JSON.stringify(options)]);
|
const res = spawn('node', [path.join(__dirname, '..', 'fixtures', 'closeme.js'), playwrightPath, product, JSON.stringify(options)]);
|
||||||
let wsEndPointCallback;
|
let wsEndPointCallback;
|
||||||
|
@ -21,20 +21,6 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
|
|||||||
const {it, fit, xit, dit} = testRunner;
|
const {it, fit, xit, dit} = testRunner;
|
||||||
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
||||||
|
|
||||||
describe('Chromium', function() {
|
|
||||||
it('should work across sessions', async function({browserApp, server, browser, newContext}) {
|
|
||||||
expect(browser.browserContexts().length).toBe(2);
|
|
||||||
await newContext();
|
|
||||||
expect(browser.browserContexts().length).toBe(3);
|
|
||||||
const remoteBrowser = await playwright.connect({
|
|
||||||
browserWSEndpoint: browserApp.wsEndpoint()
|
|
||||||
});
|
|
||||||
const contexts = remoteBrowser.browserContexts();
|
|
||||||
expect(contexts.length).toBe(3);
|
|
||||||
remoteBrowser.disconnect();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Target', function() {
|
describe('Target', function() {
|
||||||
it('Chromium.targets should return all of the targets', async({page, server, browser}) => {
|
it('Chromium.targets should return all of the targets', async({page, server, browser}) => {
|
||||||
// The pages will be the testing page and the original newtab page
|
// The pages will be the testing page and the original newtab page
|
||||||
|
@ -24,12 +24,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
|
|
||||||
describe('Playwright.connect', function() {
|
describe('Playwright.connect', function() {
|
||||||
it('should be able to connect multiple times to the same browser', async({server}) => {
|
it('should be able to connect multiple times to the same browser', async({server}) => {
|
||||||
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
const local = await playwright.connect(browserApp.connectOptions());
|
const local = await playwright.connect(browserApp.connectOptions());
|
||||||
const remote = await playwright.connect({
|
const remote = await playwright.connect(browserApp.connectOptions());
|
||||||
...defaultBrowserOptions,
|
|
||||||
browserWSEndpoint: browserApp.wsEndpoint()
|
|
||||||
});
|
|
||||||
const page = await remote.defaultContext().newPage();
|
const page = await remote.defaultContext().newPage();
|
||||||
expect(await page.evaluate(() => 7 * 8)).toBe(56);
|
expect(await page.evaluate(() => 7 * 8)).toBe(56);
|
||||||
remote.disconnect();
|
remote.disconnect();
|
||||||
@ -38,13 +35,21 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
expect(await secondPage.evaluate(() => 7 * 6)).toBe(42, 'original browser should still work');
|
expect(await secondPage.evaluate(() => 7 * 6)).toBe(42, 'original browser should still work');
|
||||||
await browserApp.close();
|
await browserApp.close();
|
||||||
});
|
});
|
||||||
it('should be able to close remote browser', async({server}) => {
|
it('should not be able to connect multiple times without websocket', async({server}) => {
|
||||||
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
||||||
|
const connectOptions = browserApp.connectOptions();
|
||||||
|
expect(connectOptions.transport).toBeTruthy();
|
||||||
|
expect(browserApp.wsEndpoint()).toBe(null);
|
||||||
|
expect(connectOptions.browserWSEndpoint).toBe(undefined);
|
||||||
|
const local = await playwright.connect(connectOptions);
|
||||||
|
const error = await playwright.connect(connectOptions).catch(e => e);
|
||||||
|
expect(error.message).toBe('Transport is already in use');
|
||||||
|
await browserApp.close();
|
||||||
|
});
|
||||||
|
it('should be able to close remote browser', async({server}) => {
|
||||||
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
const local = await playwright.connect(browserApp.connectOptions());
|
const local = await playwright.connect(browserApp.connectOptions());
|
||||||
const remote = await playwright.connect({
|
const remote = await playwright.connect(browserApp.connectOptions());
|
||||||
...defaultBrowserOptions,
|
|
||||||
browserWSEndpoint: browserApp.wsEndpoint()
|
|
||||||
});
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
utils.waitEvent(local, 'disconnected'),
|
utils.waitEvent(local, 'disconnected'),
|
||||||
remote.close(),
|
remote.close(),
|
||||||
@ -52,9 +57,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
});
|
});
|
||||||
// @see https://github.com/GoogleChrome/puppeteer/issues/4197#issuecomment-481793410
|
// @see https://github.com/GoogleChrome/puppeteer/issues/4197#issuecomment-481793410
|
||||||
it('should be able to connect to the same page simultaneously', async({server}) => {
|
it('should be able to connect to the same page simultaneously', async({server}) => {
|
||||||
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
const local = await playwright.connect(browserApp.connectOptions());
|
const local = await playwright.connect(browserApp.connectOptions());
|
||||||
const remote = await playwright.connect({ ...defaultBrowserOptions, browserWSEndpoint: browserApp.wsEndpoint() });
|
const remote = await playwright.connect(browserApp.connectOptions());
|
||||||
const [page1, page2] = await Promise.all([
|
const [page1, page2] = await Promise.all([
|
||||||
new Promise(x => local.once('targetcreated', target => x(target.page()))),
|
new Promise(x => local.once('targetcreated', target => x(target.page()))),
|
||||||
remote.defaultContext().newPage(),
|
remote.defaultContext().newPage(),
|
||||||
|
@ -32,12 +32,28 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
||||||
|
|
||||||
describe('CrPlaywright', function() {
|
describe('CrPlaywright', function() {
|
||||||
|
describe('BrowserContext', function() {
|
||||||
|
it('should work across sessions', async () => {
|
||||||
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
|
const browser = await playwright.connect(browserApp.connectOptions());
|
||||||
|
expect(browser.browserContexts().length).toBe(1);
|
||||||
|
await browser.newContext();
|
||||||
|
expect(browser.browserContexts().length).toBe(2);
|
||||||
|
const remoteBrowser = await playwright.connect(browserApp.connectOptions());
|
||||||
|
const contexts = remoteBrowser.browserContexts();
|
||||||
|
expect(contexts.length).toBe(2);
|
||||||
|
await browserApp.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
describe('Playwright.launch |browserURL| option', function() {
|
describe('Playwright.launch |browserURL| option', function() {
|
||||||
|
function getBrowserUrl(wsEndpoint) {
|
||||||
|
const port = wsEndpoint.match(/ws:\/\/([0-9A-Za-z\.]*):(\d+)\//)[2];
|
||||||
|
return `http://127.0.0.1:${port}`;
|
||||||
|
}
|
||||||
|
|
||||||
it('should be able to connect using browserUrl, with and without trailing slash', async({server}) => {
|
it('should be able to connect using browserUrl, with and without trailing slash', async({server}) => {
|
||||||
const originalBrowser = await playwright.launch(Object.assign({}, defaultBrowserOptions, {
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
args: ['--remote-debugging-port=21222']
|
const browserURL = getBrowserUrl(browserApp.wsEndpoint());
|
||||||
}));
|
|
||||||
const browserURL = 'http://127.0.0.1:21222';
|
|
||||||
|
|
||||||
const browser1 = await playwright.connect({browserURL});
|
const browser1 = await playwright.connect({browserURL});
|
||||||
const page1 = await browser1.defaultContext().newPage();
|
const page1 = await browser1.defaultContext().newPage();
|
||||||
@ -48,46 +64,42 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
const page2 = await browser2.defaultContext().newPage();
|
const page2 = await browser2.defaultContext().newPage();
|
||||||
expect(await page2.evaluate(() => 8 * 7)).toBe(56);
|
expect(await page2.evaluate(() => 8 * 7)).toBe(56);
|
||||||
browser2.disconnect();
|
browser2.disconnect();
|
||||||
originalBrowser.close();
|
await browserApp.close();
|
||||||
});
|
});
|
||||||
it('should throw when using both browserWSEndpoint and browserURL', async({server}) => {
|
it('should throw when using both browserWSEndpoint and browserURL', async({server}) => {
|
||||||
const browserApp = await playwright.launchBrowserApp(Object.assign({}, defaultBrowserOptions, {
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
args: ['--remote-debugging-port=21222']
|
const browserURL = getBrowserUrl(browserApp.wsEndpoint());
|
||||||
}));
|
|
||||||
const browserURL = 'http://127.0.0.1:21222';
|
|
||||||
|
|
||||||
let error = null;
|
let error = null;
|
||||||
await playwright.connect({browserURL, browserWSEndpoint: browserApp.wsEndpoint()}).catch(e => error = e);
|
await playwright.connect({browserURL, browserWSEndpoint: browserApp.wsEndpoint()}).catch(e => error = e);
|
||||||
expect(error.message).toContain('Exactly one of browserWSEndpoint, browserURL or transport');
|
expect(error.message).toContain('Exactly one of browserWSEndpoint, browserURL or transport');
|
||||||
|
|
||||||
browserApp.close();
|
await browserApp.close();
|
||||||
});
|
});
|
||||||
it('should throw when trying to connect to non-existing browser', async({server}) => {
|
it('should throw when trying to connect to non-existing browser', async({server}) => {
|
||||||
const originalBrowser = await playwright.launch(Object.assign({}, defaultBrowserOptions, {
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
args: ['--remote-debugging-port=21222']
|
const browserURL = getBrowserUrl(browserApp.wsEndpoint());
|
||||||
}));
|
|
||||||
const browserURL = 'http://127.0.0.1:32333';
|
|
||||||
|
|
||||||
let error = null;
|
let error = null;
|
||||||
await playwright.connect({browserURL}).catch(e => error = e);
|
await playwright.connect({browserURL: browserURL + 'foo'}).catch(e => error = e);
|
||||||
expect(error.message).toContain('Failed to fetch browser webSocket url from');
|
expect(error.message).toContain('Failed to fetch browser webSocket url from');
|
||||||
originalBrowser.close();
|
await browserApp.close();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Playwright.launch |pipe| option', function() {
|
describe('Playwright.launch webSocket option', function() {
|
||||||
it('should support the pipe option', async() => {
|
it('should support the remote-debugging-port argument', async() => {
|
||||||
const options = Object.assign({pipe: true}, defaultBrowserOptions);
|
const options = Object.assign({}, defaultBrowserOptions);
|
||||||
|
options.args = ['--remote-debugging-port=0'].concat(options.args || []);
|
||||||
const browserApp = await playwright.launchBrowserApp(options);
|
const browserApp = await playwright.launchBrowserApp(options);
|
||||||
const browser = await playwright.connect(browserApp.connectOptions());
|
const browser = await playwright.connect(browserApp.connectOptions());
|
||||||
expect((await browser.defaultContext().pages()).length).toBe(1);
|
expect(browserApp.wsEndpoint()).not.toBe(null);
|
||||||
expect(browserApp.wsEndpoint()).toBe(null);
|
|
||||||
const page = await browser.defaultContext().newPage();
|
const page = await browser.defaultContext().newPage();
|
||||||
expect(await page.evaluate('11 * 11')).toBe(121);
|
expect(await page.evaluate('11 * 11')).toBe(121);
|
||||||
await page.close();
|
await page.close();
|
||||||
await browserApp.close();
|
await browserApp.close();
|
||||||
});
|
});
|
||||||
it('should support the pipe argument', async() => {
|
it('should support the remote-debugging-pipe argument', async() => {
|
||||||
const options = Object.assign({}, defaultBrowserOptions);
|
const options = Object.assign({}, defaultBrowserOptions);
|
||||||
options.args = ['--remote-debugging-pipe'].concat(options.args || []);
|
options.args = ['--remote-debugging-pipe'].concat(options.args || []);
|
||||||
const browserApp = await playwright.launchBrowserApp(options);
|
const browserApp = await playwright.launchBrowserApp(options);
|
||||||
@ -98,14 +110,11 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
await page.close();
|
await page.close();
|
||||||
await browserApp.close();
|
await browserApp.close();
|
||||||
});
|
});
|
||||||
it('should fire "disconnected" when closing with pipe', async() => {
|
it('should throw with remote-debugging-pipe argument and webSocket', async() => {
|
||||||
const options = Object.assign({pipe: true}, defaultBrowserOptions);
|
const options = Object.assign({webSocket: true}, defaultBrowserOptions);
|
||||||
const browserApp = await playwright.launchBrowserApp(options);
|
options.args = ['--remote-debugging-pipe'].concat(options.args || []);
|
||||||
const browser = await playwright.connect(browserApp.connectOptions());
|
const error = await playwright.launchBrowserApp(options).catch(e => e);
|
||||||
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
|
expect(error.message).toBe('Argument "--remote-debugging-pipe" is not compatible with "webSocket" launch option.');
|
||||||
// Emulate user exiting browser.
|
|
||||||
browserApp.process().kill();
|
|
||||||
await disconnectedEventPromise;
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -127,7 +136,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
|
|
||||||
describe('Browser.Events.disconnected', function() {
|
describe('Browser.Events.disconnected', function() {
|
||||||
it('should be emitted when: browser gets closed, disconnected or underlying websocket gets closed', async() => {
|
it('should be emitted when: browser gets closed, disconnected or underlying websocket gets closed', async() => {
|
||||||
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
const originalBrowser = await playwright.connect(browserApp.connectOptions());
|
const originalBrowser = await playwright.connect(browserApp.connectOptions());
|
||||||
const browserWSEndpoint = browserApp.wsEndpoint();
|
const browserWSEndpoint = browserApp.wsEndpoint();
|
||||||
const remoteBrowser1 = await playwright.connect({browserWSEndpoint});
|
const remoteBrowser1 = await playwright.connect({browserWSEndpoint});
|
||||||
|
@ -27,6 +27,7 @@ module.exports.describe = function({testRunner, defaultBrowserOptions, playwrigh
|
|||||||
const options = Object.assign({}, defaultBrowserOptions, {
|
const options = Object.assign({}, defaultBrowserOptions, {
|
||||||
// Disable DUMPIO to cleanly read stdout.
|
// Disable DUMPIO to cleanly read stdout.
|
||||||
dumpio: false,
|
dumpio: false,
|
||||||
|
webSocket: true,
|
||||||
});
|
});
|
||||||
const res = spawn('node', [path.join(__dirname, '..', 'fixtures', 'closeme.js'), playwrightPath, product, JSON.stringify(options)]);
|
const res = spawn('node', [path.join(__dirname, '..', 'fixtures', 'closeme.js'), playwrightPath, product, JSON.stringify(options)]);
|
||||||
let wsEndPointCallback;
|
let wsEndPointCallback;
|
||||||
|
@ -24,9 +24,9 @@ module.exports.describe = function({testRunner, expect, product, playwrightPath,
|
|||||||
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
||||||
|
|
||||||
describe('Fixtures', function() {
|
describe('Fixtures', function() {
|
||||||
it('dumpio option should work with pipe option ', async({server}) => {
|
it('dumpio option should work with webSocket option', async({server}) => {
|
||||||
let dumpioData = '';
|
let dumpioData = '';
|
||||||
const res = spawn('node', [path.join(__dirname, 'fixtures', 'dumpio.js'), playwrightPath, product, 'use-pipe']);
|
const res = spawn('node', [path.join(__dirname, 'fixtures', 'dumpio.js'), playwrightPath, product, 'usewebsocket']);
|
||||||
res.stderr.on('data', data => dumpioData += data.toString('utf8'));
|
res.stderr.on('data', data => dumpioData += data.toString('utf8'));
|
||||||
await new Promise(resolve => res.on('close', resolve));
|
await new Promise(resolve => res.on('close', resolve));
|
||||||
expect(dumpioData).toContain('message from dumpio');
|
expect(dumpioData).toContain('message from dumpio');
|
||||||
|
6
test/fixtures/dumpio.js
vendored
6
test/fixtures/dumpio.js
vendored
@ -4,16 +4,16 @@
|
|||||||
console.log('unhandledRejection', error.message);
|
console.log('unhandledRejection', error.message);
|
||||||
});
|
});
|
||||||
|
|
||||||
const [, , playwrightRoot, product, usePipe] = process.argv;
|
const [, , playwrightRoot, product, useWebSocket] = process.argv;
|
||||||
const options = {
|
const options = {
|
||||||
pipe: usePipe === 'use-pipe',
|
webSocket: useWebSocket === 'usewebsocket',
|
||||||
ignoreDefaultArgs: true,
|
ignoreDefaultArgs: true,
|
||||||
dumpio: true,
|
dumpio: true,
|
||||||
timeout: 1,
|
timeout: 1,
|
||||||
executablePath: 'node',
|
executablePath: 'node',
|
||||||
args: ['-e', 'console.error("message from dumpio")', '--']
|
args: ['-e', 'console.error("message from dumpio")', '--']
|
||||||
}
|
}
|
||||||
console.error('using pipe: ' + options.pipe);
|
console.error('using web socket: ' + options.webSocket);
|
||||||
if (product.toLowerCase() === 'firefox')
|
if (product.toLowerCase() === 'firefox')
|
||||||
options.args.push('-juggler', '-profile');
|
options.args.push('-juggler', '-profile');
|
||||||
try {
|
try {
|
||||||
|
@ -90,17 +90,16 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
|
|
||||||
describe('Browser.isConnected', () => {
|
describe('Browser.isConnected', () => {
|
||||||
it('should set the browser connected state', async () => {
|
it('should set the browser connected state', async () => {
|
||||||
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
const browserWSEndpoint = browserApp.wsEndpoint();
|
const remote = await playwright.connect({browserWSEndpoint: browserApp.wsEndpoint()});
|
||||||
const remote = await playwright.connect({browserWSEndpoint});
|
|
||||||
expect(remote.isConnected()).toBe(true);
|
expect(remote.isConnected()).toBe(true);
|
||||||
await remote.disconnect();
|
await remote.disconnect();
|
||||||
expect(remote.isConnected()).toBe(false);
|
expect(remote.isConnected()).toBe(false);
|
||||||
await browserApp.close();
|
await browserApp.close();
|
||||||
});
|
});
|
||||||
it('should throw when used after isConnected returns false', async({server}) => {
|
it('should throw when used after isConnected returns false', async({server}) => {
|
||||||
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserApp.wsEndpoint()});
|
const remote = await playwright.connect(browserApp.connectOptions());
|
||||||
const page = await remote.defaultContext().newPage();
|
const page = await remote.defaultContext().newPage();
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
browserApp.close(),
|
browserApp.close(),
|
||||||
@ -115,8 +114,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
describe('Browser.disconnect', function() {
|
describe('Browser.disconnect', function() {
|
||||||
it('should reject navigation when browser closes', async({server}) => {
|
it('should reject navigation when browser closes', async({server}) => {
|
||||||
server.setRoute('/one-style.css', () => {});
|
server.setRoute('/one-style.css', () => {});
|
||||||
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserApp.wsEndpoint()});
|
const remote = await playwright.connect(browserApp.connectOptions());
|
||||||
const page = await remote.defaultContext().newPage();
|
const page = await remote.defaultContext().newPage();
|
||||||
const navigationPromise = page.goto(server.PREFIX + '/one-style.html', {timeout: 60000}).catch(e => e);
|
const navigationPromise = page.goto(server.PREFIX + '/one-style.html', {timeout: 60000}).catch(e => e);
|
||||||
await server.waitForRequest('/one-style.css');
|
await server.waitForRequest('/one-style.css');
|
||||||
@ -127,8 +126,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
});
|
});
|
||||||
it('should reject waitForSelector when browser closes', async({server}) => {
|
it('should reject waitForSelector when browser closes', async({server}) => {
|
||||||
server.setRoute('/empty.html', () => {});
|
server.setRoute('/empty.html', () => {});
|
||||||
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserApp.wsEndpoint()});
|
const remote = await playwright.connect(browserApp.connectOptions());
|
||||||
const page = await remote.defaultContext().newPage();
|
const page = await remote.defaultContext().newPage();
|
||||||
const watchdog = page.waitForSelector('div', { timeout: 60000 }).catch(e => e);
|
const watchdog = page.waitForSelector('div', { timeout: 60000 }).catch(e => e);
|
||||||
await remote.disconnect();
|
await remote.disconnect();
|
||||||
@ -137,8 +136,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
await browserApp.close();
|
await browserApp.close();
|
||||||
});
|
});
|
||||||
it('should throw if used after disconnect', async({server}) => {
|
it('should throw if used after disconnect', async({server}) => {
|
||||||
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserApp.wsEndpoint()});
|
const remote = await playwright.connect(browserApp.connectOptions());
|
||||||
const page = await remote.defaultContext().newPage();
|
const page = await remote.defaultContext().newPage();
|
||||||
await remote.disconnect();
|
await remote.disconnect();
|
||||||
const error = await page.evaluate('1 + 1').catch(e => e);
|
const error = await page.evaluate('1 + 1').catch(e => e);
|
||||||
@ -149,8 +148,8 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
|
|
||||||
describe('Browser.close', function() {
|
describe('Browser.close', function() {
|
||||||
it('should terminate network waiters', async({context, server}) => {
|
it('should terminate network waiters', async({context, server}) => {
|
||||||
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserApp.wsEndpoint()});
|
const remote = await playwright.connect(browserApp.connectOptions());
|
||||||
const newPage = await remote.defaultContext().newPage();
|
const newPage = await remote.defaultContext().newPage();
|
||||||
const results = await Promise.all([
|
const results = await Promise.all([
|
||||||
newPage.waitForRequest(server.EMPTY_PAGE).catch(e => e),
|
newPage.waitForRequest(server.EMPTY_PAGE).catch(e => e),
|
||||||
@ -165,16 +164,56 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Playwright.connect', function() {
|
describe('Playwright.launch |webSocket| option', function() {
|
||||||
it.skip(WEBKIT)('should be able to reconnect to a browser', async({server}) => {
|
it('should not have websocket by default', async() => {
|
||||||
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
||||||
const browser = await playwright.connect(browserApp.connectOptions());
|
const browser = await playwright.connect(browserApp.connectOptions());
|
||||||
const browserWSEndpoint = browserApp.wsEndpoint();
|
expect((await browser.defaultContext().pages()).length).toBe(1);
|
||||||
|
expect(browserApp.wsEndpoint()).toBe(null);
|
||||||
|
const page = await browser.defaultContext().newPage();
|
||||||
|
expect(await page.evaluate('11 * 11')).toBe(121);
|
||||||
|
await page.close();
|
||||||
|
await browserApp.close();
|
||||||
|
});
|
||||||
|
it('should support the webSocket option', async() => {
|
||||||
|
const options = Object.assign({webSocket: true}, defaultBrowserOptions);
|
||||||
|
const browserApp = await playwright.launchBrowserApp(options);
|
||||||
|
const browser = await playwright.connect(browserApp.connectOptions());
|
||||||
|
expect((await browser.defaultContext().pages()).length).toBe(1);
|
||||||
|
expect(browserApp.wsEndpoint()).not.toBe(null);
|
||||||
|
const page = await browser.defaultContext().newPage();
|
||||||
|
expect(await page.evaluate('11 * 11')).toBe(121);
|
||||||
|
await page.close();
|
||||||
|
await browserApp.close();
|
||||||
|
});
|
||||||
|
it('should fire "disconnected" when closing without webSocket', async() => {
|
||||||
|
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
||||||
|
const browser = await playwright.connect(browserApp.connectOptions());
|
||||||
|
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
|
||||||
|
// Emulate user exiting browser.
|
||||||
|
process.kill(-browserApp.process().pid, 'SIGKILL');
|
||||||
|
await disconnectedEventPromise;
|
||||||
|
});
|
||||||
|
it('should fire "disconnected" when closing with webSocket', async() => {
|
||||||
|
const options = Object.assign({webSocket: true}, defaultBrowserOptions);
|
||||||
|
const browserApp = await playwright.launchBrowserApp(options);
|
||||||
|
const browser = await playwright.connect(browserApp.connectOptions());
|
||||||
|
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
|
||||||
|
// Emulate user exiting browser.
|
||||||
|
process.kill(-browserApp.process().pid, 'SIGKILL');
|
||||||
|
await disconnectedEventPromise;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Playwright.connect', function() {
|
||||||
|
it.skip(WEBKIT)('should be able to reconnect to a browser', async({server}) => {
|
||||||
|
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions, webSocket: true});
|
||||||
|
const browser = await playwright.connect(browserApp.connectOptions());
|
||||||
const page = await browser.defaultContext().newPage();
|
const page = await browser.defaultContext().newPage();
|
||||||
await page.goto(server.PREFIX + '/frames/nested-frames.html');
|
await page.goto(server.PREFIX + '/frames/nested-frames.html');
|
||||||
await browser.disconnect();
|
await browser.disconnect();
|
||||||
|
|
||||||
const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint});
|
const remote = await playwright.connect(browserApp.connectOptions());
|
||||||
const pages = await remote.defaultContext().pages();
|
const pages = await remote.defaultContext().pages();
|
||||||
const restoredPage = pages.find(page => page.url() === server.PREFIX + '/frames/nested-frames.html');
|
const restoredPage = pages.find(page => page.url() === server.PREFIX + '/frames/nested-frames.html');
|
||||||
expect(utils.dumpFrames(restoredPage.mainFrame())).toEqual([
|
expect(utils.dumpFrames(restoredPage.mainFrame())).toEqual([
|
||||||
@ -189,7 +228,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.skip(FFOX | WEBKIT)('Playwright.launch({userDataDir})', function() {
|
describe.skip(FFOX || WEBKIT)('Playwright.launch({userDataDir})', function() {
|
||||||
it('userDataDir option', async({server}) => {
|
it('userDataDir option', async({server}) => {
|
||||||
const userDataDir = await mkdtempAsync(TMP_FOLDER);
|
const userDataDir = await mkdtempAsync(TMP_FOLDER);
|
||||||
const options = Object.assign({userDataDir}, defaultBrowserOptions);
|
const options = Object.assign({userDataDir}, defaultBrowserOptions);
|
||||||
|
@ -105,7 +105,7 @@ module.exports.describe = ({testRunner, product, playwrightPath}) => {
|
|||||||
const onLine = (line) => test.output += line + '\n';
|
const onLine = (line) => test.output += line + '\n';
|
||||||
|
|
||||||
let rl;
|
let rl;
|
||||||
if (!WEBKIT) {
|
if (state.browserApp.process().stderr) {
|
||||||
rl = require('readline').createInterface({ input: state.browserApp.process().stderr });
|
rl = require('readline').createInterface({ input: state.browserApp.process().stderr });
|
||||||
test.output = '';
|
test.output = '';
|
||||||
rl.on('line', onLine);
|
rl.on('line', onLine);
|
||||||
@ -113,7 +113,7 @@ module.exports.describe = ({testRunner, product, playwrightPath}) => {
|
|||||||
|
|
||||||
state.tearDown = async () => {
|
state.tearDown = async () => {
|
||||||
await Promise.all(contexts.map(c => c.close()));
|
await Promise.all(contexts.map(c => c.close()));
|
||||||
if (!WEBKIT) {
|
if (rl) {
|
||||||
rl.removeListener('line', onLine);
|
rl.removeListener('line', onLine);
|
||||||
rl.close();
|
rl.close();
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
|||||||
|
|
||||||
(CHROMIUM || FFOX) && describe('Web SDK', function() {
|
(CHROMIUM || FFOX) && describe('Web SDK', function() {
|
||||||
beforeAll(async state => {
|
beforeAll(async state => {
|
||||||
state.controlledBrowserApp = await playwright.launchBrowserApp({ ...defaultBrowserOptions, pipe: false });
|
state.controlledBrowserApp = await playwright.launchBrowserApp({ ...defaultBrowserOptions, webSocket: true });
|
||||||
state.hostBrowserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
state.hostBrowserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
||||||
state.hostBrowser = await playwright.connect(state.hostBrowserApp.connectOptions());
|
state.hostBrowser = await playwright.connect(state.hostBrowserApp.connectOptions());
|
||||||
});
|
});
|
||||||
|
@ -21,47 +21,6 @@ module.exports.describe = function ({ testRunner, expect, playwright, defaultBro
|
|||||||
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
||||||
|
|
||||||
describe('WKPlaywright', function() {
|
describe('WKPlaywright', function() {
|
||||||
describe('Playwright.launch |pipe| option', function() {
|
|
||||||
it('should have websocket by default', async() => {
|
|
||||||
const options = Object.assign({pipe: false}, defaultBrowserOptions);
|
|
||||||
const browserApp = await playwright.launchBrowserApp(options);
|
|
||||||
const browser = await playwright.connect(browserApp.connectOptions());
|
|
||||||
expect((await browser.defaultContext().pages()).length).toBe(1);
|
|
||||||
expect(browserApp.wsEndpoint()).not.toBe(null);
|
|
||||||
const page = await browser.defaultContext().newPage();
|
|
||||||
expect(await page.evaluate('11 * 11')).toBe(121);
|
|
||||||
await page.close();
|
|
||||||
await browserApp.close();
|
|
||||||
});
|
|
||||||
it('should support the pipe option', async() => {
|
|
||||||
const options = Object.assign({pipe: true}, defaultBrowserOptions);
|
|
||||||
const browserApp = await playwright.launchBrowserApp(options);
|
|
||||||
const browser = await playwright.connect(browserApp.connectOptions());
|
|
||||||
expect((await browser.defaultContext().pages()).length).toBe(1);
|
|
||||||
expect(browserApp.wsEndpoint()).toBe(null);
|
|
||||||
const page = await browser.defaultContext().newPage();
|
|
||||||
expect(await page.evaluate('11 * 11')).toBe(121);
|
|
||||||
await page.close();
|
|
||||||
await browserApp.close();
|
|
||||||
});
|
|
||||||
it('should fire "disconnected" when closing with pipe', async() => {
|
|
||||||
const options = Object.assign({pipe: true}, defaultBrowserOptions);
|
|
||||||
const browserApp = await playwright.launchBrowserApp(options);
|
|
||||||
const browser = await playwright.connect(browserApp.connectOptions());
|
|
||||||
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
|
|
||||||
// Emulate user exiting browser.
|
|
||||||
process.kill(-browserApp.process().pid, 'SIGKILL');
|
|
||||||
await disconnectedEventPromise;
|
|
||||||
});
|
|
||||||
it('should fire "disconnected" when closing with websocket', async() => {
|
|
||||||
const options = Object.assign({pipe: false}, defaultBrowserOptions);
|
|
||||||
const browserApp = await playwright.launchBrowserApp(options);
|
|
||||||
const browser = await playwright.connect(browserApp.connectOptions());
|
|
||||||
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
|
|
||||||
// Emulate user exiting browser.
|
|
||||||
process.kill(-browserApp.process().pid, 'SIGKILL');
|
|
||||||
await disconnectedEventPromise;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -10,7 +10,7 @@ async function generateChromiunProtocol(revision) {
|
|||||||
if (revision.local && fs.existsSync(outputPath))
|
if (revision.local && fs.existsSync(outputPath))
|
||||||
return;
|
return;
|
||||||
const playwright = await require('../../index').chromium;
|
const playwright = await require('../../index').chromium;
|
||||||
const browserApp = await playwright.launchBrowserApp({executablePath: revision.executablePath});
|
const browserApp = await playwright.launchBrowserApp({executablePath: revision.executablePath, webSocket: true});
|
||||||
const origin = browserApp.wsEndpoint().match(/ws:\/\/([0-9A-Za-z:\.]*)\//)[1];
|
const origin = browserApp.wsEndpoint().match(/ws:\/\/([0-9A-Za-z:\.]*)\//)[1];
|
||||||
const browser = await playwright.connect(browserApp.connectOptions());
|
const browser = await playwright.connect(browserApp.connectOptions());
|
||||||
const page = await browser.defaultContext().newPage();
|
const page = await browser.defaultContext().newPage();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user