fix(api): BrowserServer -> BrowserApp, resuse it between browsers (#599)

This commit is contained in:
Dmitry Gozman 2020-01-23 14:40:37 -08:00 committed by GitHub
parent b4209e9dc8
commit ac2ba3cbd9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 263 additions and 391 deletions

View file

@ -7,6 +7,7 @@
<!-- GEN:toc-top-level --> <!-- GEN:toc-top-level -->
- [class: Playwright](#class-playwright) - [class: Playwright](#class-playwright)
- [class: Browser](#class-browser) - [class: Browser](#class-browser)
- [class: BrowserApp](#class-browserapp)
- [class: BrowserContext](#class-browsercontext) - [class: BrowserContext](#class-browsercontext)
- [class: ConsoleMessage](#class-consolemessage) - [class: ConsoleMessage](#class-consolemessage)
- [class: Dialog](#class-dialog) - [class: Dialog](#class-dialog)
@ -24,15 +25,12 @@
- [class: Worker](#class-worker) - [class: Worker](#class-worker)
- [class: ChromiumPlaywright](#class-chromiumplaywright) - [class: ChromiumPlaywright](#class-chromiumplaywright)
- [class: ChromiumBrowser](#class-chromiumbrowser) - [class: ChromiumBrowser](#class-chromiumbrowser)
- [class: ChromiumBrowserServer](#class-chromiumbrowserserver)
- [class: ChromiumSession](#class-chromiumsession) - [class: ChromiumSession](#class-chromiumsession)
- [class: ChromiumTarget](#class-chromiumtarget) - [class: ChromiumTarget](#class-chromiumtarget)
- [class: FirefoxPlaywright](#class-firefoxplaywright) - [class: FirefoxPlaywright](#class-firefoxplaywright)
- [class: FirefoxBrowser](#class-firefoxbrowser) - [class: FirefoxBrowser](#class-firefoxbrowser)
- [class: FirefoxBrowserServer](#class-firefoxbrowserserver)
- [class: WebKitPlaywright](#class-webkitplaywright) - [class: WebKitPlaywright](#class-webkitplaywright)
- [class: WebKitBrowser](#class-webkitbrowser) - [class: WebKitBrowser](#class-webkitbrowser)
- [class: WebKitBrowserServer](#class-webkitbrowserserver)
- [Working with selectors](#working-with-selectors) - [Working with selectors](#working-with-selectors)
- [Working with Chrome Extensions](#working-with-chrome-extensions) - [Working with Chrome Extensions](#working-with-chrome-extensions)
- [Downloaded browsers](#downloaded-browsers) - [Downloaded browsers](#downloaded-browsers)
@ -128,17 +126,17 @@ const playwright = require('playwright').firefox; // Or 'chromium' or 'webkit'.
})(); })();
``` ```
An example of disconnecting from and reconnecting to a [Browser]: An example of launching a browser executable and connecting to a [Browser] later:
```js ```js
const playwright = require('playwright').webkit; // Or 'chromium' or 'firefox'. const playwright = require('playwright').webkit; // Or 'chromium' or 'firefox'.
(async () => { (async () => {
const browserServer = await playwright.launchServer(); const browserApp = await playwright.launchBrowserApp();
const browserWSEndpoint = browserServer.wsEndpoint(); const connectOptions = browserApp.connectOptions();
// Use the endpoint to establish a connection // Use connect options later to establish a connection.
const browser = await playwright.connect({browserWSEndpoint}); const browser = await playwright.connect(connectOptions);
// Close Chromium // Close browser instance.
await browser.close(); await browserApp.close();
})(); })();
``` ```
<!-- GEN:toc --> <!-- GEN:toc -->
@ -214,6 +212,38 @@ Creates a new browser context. It won't share cookies/cache with other browser c
})(); })();
``` ```
### class: BrowserApp
<!-- GEN:toc -->
- [browserApp.close()](#browserappclose)
- [browserApp.connectOptions()](#browserappconnectoptions)
- [browserApp.process()](#browserappprocess)
- [browserApp.wsEndpoint()](#browserappwsendpoint)
<!-- GEN:stop -->
#### browserApp.close()
- returns: <[Promise]>
Closes the browser gracefully and makes sure the process is terminated.
#### browserApp.connectOptions()
- returns: <[Object]>
- `browserWSEndpoint` <?[string]> a [browser websocket endpoint](#browserwsendpoint) to connect to.
- `slowMo` <[number]>
- `transport` <[ConnectionTransport]> **Experimental** A custom transport object which should be used to connect.
This options object can be passed to [chromiumPlaywright.connect(options)](#chromiumplaywrightconnectoptions), [firefoxPlaywright.connect(options)](#firefoxplaywrightconnectoptions) or [webkitPlaywright.connect(options)](#webkitplaywrightconnectoptions) to establish connection to the browser.
#### browserApp.process()
- returns: <?[ChildProcess]> Spawned browser server process.
#### browserApp.wsEndpoint()
- returns: <?[string]> Browser websocket url.
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)
@ -3191,7 +3221,7 @@ If the function passed to the `worker.evaluateHandle` returns a [Promise], then
- [chromiumPlaywright.connect(options)](#chromiumplaywrightconnectoptions) - [chromiumPlaywright.connect(options)](#chromiumplaywrightconnectoptions)
- [chromiumPlaywright.defaultArgs([options])](#chromiumplaywrightdefaultargsoptions) - [chromiumPlaywright.defaultArgs([options])](#chromiumplaywrightdefaultargsoptions)
- [chromiumPlaywright.launch([options])](#chromiumplaywrightlaunchoptions) - [chromiumPlaywright.launch([options])](#chromiumplaywrightlaunchoptions)
- [chromiumPlaywright.launchServer([options])](#chromiumplaywrightlaunchserveroptions) - [chromiumPlaywright.launchBrowserApp([options])](#chromiumplaywrightlaunchbrowserappoptions)
<!-- GEN:stop --> <!-- GEN:stop -->
#### chromiumPlaywright.connect(options) #### chromiumPlaywright.connect(options)
@ -3248,7 +3278,7 @@ const browser = await playwright.launch({
> >
> See [`this article`](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for a description of the differences between Chromium and Chrome. [`This article`](https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users. > See [`this article`](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for a description of the differences between Chromium and Chrome. [`This article`](https://chromium.googlesource.com/chromium/src/+/lkgr/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users.
#### chromiumPlaywright.launchServer([options]) #### chromiumPlaywright.launchBrowserApp([options])
- `options` <[Object]> Set of configurable options to set on the browser. Can have the following fields: - `options` <[Object]> Set of configurable options to set on the browser. Can have the following fields:
- `headless` <[boolean]> Whether to run Chromium in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). Defaults to `true` unless the `devtools` option is `true`. - `headless` <[boolean]> Whether to run Chromium in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). Defaults to `true` unless the `devtools` option is `true`.
- `executablePath` <[string]> Path to a Chromium or Chrome executable to run instead of the bundled Chromium. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only [guaranteed to work](https://github.com/Microsoft/playwright/#q-why-doesnt-playwright-vxxx-work-with-chromium-vyyy) with the bundled Chromium, use at your own risk. - `executablePath` <[string]> Path to a Chromium or Chrome executable to run instead of the bundled Chromium. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only [guaranteed to work](https://github.com/Microsoft/playwright/#q-why-doesnt-playwright-vxxx-work-with-chromium-vyyy) with the bundled Chromium, use at your own risk.
@ -3264,7 +3294,7 @@ const browser = await playwright.launch({
- `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`. - `pipe` <[boolean]> Connects to the browser over a pipe instead of a WebSocket. Defaults to `false`.
- returns: <[Promise]<[ChromiumBrowserServer]>> Promise which resolves to browser server instance. - returns: <[Promise]<[BrowserApp]>> Promise which resolves to browser server instance.
### class: ChromiumBrowser ### class: ChromiumBrowser
@ -3363,39 +3393,6 @@ await page.evaluate(() => window.open('https://www.example.com/'));
const newWindowTarget = await browser.chromium.waitForTarget(target => target.url() === 'https://www.example.com/'); const newWindowTarget = await browser.chromium.waitForTarget(target => target.url() === 'https://www.example.com/');
``` ```
### class: ChromiumBrowserServer
<!-- GEN:toc -->
- [chromiumBrowserServer.close()](#chromiumbrowserserverclose)
- [chromiumBrowserServer.connectOptions()](#chromiumbrowserserverconnectoptions)
- [chromiumBrowserServer.process()](#chromiumbrowserserverprocess)
- [chromiumBrowserServer.wsEndpoint()](#chromiumbrowserserverwsendpoint)
<!-- GEN:stop -->
#### chromiumBrowserServer.close()
- returns: <[Promise]>
Closes the browser gracefully and makes sure the process is terminated.
#### chromiumBrowserServer.connectOptions()
- returns: <[Object]>
- `browserWSEndpoint` <?[string]> a [browser websocket endpoint](#browserwsendpoint) 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).
- `slowMo` <[number]>
- `transport` <[ConnectionTransport]> **Experimental** A custom transport object which should be used to connect.
This options object can be passed to [chromiumPlaywright.connect(options)](#chromiumplaywrightconnectoptions) to establish connection to the browser.
#### chromiumBrowserServer.process()
- returns: <?[ChildProcess]> Spawned browser server process.
#### chromiumBrowserServer.wsEndpoint()
- returns: <?[string]> Browser websocket url.
Browser websocket endpoint which can be used as an argument to [chromiumPlaywright.connect(options)](#chromiumplaywrightconnectoptions).
Learn more about the [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: ChromiumSession ### class: ChromiumSession
* extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter) * extends: [EventEmitter](https://nodejs.org/api/events.html#events_class_eventemitter)
@ -3484,7 +3481,7 @@ Identifies what kind of target this is. Can be `"page"`, [`"background_page"`](h
- [firefoxPlaywright.connect(options)](#firefoxplaywrightconnectoptions) - [firefoxPlaywright.connect(options)](#firefoxplaywrightconnectoptions)
- [firefoxPlaywright.defaultArgs([options])](#firefoxplaywrightdefaultargsoptions) - [firefoxPlaywright.defaultArgs([options])](#firefoxplaywrightdefaultargsoptions)
- [firefoxPlaywright.launch([options])](#firefoxplaywrightlaunchoptions) - [firefoxPlaywright.launch([options])](#firefoxplaywrightlaunchoptions)
- [firefoxPlaywright.launchServer([options])](#firefoxplaywrightlaunchserveroptions) - [firefoxPlaywright.launchBrowserApp([options])](#firefoxplaywrightlaunchbrowserappoptions)
<!-- GEN:stop --> <!-- GEN:stop -->
#### firefoxPlaywright.connect(options) #### firefoxPlaywright.connect(options)
@ -3529,7 +3526,7 @@ const browser = await playwright.launch({
}); });
``` ```
#### firefoxPlaywright.launchServer([options]) #### firefoxPlaywright.launchBrowserApp([options])
- `options` <[Object]> Set of configurable options to set on the browser. Can have the following fields: - `options` <[Object]> Set of configurable options to set on the browser. Can have the following fields:
- `headless` <[boolean]> Whether to run Firefox in [headless mode](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true`. - `headless` <[boolean]> Whether to run Firefox in [headless mode](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true`.
- `executablePath` <[string]> Path to a Firefox executable to run instead of the bundled Firefox. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only guaranteed to work with the bundled Firefox, use at your own risk. - `executablePath` <[string]> Path to a Firefox executable to run instead of the bundled Firefox. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only guaranteed to work with the bundled Firefox, use at your own risk.
@ -3543,7 +3540,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`.
- returns: <[Promise]<[FirefoxBrowserServer]>> Promise which resolves to browser server instance. - returns: <[Promise]<[BrowserApp]>> Promise which resolves to browser server instance.
### class: FirefoxBrowser ### class: FirefoxBrowser
@ -3551,35 +3548,6 @@ const browser = await playwright.launch({
Firefox browser instance does not expose Firefox-specific features. Firefox browser instance does not expose Firefox-specific features.
### class: FirefoxBrowserServer
<!-- GEN:toc -->
- [firefoxBrowserServer.close()](#firefoxbrowserserverclose)
- [firefoxBrowserServer.connectOptions()](#firefoxbrowserserverconnectoptions)
- [firefoxBrowserServer.process()](#firefoxbrowserserverprocess)
- [firefoxBrowserServer.wsEndpoint()](#firefoxbrowserserverwsendpoint)
<!-- GEN:stop -->
#### firefoxBrowserServer.close()
- returns: <[Promise]>
Closes the browser gracefully and makes sure the process is terminated.
#### firefoxBrowserServer.connectOptions()
- returns: <[Object]>
- `browserWSEndpoint` <?[string]> a [browser websocket endpoint](#browserwsendpoint) to connect to.
- `slowMo` <[number]>
- `transport` <[ConnectionTransport]> **Experimental** A custom transport object which should be used to connect.
This options object can be passed to [firefoxPlaywright.connect(options)](#firefoxplaywrightconnectoptions) to establish connection to the browser.
#### firefoxBrowserServer.process()
- returns: <?[ChildProcess]> Spawned browser server process.
#### firefoxBrowserServer.wsEndpoint()
- returns: <?[string]> Browser websocket url.
Browser websocket endpoint which can be used as an argument to [firefoxPlaywright.connect(options)](#firefoxplaywrightconnectoptions).
### class: WebKitPlaywright ### class: WebKitPlaywright
@ -3589,7 +3557,7 @@ Browser websocket endpoint which can be used as an argument to [firefoxPlaywrigh
- [webkitPlaywright.connect(options)](#webkitplaywrightconnectoptions) - [webkitPlaywright.connect(options)](#webkitplaywrightconnectoptions)
- [webkitPlaywright.defaultArgs([options])](#webkitplaywrightdefaultargsoptions) - [webkitPlaywright.defaultArgs([options])](#webkitplaywrightdefaultargsoptions)
- [webkitPlaywright.launch([options])](#webkitplaywrightlaunchoptions) - [webkitPlaywright.launch([options])](#webkitplaywrightlaunchoptions)
- [webkitPlaywright.launchServer([options])](#webkitplaywrightlaunchserveroptions) - [webkitPlaywright.launchBrowserApp([options])](#webkitplaywrightlaunchbrowserappoptions)
<!-- GEN:stop --> <!-- GEN:stop -->
#### webkitPlaywright.connect(options) #### webkitPlaywright.connect(options)
@ -3635,7 +3603,7 @@ const browser = await playwright.launch({
}); });
``` ```
#### webkitPlaywright.launchServer([options]) #### webkitPlaywright.launchBrowserApp([options])
- `options` <[Object]> Set of configurable options to set on the browser. Can have the following fields: - `options` <[Object]> Set of configurable options to set on the browser. Can have the following fields:
- `headless` <[boolean]> Whether to run WebKit in headless mode. Defaults to `true`. - `headless` <[boolean]> Whether to run WebKit in headless mode. Defaults to `true`.
- `executablePath` <[string]> Path to a WebKit executable to run instead of the bundled WebKit. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only guaranteed to work with the bundled WebKit, use at your own risk. - `executablePath` <[string]> Path to a WebKit executable to run instead of the bundled WebKit. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only guaranteed to work with the bundled WebKit, use at your own risk.
@ -3650,7 +3618,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`.
- `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`. - `pipe` <[boolean]> Connects to the browser over a pipe instead of a WebSocket. Defaults to `false`.
- returns: <[Promise]<[WebKitBrowserServer]>> Promise which resolves to browser server instance. - returns: <[Promise]<[BrowserApp]>> Promise which resolves to browser server instance.
### class: WebKitBrowser ### class: WebKitBrowser
@ -3658,35 +3626,6 @@ const browser = await playwright.launch({
WebKit browser instance does not expose WebKit-specific features. WebKit browser instance does not expose WebKit-specific features.
### class: WebKitBrowserServer
<!-- GEN:toc -->
- [webKitBrowserServer.close()](#webkitbrowserserverclose)
- [webKitBrowserServer.connectOptions()](#webkitbrowserserverconnectoptions)
- [webKitBrowserServer.process()](#webkitbrowserserverprocess)
- [webKitBrowserServer.wsEndpoint()](#webkitbrowserserverwsendpoint)
<!-- GEN:stop -->
#### webKitBrowserServer.close()
- returns: <[Promise]>
Closes the browser gracefully and makes sure the process is terminated.
#### webKitBrowserServer.connectOptions()
- returns: <[Object]>
- `slowMo` <[number]>
- `transport` <[ConnectionTransport]> **Experimental** A custom transport object which should be used to connect.
This options object can be passed to [webKitPlaywright.connect(options)](#webkitplaywrightconnectoptions) to establish connection to the browser.
#### webKitBrowserServer.process()
- returns: <?[ChildProcess]> Spawned browser server process.
#### webKitBrowserServer.wsEndpoint()
- returns: <?[string]> Browser websocket url.
Browser websocket endpoint which can be used as an argument to [webkitPlaywright.connect(options)](#webkitplaywrightconnectoptions).
### Working with selectors ### Working with selectors
Selector describes an element in the page. It can be used to obtain `ElementHandle` (see [page.$()](#pageselector) for example) or shortcut element operations to avoid intermediate handle (see [page.click()](#pageclickselector-options) for example). Selector describes an element in the page. It can be used to obtain `ElementHandle` (see [page.$()](#pageselector) for example) or shortcut element operations to avoid intermediate handle (see [page.click()](#pageclickselector-options) for example).
@ -3768,12 +3707,12 @@ During installation Playwright downloads browser executables, according to revis
[Accessibility]: #class-accessibility "Accessibility" [Accessibility]: #class-accessibility "Accessibility"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array" [Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Body]: #class-body "Body" [Body]: #class-body "Body"
[BrowserApp]: #class-browserapp "BrowserApp"
[BrowserContext]: #class-browsercontext "BrowserContext" [BrowserContext]: #class-browsercontext "BrowserContext"
[Browser]: #class-browser "Browser" [Browser]: #class-browser "Browser"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer" [Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess" [ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[ChromiumBrowser]: #class-chromiumbrowser "ChromiumBrowser" [ChromiumBrowser]: #class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserServer]: #class-chromiumbrowserserver "ChromiumBrowserServer"
[ChromiumPlaywright]: #class-chromiumplaywright "ChromiumPlaywright" [ChromiumPlaywright]: #class-chromiumplaywright "ChromiumPlaywright"
[ChromiumSession]: #class-chromiumsession "ChromiumSession" [ChromiumSession]: #class-chromiumsession "ChromiumSession"
[ChromiumTarget]: #class-chromiumtarget "ChromiumTarget" [ChromiumTarget]: #class-chromiumtarget "ChromiumTarget"
@ -3787,7 +3726,6 @@ During installation Playwright downloads browser executables, according to revis
[File]: #class-file "https://developer.mozilla.org/en-US/docs/Web/API/File" [File]: #class-file "https://developer.mozilla.org/en-US/docs/Web/API/File"
[FileChooser]: #class-filechooser "FileChooser" [FileChooser]: #class-filechooser "FileChooser"
[FirefoxBrowser]: #class-firefoxbrowser "FirefoxBrowser" [FirefoxBrowser]: #class-firefoxbrowser "FirefoxBrowser"
[FirefoxBrowserServer]: #class-firefoxbrowserserver "FirefoxBrowserServer"
[FirefoxPlaywright]: #class-firefoxplaywright "FirefoxPlaywright" [FirefoxPlaywright]: #class-firefoxplaywright "FirefoxPlaywright"
[Frame]: #class-frame "Frame" [Frame]: #class-frame "Frame"
[JSHandle]: #class-jshandle "JSHandle" [JSHandle]: #class-jshandle "JSHandle"
@ -3809,7 +3747,6 @@ During installation Playwright downloads browser executables, according to revis
[USKeyboardLayout]: ../lib/USKeyboardLayout.js "USKeyboardLayout" [USKeyboardLayout]: ../lib/USKeyboardLayout.js "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time" [UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[WebKitBrowser]: #class-webkitbrowser "WebKitBrowser" [WebKitBrowser]: #class-webkitbrowser "WebKitBrowser"
[WebKitBrowserServer]: #class-webkitbrowserserver "WebKitBrowserServer"
[WebKitPlaywright]: #class-webkitplaywright "WebKitPlaywright" [WebKitPlaywright]: #class-webkitplaywright "WebKitPlaywright"
[Worker]: #class-worker "Worker" [Worker]: #class-worker "Worker"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean" [boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"

View file

@ -36,6 +36,7 @@ export { FFBrowser as FirefoxBrowser } from './firefox/ffBrowser';
export { WKBrowser as WebKitBrowser } from './webkit/wkBrowser'; export { WKBrowser as WebKitBrowser } from './webkit/wkBrowser';
export { Playwright } from './server/playwright'; export { Playwright } from './server/playwright';
export { CRPlaywright as ChromiumPlaywright, CRBrowserServer as ChromiumBrowserServer } from './server/crPlaywright'; export { BrowserApp } from './server/browserApp';
export { FFPlaywright as FirefoxPlaywright, FFBrowserServer as FirefoxBrowserServer } from './server/ffPlaywright'; export { CRPlaywright as ChromiumPlaywright } from './server/crPlaywright';
export { WKPlaywright as WebKitPlaywright, WKBrowserServer as WebKitBrowserServer } from './server/wkPlaywright'; export { FFPlaywright as FirefoxPlaywright } from './server/ffPlaywright';
export { WKPlaywright as WebKitPlaywright } from './server/wkPlaywright';

View file

@ -15,7 +15,9 @@
*/ */
import { BrowserContext, BrowserContextOptions } from './browserContext'; import { BrowserContext, BrowserContextOptions } from './browserContext';
import { ConnectionTransport, SlowMoTransport } from './transport';
import * as platform from './platform'; import * as platform from './platform';
import { assert } from './helper';
export interface Browser extends platform.EventEmitterType { export interface Browser extends platform.EventEmitterType {
newContext(options?: BrowserContextOptions): Promise<BrowserContext>; newContext(options?: BrowserContextOptions): Promise<BrowserContext>;
@ -26,3 +28,19 @@ export interface Browser extends platform.EventEmitterType {
isConnected(): boolean; isConnected(): boolean;
close(): Promise<void>; close(): Promise<void>;
} }
export type ConnectOptions = {
slowMo?: number,
browserWSEndpoint?: string;
transport?: ConnectionTransport;
};
export async function createTransport(options: ConnectOptions): Promise<ConnectionTransport> {
assert(Number(!!options.browserWSEndpoint) + Number(!!options.transport) === 1, 'Exactly one of browserWSEndpoint or transport must be passed to connect');
let transport: ConnectionTransport | undefined;
if (options.transport)
transport = options.transport;
else if (options.browserWSEndpoint)
transport = await platform.createWebSocketTransport(options.browserWSEndpoint);
return SlowMoTransport.wrap(transport!, options.slowMo);
}

View file

@ -24,20 +24,12 @@ import { Page, Worker } from '../page';
import { CRTarget } from './crTarget'; import { CRTarget } from './crTarget';
import { Protocol } from './protocol'; import { Protocol } from './protocol';
import { CRPage } from './crPage'; import { CRPage } from './crPage';
import { Browser } from '../browser'; import { Browser, createTransport, ConnectOptions } from '../browser';
import * as network from '../network'; import * as network from '../network';
import * as types from '../types'; import * as types from '../types';
import * as platform from '../platform'; import * as platform from '../platform';
import { ConnectionTransport, SlowMoTransport } from '../transport';
import { readProtocolStream } from './crProtocolHelper'; import { readProtocolStream } from './crProtocolHelper';
export type CRConnectOptions = {
slowMo?: number,
browserWSEndpoint?: string;
browserURL?: string;
transport?: ConnectionTransport;
};
export class CRBrowser extends platform.EventEmitter implements Browser { export class CRBrowser extends platform.EventEmitter implements Browser {
_connection: CRConnection; _connection: CRConnection;
_client: CRSession; _client: CRSession;
@ -49,7 +41,7 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
private _tracingPath: string | null = ''; private _tracingPath: string | null = '';
private _tracingClient: CRSession | undefined; private _tracingClient: CRSession | undefined;
static async connect(options: CRConnectOptions): Promise<CRBrowser> { static async connect(options: ConnectOptions): Promise<CRBrowser> {
const transport = await createTransport(options); const transport = await createTransport(options);
const connection = new CRConnection(transport); const connection = new CRConnection(transport);
const { browserContextIds } = await connection.rootSession.send('Target.getBrowserContexts'); const { browserContextIds } = await connection.rootSession.send('Target.getBrowserContexts');
@ -309,24 +301,3 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
return !this._connection._closed; return !this._connection._closed;
} }
} }
export async function createTransport(options: CRConnectOptions): Promise<ConnectionTransport> {
assert(Number(!!options.browserWSEndpoint) + Number(!!options.browserURL) + Number(!!options.transport) === 1, 'Exactly one of browserWSEndpoint, browserURL or transport must be passed to connect');
let transport: ConnectionTransport | undefined;
if (options.transport) {
transport = options.transport;
} else if (options.browserWSEndpoint) {
transport = await platform.createWebSocketTransport(options.browserWSEndpoint);
} else if (options.browserURL) {
let connectionURL: string;
try {
const data = await platform.fetchUrl(new URL('/json/version', options.browserURL).href);
connectionURL = JSON.parse(data).webSocketDebuggerUrl;
} catch (e) {
e.message = `Failed to fetch browser webSocket url from ${options.browserURL}: ` + e.message;
throw e;
}
transport = await platform.createWebSocketTransport(connectionURL);
}
return SlowMoTransport.wrap(transport!, options.slowMo);
}

View file

@ -15,25 +15,18 @@
* limitations under the License. * limitations under the License.
*/ */
import { Browser } from '../browser'; import { Browser, createTransport, ConnectOptions } from '../browser';
import { BrowserContext, BrowserContextOptions } from '../browserContext'; import { BrowserContext, BrowserContextOptions } from '../browserContext';
import { Events } from '../events'; import { Events } from '../events';
import { assert, helper, RegisteredListener } from '../helper'; import { assert, helper, RegisteredListener } from '../helper';
import * as network from '../network'; import * as network from '../network';
import * as types from '../types'; import * as types from '../types';
import { Page } from '../page'; import { Page } from '../page';
import { ConnectionTransport, SlowMoTransport } from '../transport';
import { ConnectionEvents, FFConnection, FFSessionEvents } from './ffConnection'; import { ConnectionEvents, FFConnection, FFSessionEvents } from './ffConnection';
import { FFPage } from './ffPage'; import { FFPage } from './ffPage';
import * as platform from '../platform'; import * as platform from '../platform';
import { Protocol } from './protocol'; import { Protocol } from './protocol';
export type FFConnectOptions = {
slowMo?: number,
browserWSEndpoint?: string;
transport?: ConnectionTransport;
};
export class FFBrowser extends platform.EventEmitter implements Browser { export class FFBrowser extends platform.EventEmitter implements Browser {
_connection: FFConnection; _connection: FFConnection;
_targets: Map<string, Target>; _targets: Map<string, Target>;
@ -41,7 +34,7 @@ export class FFBrowser extends platform.EventEmitter implements Browser {
private _contexts: Map<string, BrowserContext>; private _contexts: Map<string, BrowserContext>;
private _eventListeners: RegisteredListener[]; private _eventListeners: RegisteredListener[];
static async connect(options: FFConnectOptions): Promise<FFBrowser> { static async connect(options: ConnectOptions): Promise<FFBrowser> {
const transport = await createTransport(options); const transport = await createTransport(options);
const connection = new FFConnection(transport); const connection = new FFConnection(transport);
const {browserContextIds} = await connection.send('Target.getBrowserContexts'); const {browserContextIds} = await connection.send('Target.getBrowserContexts');
@ -292,13 +285,3 @@ class Target {
return this._browser; return this._browser;
} }
} }
export async function createTransport(options: FFConnectOptions): Promise<ConnectionTransport> {
assert(Number(!!options.browserWSEndpoint) + Number(!!options.transport) === 1, 'Exactly one of browserWSEndpoint or transport must be passed to connect');
let transport: ConnectionTransport | undefined;
if (options.transport)
transport = options.transport;
else if (options.browserWSEndpoint)
transport = await platform.createWebSocketTransport(options.browserWSEndpoint);
return SlowMoTransport.wrap(transport!, options.slowMo);
}

46
src/server/browserApp.ts Normal file
View file

@ -0,0 +1,46 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ChildProcess } from 'child_process';
import { ConnectOptions } from '../browser';
export class BrowserApp {
private _process: ChildProcess;
private _gracefullyClose: () => Promise<void>;
private _connectOptions: ConnectOptions;
constructor(process: ChildProcess, gracefullyClose: () => Promise<void>, connectOptions: ConnectOptions) {
this._process = process;
this._gracefullyClose = gracefullyClose;
this._connectOptions = connectOptions;
}
process(): ChildProcess {
return this._process;
}
wsEndpoint(): string | null {
return this._connectOptions.browserWSEndpoint || null;
}
connectOptions(): ConnectOptions {
return this._connectOptions;
}
async close(): Promise<void> {
await this._gracefullyClose();
}
}

View file

@ -23,14 +23,15 @@ import { BrowserFetcher, BrowserFetcherOptions } from '../server/browserFetcher'
import { DeviceDescriptors } from '../deviceDescriptors'; import { DeviceDescriptors } from '../deviceDescriptors';
import * as types from '../types'; import * as types from '../types';
import { assert } from '../helper'; import { assert } from '../helper';
import { CRBrowser, CRConnectOptions, createTransport } from '../chromium/crBrowser'; 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 { ChildProcess } from 'child_process';
import { CRConnection } from '../chromium/crConnection'; import { CRConnection } 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 { BrowserApp } from './browserApp';
export type SlowMoOptions = { export type SlowMoOptions = {
slowMo?: number, slowMo?: number,
@ -55,34 +56,6 @@ export type LaunchOptions = ChromiumArgOptions & SlowMoOptions & {
pipe?: boolean, pipe?: boolean,
}; };
export class CRBrowserServer {
private _process: ChildProcess;
private _gracefullyClose: () => Promise<void>;
private _connectOptions: CRConnectOptions;
constructor(process: ChildProcess, gracefullyClose: () => Promise<void>, connectOptions: CRConnectOptions) {
this._process = process;
this._gracefullyClose = gracefullyClose;
this._connectOptions = connectOptions;
}
process(): ChildProcess {
return this._process;
}
wsEndpoint(): string | null {
return this._connectOptions.browserWSEndpoint || null;
}
connectOptions(): CRConnectOptions {
return this._connectOptions;
}
async close(): Promise<void> {
await this._gracefullyClose();
}
}
export class CRPlaywright implements Playwright { export class CRPlaywright implements Playwright {
private _projectRoot: string; private _projectRoot: string;
readonly _revision: string; readonly _revision: string;
@ -93,14 +66,14 @@ export class CRPlaywright implements Playwright {
} }
async launch(options?: LaunchOptions): Promise<CRBrowser> { async launch(options?: LaunchOptions): Promise<CRBrowser> {
const server = await this.launchServer(options); const app = await this.launchBrowserApp(options);
const browser = await CRBrowser.connect(server.connectOptions()); const browser = await CRBrowser.connect(app.connectOptions());
// Hack: for typical launch scenario, ensure that close waits for actual process termination. // Hack: for typical launch scenario, ensure that close waits for actual process termination.
browser.close = () => server.close(); browser.close = () => app.close();
return browser; return browser;
} }
async launchServer(options: LaunchOptions = {}): Promise<CRBrowserServer> { async launchBrowserApp(options: LaunchOptions = {}): Promise<BrowserApp> {
const { const {
ignoreDefaultArgs = false, ignoreDefaultArgs = false,
args = [], args = [],
@ -165,7 +138,7 @@ export class CRPlaywright implements Playwright {
}, },
}); });
let connectOptions: CRConnectOptions | undefined; let connectOptions: ConnectOptions | undefined;
if (!usePipe) { if (!usePipe) {
const timeoutError = new TimeoutError(`Timed out after ${timeout} ms while trying to connect to Chromium! The only Chromium revision guaranteed to work is r${this._revision}`); const timeoutError = new TimeoutError(`Timed out after ${timeout} ms while trying to connect to Chromium! The only Chromium revision guaranteed to work is r${this._revision}`);
const match = await waitForLine(launchedProcess, launchedProcess.stderr, /^DevTools listening on (ws:\/\/.*)$/, timeout, timeoutError); const match = await waitForLine(launchedProcess, launchedProcess.stderr, /^DevTools listening on (ws:\/\/.*)$/, timeout, timeoutError);
@ -175,10 +148,23 @@ export class CRPlaywright implements Playwright {
const transport = new PipeTransport(launchedProcess.stdio[3] as NodeJS.WritableStream, launchedProcess.stdio[4] as NodeJS.ReadableStream); const transport = new PipeTransport(launchedProcess.stdio[3] as NodeJS.WritableStream, launchedProcess.stdio[4] as NodeJS.ReadableStream);
connectOptions = { slowMo, transport }; connectOptions = { slowMo, transport };
} }
return new CRBrowserServer(launchedProcess, gracefullyClose, connectOptions); return new BrowserApp(launchedProcess, gracefullyClose, connectOptions);
} }
async connect(options: CRConnectOptions): Promise<CRBrowser> { async connect(options: ConnectOptions & { browserURL?: string }): Promise<CRBrowser> {
if (options.browserURL) {
assert(!options.browserWSEndpoint && !options.transport, 'Exactly one of browserWSEndpoint, browserURL or transport must be passed to connect');
let connectionURL: string;
try {
const data = await platform.fetchUrl(new URL('/json/version', options.browserURL).href);
connectionURL = JSON.parse(data).webSocketDebuggerUrl;
} catch (e) {
e.message = `Failed to fetch browser webSocket url from ${options.browserURL}: ` + e.message;
throw e;
}
const transport = await platform.createWebSocketTransport(connectionURL);
options = { ...options, transport };
}
return CRBrowser.connect(options); return CRBrowser.connect(options);
} }

View file

@ -15,14 +15,13 @@
* limitations under the License. * limitations under the License.
*/ */
import { FFBrowser, FFConnectOptions, createTransport } from '../firefox/ffBrowser'; import { FFBrowser } from '../firefox/ffBrowser';
import { BrowserFetcher, BrowserFetcherOptions } from './browserFetcher'; import { BrowserFetcher, BrowserFetcherOptions } from './browserFetcher';
import { DeviceDescriptors } from '../deviceDescriptors'; 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 { FFConnection } from '../firefox/ffConnection';
import { ChildProcess } from 'child_process';
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';
@ -30,6 +29,8 @@ import * as util from 'util';
import { TimeoutError } from '../errors'; import { TimeoutError } from '../errors';
import { assert } from '../helper'; import { assert } from '../helper';
import { Playwright } from './playwright'; import { Playwright } from './playwright';
import { createTransport, ConnectOptions } from '../browser';
import { BrowserApp } from './browserApp';
export type SlowMoOptions = { export type SlowMoOptions = {
slowMo?: number, slowMo?: number,
@ -52,34 +53,6 @@ export type LaunchOptions = FirefoxArgOptions & SlowMoOptions & {
env?: {[key: string]: string} | undefined, env?: {[key: string]: string} | undefined,
}; };
export class FFBrowserServer {
private _process: ChildProcess;
private _gracefullyClose: () => Promise<void>;
private _connectOptions: FFConnectOptions;
constructor(process: ChildProcess, gracefullyClose: () => Promise<void>, connectOptions: FFConnectOptions) {
this._process = process;
this._gracefullyClose = gracefullyClose;
this._connectOptions = connectOptions;
}
process(): ChildProcess {
return this._process;
}
wsEndpoint(): string | null {
return this._connectOptions.browserWSEndpoint || null;
}
connectOptions(): FFConnectOptions {
return this._connectOptions;
}
async close(): Promise<void> {
await this._gracefullyClose();
}
}
export class FFPlaywright implements Playwright { export class FFPlaywright implements Playwright {
private _projectRoot: string; private _projectRoot: string;
readonly _revision: string; readonly _revision: string;
@ -90,14 +63,14 @@ export class FFPlaywright implements Playwright {
} }
async launch(options: LaunchOptions): Promise<FFBrowser> { async launch(options: LaunchOptions): Promise<FFBrowser> {
const server = await this.launchServer(options); const app = await this.launchBrowserApp(options);
const browser = await FFBrowser.connect(server.connectOptions()); const browser = await FFBrowser.connect(app.connectOptions());
// Hack: for typical launch scenario, ensure that close waits for actual process termination. // Hack: for typical launch scenario, ensure that close waits for actual process termination.
browser.close = () => server.close(); browser.close = () => app.close();
return browser; return browser;
} }
async launchServer(options: LaunchOptions = {}): Promise<FFBrowserServer> { async launchBrowserApp(options: LaunchOptions = {}): Promise<BrowserApp> {
const { const {
ignoreDefaultArgs = false, ignoreDefaultArgs = false,
args = [], args = [],
@ -136,7 +109,7 @@ export class FFPlaywright implements Playwright {
firefoxExecutable = executablePath; firefoxExecutable = executablePath;
} }
let connectOptions: FFConnectOptions | undefined = undefined; let connectOptions: ConnectOptions | undefined = undefined;
const { launchedProcess, gracefullyClose } = await launchProcess({ const { launchedProcess, gracefullyClose } = await launchProcess({
executablePath: firefoxExecutable, executablePath: firefoxExecutable,
@ -168,10 +141,10 @@ export class FFPlaywright implements Playwright {
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 url = match[1];
connectOptions = { browserWSEndpoint: url, slowMo }; connectOptions = { browserWSEndpoint: url, slowMo };
return new FFBrowserServer(launchedProcess, gracefullyClose, connectOptions); return new BrowserApp(launchedProcess, gracefullyClose, connectOptions);
} }
async connect(options: FFConnectOptions): Promise<FFBrowser> { async connect(options: ConnectOptions): Promise<FFBrowser> {
return FFBrowser.connect(options); return FFBrowser.connect(options);
} }

View file

@ -20,8 +20,7 @@ import { DeviceDescriptors } from '../deviceDescriptors';
import { TimeoutError } from '../errors'; import { TimeoutError } from '../errors';
import * as types from '../types'; import * as types from '../types';
import { WKBrowser } from '../webkit/wkBrowser'; import { WKBrowser } from '../webkit/wkBrowser';
import { WKConnectOptions } from '../webkit/wkBrowser'; import { execSync } from 'child_process';
import { execSync, ChildProcess } from 'child_process';
import { PipeTransport } from './pipeTransport'; import { PipeTransport } from './pipeTransport';
import { launchProcess } from './processLauncher'; import { launchProcess } from './processLauncher';
import * as fs from 'fs'; import * as fs from 'fs';
@ -35,6 +34,8 @@ import { Playwright } from './playwright';
import { ConnectionTransport } from '../transport'; import { ConnectionTransport } from '../transport';
import * as ws from 'ws'; import * as ws from 'ws';
import * as uuidv4 from 'uuid/v4'; import * as uuidv4 from 'uuid/v4';
import { ConnectOptions } from '../browser';
import { BrowserApp } from './browserApp';
export type SlowMoOptions = { export type SlowMoOptions = {
slowMo?: number, slowMo?: number,
@ -58,34 +59,6 @@ export type LaunchOptions = WebKitArgOptions & SlowMoOptions & {
pipe?: boolean, pipe?: boolean,
}; };
export class WKBrowserServer {
private _process: ChildProcess;
private _gracefullyClose: () => Promise<void>;
private _connectOptions: WKConnectOptions;
constructor(process: ChildProcess, gracefullyClose: () => Promise<void>, connectOptions: WKConnectOptions) {
this._process = process;
this._gracefullyClose = gracefullyClose;
this._connectOptions = connectOptions;
}
process(): ChildProcess {
return this._process;
}
wsEndpoint(): string | null {
return this._connectOptions.browserWSEndpoint || null;
}
connectOptions(): WKConnectOptions {
return this._connectOptions;
}
async close(): Promise<void> {
await this._gracefullyClose();
}
}
export class WKPlaywright implements Playwright { export class WKPlaywright implements Playwright {
private _projectRoot: string; private _projectRoot: string;
readonly _revision: string; readonly _revision: string;
@ -96,14 +69,14 @@ export class WKPlaywright implements Playwright {
} }
async launch(options?: LaunchOptions): Promise<WKBrowser> { async launch(options?: LaunchOptions): Promise<WKBrowser> {
const server = await this.launchServer(options); const app = await this.launchBrowserApp(options);
const browser = await WKBrowser.connect(server.connectOptions()); const browser = await WKBrowser.connect(app.connectOptions());
// Hack: for typical launch scenario, ensure that close waits for actual process termination. // Hack: for typical launch scenario, ensure that close waits for actual process termination.
browser.close = () => server.close(); browser.close = () => app.close();
return browser; return browser;
} }
async launchServer(options: LaunchOptions = {}): Promise<WKBrowserServer> { async launchBrowserApp(options: LaunchOptions = {}): Promise<BrowserApp> {
const { const {
ignoreDefaultArgs = false, ignoreDefaultArgs = false,
args = [], args = [],
@ -166,17 +139,17 @@ 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: WKConnectOptions; let connectOptions: ConnectOptions;
if (!pipe) { if (!pipe) {
const browserWSEndpoint = wrapTransportWithWebSocket(transport); const browserWSEndpoint = wrapTransportWithWebSocket(transport);
connectOptions = { browserWSEndpoint, slowMo }; connectOptions = { browserWSEndpoint, slowMo };
} else { } else {
connectOptions = { transport, slowMo }; connectOptions = { transport, slowMo };
} }
return new WKBrowserServer(launchedProcess, gracefullyClose, connectOptions); return new BrowserApp(launchedProcess, gracefullyClose, connectOptions);
} }
async connect(options: WKConnectOptions): Promise<WKBrowser> { async connect(options: ConnectOptions): Promise<WKBrowser> {
return WKBrowser.connect(options); return WKBrowser.connect(options);
} }

View file

@ -15,12 +15,12 @@
* limitations under the License. * limitations under the License.
*/ */
import { Browser } from '../browser'; import { Browser, createTransport, ConnectOptions } from '../browser';
import { BrowserContext, BrowserContextOptions } from '../browserContext'; import { BrowserContext, BrowserContextOptions } from '../browserContext';
import { assert, helper, RegisteredListener } from '../helper'; import { assert, helper, RegisteredListener } from '../helper';
import * as network from '../network'; import * as network from '../network';
import { Page } from '../page'; import { Page } from '../page';
import { ConnectionTransport, SlowMoTransport } from '../transport'; import { ConnectionTransport } from '../transport';
import * as types from '../types'; import * as types from '../types';
import { Events } from '../events'; import { Events } from '../events';
import { Protocol } from './protocol'; import { Protocol } from './protocol';
@ -28,12 +28,6 @@ import { WKConnection, WKSession, kPageProxyMessageReceived, PageProxyMessageRec
import { WKPageProxy } from './wkPageProxy'; import { WKPageProxy } from './wkPageProxy';
import * as platform from '../platform'; import * as platform from '../platform';
export type WKConnectOptions = {
slowMo?: number,
browserWSEndpoint?: string,
transport?: ConnectionTransport,
};
export class WKBrowser extends platform.EventEmitter implements Browser { export class WKBrowser extends platform.EventEmitter implements Browser {
private readonly _connection: WKConnection; private readonly _connection: WKConnection;
private readonly _browserSession: WKSession; private readonly _browserSession: WKSession;
@ -45,7 +39,7 @@ export class WKBrowser extends platform.EventEmitter implements Browser {
private _firstPageProxyCallback?: () => void; private _firstPageProxyCallback?: () => void;
private readonly _firstPageProxyPromise: Promise<void>; private readonly _firstPageProxyPromise: Promise<void>;
static async connect(options: WKConnectOptions): Promise<WKBrowser> { static async connect(options: ConnectOptions): Promise<WKBrowser> {
const transport = await createTransport(options); const transport = await createTransport(options);
const browser = new WKBrowser(transport); const browser = new WKBrowser(transport);
// TODO: figure out the timeout. // TODO: figure out the timeout.
@ -228,13 +222,3 @@ export class WKBrowser extends platform.EventEmitter implements Browser {
return context; return context;
} }
} }
export async function createTransport(options: WKConnectOptions): Promise<ConnectionTransport> {
assert(Number(!!options.browserWSEndpoint) + Number(!!options.transport) === 1, 'Exactly one of browserWSEndpoint or transport must be passed to connect');
let transport: ConnectionTransport | undefined;
if (options.transport)
transport = options.transport;
else if (options.browserWSEndpoint)
transport = await platform.createWebSocketTransport(options.browserWSEndpoint);
return SlowMoTransport.wrap(transport!, options.slowMo);
}

View file

@ -22,7 +22,7 @@ import { Protocol } from './protocol';
const debugProtocol = platform.debug('pw:protocol'); const debugProtocol = platform.debug('pw:protocol');
// WKBrowserServer uses this special id to issue Browser.close command which we // WKPlaywright uses this special id to issue Browser.close command which we
// should ignore. // should ignore.
export const kBrowserCloseMessageId = -9999; export const kBrowserCloseMessageId = -9999;

View file

@ -21,8 +21,8 @@ module.exports.describe = function({testRunner, expect, headless, playwright, FF
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Browser.process', function() { describe('Browser.process', function() {
it('should return child_process instance', async function({browserServer}) { it('should return child_process instance', async function({browserApp}) {
const process = await browserServer.process(); const process = await browserApp.process();
expect(process.pid).toBeGreaterThan(0); expect(process.pid).toBeGreaterThan(0);
}); });
}); });

View file

@ -22,12 +22,12 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner; const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
describe('Chromium', function() { describe('Chromium', function() {
it('should work across sessions', async function({browserServer, server, browser, newContext}) { it('should work across sessions', async function({browserApp, server, browser, newContext}) {
expect(browser.browserContexts().length).toBe(2); expect(browser.browserContexts().length).toBe(2);
await newContext(); await newContext();
expect(browser.browserContexts().length).toBe(3); expect(browser.browserContexts().length).toBe(3);
const remoteBrowser = await playwright.connect({ const remoteBrowser = await playwright.connect({
browserWSEndpoint: browserServer.wsEndpoint() browserWSEndpoint: browserApp.wsEndpoint()
}); });
const contexts = remoteBrowser.browserContexts(); const contexts = remoteBrowser.browserContexts();
expect(contexts.length).toBe(3); expect(contexts.length).toBe(3);

View file

@ -24,11 +24,11 @@ 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 browserServer = await playwright.launchServer(defaultBrowserOptions); const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
const local = await playwright.connect(browserServer.connectOptions()); const local = await playwright.connect(browserApp.connectOptions());
const remote = await playwright.connect({ const remote = await playwright.connect({
...defaultBrowserOptions, ...defaultBrowserOptions,
browserWSEndpoint: browserServer.wsEndpoint() 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);
@ -36,14 +36,14 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
const secondPage = await local.defaultContext().newPage(); const secondPage = await local.defaultContext().newPage();
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 browserServer.close(); await browserApp.close();
}); });
it('should be able to close remote browser', async({server}) => { it('should be able to close remote browser', async({server}) => {
const browserServer = await playwright.launchServer(defaultBrowserOptions); const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
const local = await playwright.connect(browserServer.connectOptions()); const local = await playwright.connect(browserApp.connectOptions());
const remote = await playwright.connect({ const remote = await playwright.connect({
...defaultBrowserOptions, ...defaultBrowserOptions,
browserWSEndpoint: browserServer.wsEndpoint() browserWSEndpoint: browserApp.wsEndpoint()
}); });
await Promise.all([ await Promise.all([
utils.waitEvent(local, 'disconnected'), utils.waitEvent(local, 'disconnected'),
@ -52,9 +52,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 browserServer = await playwright.launchServer(defaultBrowserOptions); const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
const local = await playwright.connect(browserServer.connectOptions()); const local = await playwright.connect(browserApp.connectOptions());
const remote = await playwright.connect({ ...defaultBrowserOptions, browserWSEndpoint: browserServer.wsEndpoint() }); const remote = await playwright.connect({ ...defaultBrowserOptions, browserWSEndpoint: browserApp.wsEndpoint() });
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(),

View file

@ -51,16 +51,16 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
originalBrowser.close(); originalBrowser.close();
}); });
it('should throw when using both browserWSEndpoint and browserURL', async({server}) => { it('should throw when using both browserWSEndpoint and browserURL', async({server}) => {
const browserServer = await playwright.launchServer(Object.assign({}, defaultBrowserOptions, { const browserApp = await playwright.launchBrowserApp(Object.assign({}, defaultBrowserOptions, {
args: ['--remote-debugging-port=21222'] args: ['--remote-debugging-port=21222']
})); }));
const browserURL = 'http://127.0.0.1:21222'; const browserURL = 'http://127.0.0.1:21222';
let error = null; let error = null;
await playwright.connect({browserURL, browserWSEndpoint: browserServer.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');
browserServer.close(); 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 originalBrowser = await playwright.launch(Object.assign({}, defaultBrowserOptions, {
@ -78,33 +78,33 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
describe('Playwright.launch |pipe| option', function() { describe('Playwright.launch |pipe| option', function() {
it('should support the pipe option', async() => { it('should support the pipe option', async() => {
const options = Object.assign({pipe: true}, defaultBrowserOptions); const options = Object.assign({pipe: true}, defaultBrowserOptions);
const browserServer = await playwright.launchServer(options); const browserApp = await playwright.launchBrowserApp(options);
const browser = await playwright.connect(browserServer.connectOptions()); const browser = await playwright.connect(browserApp.connectOptions());
expect((await browser.defaultContext().pages()).length).toBe(1); expect((await browser.defaultContext().pages()).length).toBe(1);
expect(browserServer.wsEndpoint()).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 browserServer.close(); await browserApp.close();
}); });
it('should support the pipe argument', async() => { it('should support the 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 browserServer = await playwright.launchServer(options); const browserApp = await playwright.launchBrowserApp(options);
const browser = await playwright.connect(browserServer.connectOptions()); const browser = await playwright.connect(browserApp.connectOptions());
expect(browserServer.wsEndpoint()).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 browserServer.close(); await browserApp.close();
}); });
it('should fire "disconnected" when closing with pipe', async() => { it('should fire "disconnected" when closing with pipe', async() => {
const options = Object.assign({pipe: true}, defaultBrowserOptions); const options = Object.assign({pipe: true}, defaultBrowserOptions);
const browserServer = await playwright.launchServer(options); const browserApp = await playwright.launchBrowserApp(options);
const browser = await playwright.connect(browserServer.connectOptions()); const browser = await playwright.connect(browserApp.connectOptions());
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve)); const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
// Emulate user exiting browser. // Emulate user exiting browser.
browserServer.process().kill(); browserApp.process().kill();
await disconnectedEventPromise; await disconnectedEventPromise;
}); });
}); });
@ -127,9 +127,9 @@ 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 browserServer = await playwright.launchServer(defaultBrowserOptions); const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
const originalBrowser = await playwright.connect(browserServer.connectOptions()); const originalBrowser = await playwright.connect(browserApp.connectOptions());
const browserWSEndpoint = browserServer.wsEndpoint(); const browserWSEndpoint = browserApp.wsEndpoint();
const remoteBrowser1 = await playwright.connect({browserWSEndpoint}); const remoteBrowser1 = await playwright.connect({browserWSEndpoint});
const remoteBrowser2 = await playwright.connect({browserWSEndpoint}); const remoteBrowser2 = await playwright.connect({browserWSEndpoint});
@ -152,7 +152,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
await Promise.all([ await Promise.all([
waitEvent(remoteBrowser1, 'disconnected'), waitEvent(remoteBrowser1, 'disconnected'),
waitEvent(originalBrowser, 'disconnected'), waitEvent(originalBrowser, 'disconnected'),
browserServer.close(), browserApp.close(),
]); ]);
expect(disconnectedOriginal).toBe(1); expect(disconnectedOriginal).toBe(1);

View file

@ -65,15 +65,15 @@ module.exports.describe = function ({ testRunner, expect, defaultBrowserOptions,
it('should filter out ignored default arguments', async() => { it('should filter out ignored default arguments', async() => {
// Make sure we launch with `--enable-automation` by default. // Make sure we launch with `--enable-automation` by default.
const defaultArgs = playwright.defaultArgs(defaultBrowserOptions); const defaultArgs = playwright.defaultArgs(defaultBrowserOptions);
const browserServer = await playwright.launchServer(Object.assign({}, defaultBrowserOptions, { const browserApp = await playwright.launchBrowserApp(Object.assign({}, defaultBrowserOptions, {
// Ignore first and third default argument. // Ignore first and third default argument.
ignoreDefaultArgs: [ defaultArgs[0], defaultArgs[2] ], ignoreDefaultArgs: [ defaultArgs[0], defaultArgs[2] ],
})); }));
const spawnargs = browserServer.process().spawnargs; const spawnargs = browserApp.process().spawnargs;
expect(spawnargs.indexOf(defaultArgs[0])).toBe(-1); expect(spawnargs.indexOf(defaultArgs[0])).toBe(-1);
expect(spawnargs.indexOf(defaultArgs[1])).not.toBe(-1); expect(spawnargs.indexOf(defaultArgs[1])).not.toBe(-1);
expect(spawnargs.indexOf(defaultArgs[2])).toBe(-1); expect(spawnargs.indexOf(defaultArgs[2])).toBe(-1);
await browserServer.close(); await browserApp.close();
}); });
}); });
}); });

View file

@ -1,5 +1,5 @@
(async() => { (async() => {
const [, , playwrightRoot, product, options] = process.argv; const [, , playwrightRoot, product, options] = process.argv;
const browserServer = await require(playwrightRoot)[product.toLowerCase()].launchServer(JSON.parse(options)); const browserApp = await require(playwrightRoot)[product.toLowerCase()].launchBrowserApp(JSON.parse(options));
console.log(browserServer.wsEndpoint()); console.log(browserApp.wsEndpoint());
})(); })();

View file

@ -17,7 +17,7 @@
if (product.toLowerCase() === 'firefox') if (product.toLowerCase() === 'firefox')
options.args.push('-juggler', '-profile'); options.args.push('-juggler', '-profile');
try { try {
await require(playwrightRoot)[product.toLowerCase()].launchServer(options); await require(playwrightRoot)[product.toLowerCase()].launchBrowserApp(options);
console.error('Browser launch unexpectedly succeeded.'); console.error('Browser launch unexpectedly succeeded.');
} catch (e) { } catch (e) {
} }

View file

@ -90,20 +90,20 @@ 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 browserServer = await playwright.launchServer(defaultBrowserOptions); const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
const browserWSEndpoint = browserServer.wsEndpoint(); const browserWSEndpoint = browserApp.wsEndpoint();
const remote = await playwright.connect({browserWSEndpoint}); 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 browserServer.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 browserServer = await playwright.launchServer(defaultBrowserOptions); const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserServer.wsEndpoint()}); const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserApp.wsEndpoint()});
const page = await remote.defaultContext().newPage(); const page = await remote.defaultContext().newPage();
await Promise.all([ await Promise.all([
browserServer.close(), browserApp.close(),
new Promise(f => remote.once('disconnected', f)), new Promise(f => remote.once('disconnected', f)),
]); ]);
expect(remote.isConnected()).toBe(false); expect(remote.isConnected()).toBe(false);
@ -115,47 +115,47 @@ 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 browserServer = await playwright.launchServer(defaultBrowserOptions); const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserServer.wsEndpoint()}); const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserApp.wsEndpoint()});
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');
await remote.disconnect(); await remote.disconnect();
const error = await navigationPromise; const error = await navigationPromise;
expect(error.message).toBe('Navigation failed because browser has disconnected!'); expect(error.message).toBe('Navigation failed because browser has disconnected!');
await browserServer.close(); await browserApp.close();
}); });
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 browserServer = await playwright.launchServer(defaultBrowserOptions); const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserServer.wsEndpoint()}); const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserApp.wsEndpoint()});
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();
const error = await watchdog; const error = await watchdog;
expect(error.message).toContain('Protocol error'); expect(error.message).toContain('Protocol error');
await browserServer.close(); await browserApp.close();
}); });
it('should throw if used after disconnect', async({server}) => { it('should throw if used after disconnect', async({server}) => {
const browserServer = await playwright.launchServer(defaultBrowserOptions); const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserServer.wsEndpoint()}); const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserApp.wsEndpoint()});
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);
expect(error.message).toContain('has been closed'); expect(error.message).toContain('has been closed');
await browserServer.close(); await browserApp.close();
}); });
}); });
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 browserServer = await playwright.launchServer(defaultBrowserOptions); const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserServer.wsEndpoint()}); const remote = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint: browserApp.wsEndpoint()});
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),
newPage.waitForResponse(server.EMPTY_PAGE).catch(e => e), newPage.waitForResponse(server.EMPTY_PAGE).catch(e => e),
browserServer.close() browserApp.close()
]); ]);
for (let i = 0; i < 2; i++) { for (let i = 0; i < 2; i++) {
const message = results[i].message; const message = results[i].message;
@ -167,9 +167,9 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
describe('Playwright.connect', function() { describe('Playwright.connect', function() {
it.skip(WEBKIT)('should be able to reconnect to a browser', async({server}) => { it.skip(WEBKIT)('should be able to reconnect to a browser', async({server}) => {
const browserServer = await playwright.launchServer(defaultBrowserOptions); const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
const browser = await playwright.connect(browserServer.connectOptions()); const browser = await playwright.connect(browserApp.connectOptions());
const browserWSEndpoint = browserServer.wsEndpoint(); const browserWSEndpoint = browserApp.wsEndpoint();
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();
@ -185,7 +185,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
' http://localhost:<PORT>/frames/frame.html (uno)', ' http://localhost:<PORT>/frames/frame.html (uno)',
]); ]);
expect(await restoredPage.evaluate(() => 7 * 8)).toBe(56); expect(await restoredPage.evaluate(() => 7 * 8)).toBe(56);
await browserServer.close(); await browserApp.close();
}); });
}); });
@ -228,15 +228,15 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
it('should filter out ignored default arguments', async() => { it('should filter out ignored default arguments', async() => {
// Make sure we launch with `--enable-automation` by default. // Make sure we launch with `--enable-automation` by default.
const defaultArgs = playwright.defaultArgs(defaultBrowserOptions); const defaultArgs = playwright.defaultArgs(defaultBrowserOptions);
const browserServer = await playwright.launchServer(Object.assign({}, defaultBrowserOptions, { const browserApp = await playwright.launchBrowserApp(Object.assign({}, defaultBrowserOptions, {
// Ignore first and third default argument. // Ignore first and third default argument.
ignoreDefaultArgs: [ defaultArgs[0], defaultArgs[2] ], ignoreDefaultArgs: [ defaultArgs[0], defaultArgs[2] ],
})); }));
const spawnargs = browserServer.process().spawnargs; const spawnargs = browserApp.process().spawnargs;
expect(spawnargs.indexOf(defaultArgs[0])).toBe(-1); expect(spawnargs.indexOf(defaultArgs[0])).toBe(-1);
expect(spawnargs.indexOf(defaultArgs[1])).not.toBe(-1); expect(spawnargs.indexOf(defaultArgs[1])).not.toBe(-1);
expect(spawnargs.indexOf(defaultArgs[2])).toBe(-1); expect(spawnargs.indexOf(defaultArgs[2])).toBe(-1);
await browserServer.close(); await browserApp.close();
}); });
it('userDataDir option should restore state', async({server}) => { it('userDataDir option should restore state', async({server}) => {
const userDataDir = await mkdtempAsync(TMP_FOLDER); const userDataDir = await mkdtempAsync(TMP_FOLDER);

View file

@ -90,14 +90,14 @@ module.exports.describe = ({testRunner, product, playwrightPath}) => {
describe('Browser', function() { describe('Browser', function() {
beforeAll(async state => { beforeAll(async state => {
state.browserServer = await playwright.launchServer(defaultBrowserOptions); state.browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
state.browser = await playwright.connect(state.browserServer.connectOptions()); state.browser = await playwright.connect(state.browserApp.connectOptions());
}); });
afterAll(async state => { afterAll(async state => {
await state.browserServer.close(); await state.browserApp.close();
state.browser = null; state.browser = null;
state.browserServer = null; state.browserApp = null;
}); });
beforeEach(async(state, test) => { beforeEach(async(state, test) => {
@ -106,7 +106,7 @@ module.exports.describe = ({testRunner, product, playwrightPath}) => {
let rl; let rl;
if (!WEBKIT) { if (!WEBKIT) {
rl = require('readline').createInterface({ input: state.browserServer.process().stderr }); rl = require('readline').createInterface({ input: state.browserApp.process().stderr });
test.output = ''; test.output = '';
rl.on('line', onLine); rl.on('line', onLine);
} }

View file

@ -21,18 +21,18 @@ 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.controlledBrowserServer = await playwright.launchServer({ ...defaultBrowserOptions, pipe: false }); state.controlledBrowserApp = await playwright.launchBrowserApp({ ...defaultBrowserOptions, pipe: false });
state.hostBrowserServer = await playwright.launchServer(defaultBrowserOptions); state.hostBrowserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
state.hostBrowser = await playwright.connect(state.hostBrowserServer.connectOptions()); state.hostBrowser = await playwright.connect(state.hostBrowserApp.connectOptions());
}); });
afterAll(async state => { afterAll(async state => {
await state.hostBrowserServer.close(); await state.hostBrowserApp.close();
state.hostBrowser = null; state.hostBrowser = null;
state.hostBrowserServer = null; state.hostBrowserApp = null;
await state.controlledBrowserServer.close(); await state.controlledBrowserApp.close();
state.controlledBrowserServer = null; state.controlledBrowserApp = null;
state.webUrl = null; state.webUrl = null;
}); });
@ -40,7 +40,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
state.page = await state.hostBrowser.defaultContext().newPage(); state.page = await state.hostBrowser.defaultContext().newPage();
state.page.on('console', message => console.log('TEST: ' + message.text())); state.page.on('console', message => console.log('TEST: ' + message.text()));
await state.page.goto(state.sourceServer.PREFIX + '/test/assets/playwrightweb.html'); await state.page.goto(state.sourceServer.PREFIX + '/test/assets/playwrightweb.html');
await state.page.evaluate((product, connectOptions) => setup(product, connectOptions), product.toLowerCase(), state.controlledBrowserServer.connectOptions()); await state.page.evaluate((product, connectOptions) => setup(product, connectOptions), product.toLowerCase(), state.controlledBrowserApp.connectOptions());
}); });
afterEach(async state => { afterEach(async state => {

View file

@ -24,42 +24,42 @@ module.exports.describe = function ({ testRunner, expect, playwright, defaultBro
describe('Playwright.launch |pipe| option', function() { describe('Playwright.launch |pipe| option', function() {
it('should have websocket by default', async() => { it('should have websocket by default', async() => {
const options = Object.assign({pipe: false}, defaultBrowserOptions); const options = Object.assign({pipe: false}, defaultBrowserOptions);
const browserServer = await playwright.launchServer(options); const browserApp = await playwright.launchBrowserApp(options);
const browser = await playwright.connect(browserServer.connectOptions()); const browser = await playwright.connect(browserApp.connectOptions());
expect((await browser.defaultContext().pages()).length).toBe(1); expect((await browser.defaultContext().pages()).length).toBe(1);
expect(browserServer.wsEndpoint()).not.toBe(null); expect(browserApp.wsEndpoint()).not.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 browserServer.close(); await browserApp.close();
}); });
it('should support the pipe option', async() => { it('should support the pipe option', async() => {
const options = Object.assign({pipe: true}, defaultBrowserOptions); const options = Object.assign({pipe: true}, defaultBrowserOptions);
const browserServer = await playwright.launchServer(options); const browserApp = await playwright.launchBrowserApp(options);
const browser = await playwright.connect(browserServer.connectOptions()); const browser = await playwright.connect(browserApp.connectOptions());
expect((await browser.defaultContext().pages()).length).toBe(1); expect((await browser.defaultContext().pages()).length).toBe(1);
expect(browserServer.wsEndpoint()).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 browserServer.close(); await browserApp.close();
}); });
it('should fire "disconnected" when closing with pipe', async() => { it('should fire "disconnected" when closing with pipe', async() => {
const options = Object.assign({pipe: true}, defaultBrowserOptions); const options = Object.assign({pipe: true}, defaultBrowserOptions);
const browserServer = await playwright.launchServer(options); const browserApp = await playwright.launchBrowserApp(options);
const browser = await playwright.connect(browserServer.connectOptions()); const browser = await playwright.connect(browserApp.connectOptions());
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve)); const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
// Emulate user exiting browser. // Emulate user exiting browser.
process.kill(-browserServer.process().pid, 'SIGKILL'); process.kill(-browserApp.process().pid, 'SIGKILL');
await disconnectedEventPromise; await disconnectedEventPromise;
}); });
it('should fire "disconnected" when closing with websocket', async() => { it('should fire "disconnected" when closing with websocket', async() => {
const options = Object.assign({pipe: false}, defaultBrowserOptions); const options = Object.assign({pipe: false}, defaultBrowserOptions);
const browserServer = await playwright.launchServer(options); const browserApp = await playwright.launchBrowserApp(options);
const browser = await playwright.connect(browserServer.connectOptions()); const browser = await playwright.connect(browserApp.connectOptions());
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve)); const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
// Emulate user exiting browser. // Emulate user exiting browser.
process.kill(-browserServer.process().pid, 'SIGKILL'); process.kill(-browserApp.process().pid, 'SIGKILL');
await disconnectedEventPromise; await disconnectedEventPromise;
}); });
}); });

View file

@ -10,13 +10,13 @@ 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 browserServer = await playwright.launchServer({executablePath: revision.executablePath}); const browserApp = await playwright.launchBrowserApp({executablePath: revision.executablePath});
const origin = browserServer.wsEndpoint().match(/ws:\/\/([0-9A-Za-z:\.]*)\//)[1]; const origin = browserApp.wsEndpoint().match(/ws:\/\/([0-9A-Za-z:\.]*)\//)[1];
const browser = await playwright.connect(browserServer.connectOptions()); const browser = await playwright.connect(browserApp.connectOptions());
const page = await browser.defaultContext().newPage(); const page = await browser.defaultContext().newPage();
await page.goto(`http://${origin}/json/protocol`); await page.goto(`http://${origin}/json/protocol`);
const json = JSON.parse(await page.evaluate(() => document.documentElement.innerText)); const json = JSON.parse(await page.evaluate(() => document.documentElement.innerText));
await browserServer.close(); await browserApp.close();
fs.writeFileSync(outputPath, jsonToTS(json)); fs.writeFileSync(outputPath, jsonToTS(json));
console.log(`Wrote protocol.ts to ${path.relative(process.cwd(), outputPath)}`); console.log(`Wrote protocol.ts to ${path.relative(process.cwd(), outputPath)}`);
} }