feat(launch): introduce client, server & persistent launch modes (2) (#840)
This commit is contained in:
parent
0f1a42a5d3
commit
a2ab645e63
16
README.md
16
README.md
|
|
@ -40,9 +40,7 @@ const playwright = require('playwright');
|
||||||
(async () => {
|
(async () => {
|
||||||
for (const browserType of ['chromium', 'firefox', 'webkit']) {
|
for (const browserType of ['chromium', 'firefox', 'webkit']) {
|
||||||
const browser = await playwright[browserType].launch();
|
const browser = await playwright[browserType].launch();
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage('http://whatsmyuseragent.org/');
|
||||||
const page = await context.newPage('http://whatsmyuseragent.org/');
|
|
||||||
|
|
||||||
await page.screenshot({ path: `example-${browserType}.png` });
|
await page.screenshot({ path: `example-${browserType}.png` });
|
||||||
await browser.close();
|
await browser.close();
|
||||||
}
|
}
|
||||||
|
|
@ -59,14 +57,13 @@ const iPhone11 = devices['iPhone 11 Pro'];
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await webkit.launch();
|
const browser = await webkit.launch();
|
||||||
const context = await browser.newContext({
|
const page = await browser.newPage('https://maps.google.com', {
|
||||||
viewport: iPhone11.viewport,
|
viewport: iPhone11.viewport,
|
||||||
userAgent: iPhone11.userAgent,
|
userAgent: iPhone11.userAgent,
|
||||||
geolocation: { longitude: 12.492507, latitude: 41.889938 },
|
geolocation: { longitude: 12.492507, latitude: 41.889938 },
|
||||||
permissions: { 'https://www.google.com': ['geolocation'] }
|
permissions: { 'https://www.google.com': ['geolocation'] }
|
||||||
});
|
});
|
||||||
|
|
||||||
const page = await context.newPage('https://maps.google.com');
|
|
||||||
await page.click('text="Your location"');
|
await page.click('text="Your location"');
|
||||||
await page.waitForRequest(/.*preview\/pwa/);
|
await page.waitForRequest(/.*preview\/pwa/);
|
||||||
await page.screenshot({ path: 'colosseum-iphone.png' });
|
await page.screenshot({ path: 'colosseum-iphone.png' });
|
||||||
|
|
@ -82,14 +79,12 @@ const pixel2 = devices['Pixel 2'];
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await chromium.launch();
|
const browser = await chromium.launch();
|
||||||
const context = await browser.newContext({
|
const page = await browser.newPage('https://maps.google.com', {
|
||||||
viewport: pixel2.viewport,
|
viewport: pixel2.viewport,
|
||||||
userAgent: pixel2.userAgent,
|
userAgent: pixel2.userAgent,
|
||||||
geolocation: { longitude: 12.492507, latitude: 41.889938 },
|
geolocation: { longitude: 12.492507, latitude: 41.889938 },
|
||||||
permissions: { 'https://www.google.com': ['geolocation'] }
|
permissions: { 'https://www.google.com': ['geolocation'] }
|
||||||
});
|
});
|
||||||
|
|
||||||
const page = await context.newPage('https://maps.google.com');
|
|
||||||
await page.click('text="Your location"');
|
await page.click('text="Your location"');
|
||||||
await page.waitForRequest(/.*pwa\/net.js.*/);
|
await page.waitForRequest(/.*pwa\/net.js.*/);
|
||||||
await page.screenshot({ path: 'colosseum-android.png' });
|
await page.screenshot({ path: 'colosseum-android.png' });
|
||||||
|
|
@ -106,10 +101,7 @@ const { firefox } = require('playwright');
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await firefox.launch();
|
const browser = await firefox.launch();
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage('https://www.example.com/');
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
await page.goto('https://www.example.com/');
|
|
||||||
const dimensions = await page.evaluate(() => {
|
const dimensions = await page.evaluate(() => {
|
||||||
return {
|
return {
|
||||||
width: document.documentElement.clientWidth,
|
width: document.documentElement.clientWidth,
|
||||||
|
|
|
||||||
176
docs/api.md
176
docs/api.md
|
|
@ -24,7 +24,7 @@
|
||||||
- [class: Accessibility](#class-accessibility)
|
- [class: Accessibility](#class-accessibility)
|
||||||
- [class: Coverage](#class-coverage)
|
- [class: Coverage](#class-coverage)
|
||||||
- [class: Worker](#class-worker)
|
- [class: Worker](#class-worker)
|
||||||
- [class: BrowserApp](#class-browserapp)
|
- [class: BrowserServer](#class-browserserver)
|
||||||
- [class: BrowserType](#class-browsertype)
|
- [class: BrowserType](#class-browsertype)
|
||||||
- [class: ChromiumBrowser](#class-chromiumbrowser)
|
- [class: ChromiumBrowser](#class-chromiumbrowser)
|
||||||
- [class: ChromiumSession](#class-chromiumsession)
|
- [class: ChromiumSession](#class-chromiumsession)
|
||||||
|
|
@ -44,8 +44,7 @@ const { chromium, firefox, webkit } = require('playwright');
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await chromium.launch(); // Or 'firefox' or 'webkit'.
|
const browser = await chromium.launch(); // Or 'firefox' or 'webkit'.
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage('http://example.com');
|
||||||
const page = await context.newPage('http://example.com');
|
|
||||||
// other actions...
|
// other actions...
|
||||||
await browser.close();
|
await browser.close();
|
||||||
})();
|
})();
|
||||||
|
|
@ -70,8 +69,7 @@ This object can be used to launch or connect to Chromium, returning instances of
|
||||||
#### playwright.devices
|
#### playwright.devices
|
||||||
- returns: <[Object]>
|
- returns: <[Object]>
|
||||||
|
|
||||||
Returns a list of devices to be used with [`browser.newContext(options)`](#browsernewcontextoptions). Actual list of
|
Returns a list of devices to be used with [`browser.newContext(options)`](#browsernewcontextoptions) or [`browser.newPage(options)`](#browsernewpageoptions). Actual list of devices can be found in [src/deviceDescriptors.ts](https://github.com/Microsoft/playwright/blob/master/src/deviceDescriptors.ts).
|
||||||
devices can be found in [src/deviceDescriptors.ts](https://github.com/Microsoft/playwright/blob/master/src/deviceDescriptors.ts).
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const { webkit, devices } = require('playwright');
|
const { webkit, devices } = require('playwright');
|
||||||
|
|
@ -138,36 +136,22 @@ const { firefox } = require('playwright'); // Or 'chromium' or 'webkit'.
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await firefox.launch();
|
const browser = await firefox.launch();
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage('https://example.com');
|
||||||
const page = await context.newPage('https://example.com');
|
|
||||||
await browser.close();
|
await browser.close();
|
||||||
})();
|
})();
|
||||||
```
|
```
|
||||||
|
|
||||||
An example of launching a browser executable and connecting to a [Browser] later:
|
|
||||||
```js
|
|
||||||
const { webkit } = require('playwright'); // Or 'chromium' or 'firefox'.
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
const browserApp = await webkit.launchBrowserApp();
|
|
||||||
const wsEndpoint = browserApp.wsEndpoint();
|
|
||||||
// Use web socket endpoint later to establish a connection.
|
|
||||||
const browser = await webkit.connect({ wsEndpoint });
|
|
||||||
// Close browser instance.
|
|
||||||
await browserApp.close();
|
|
||||||
})();
|
|
||||||
```
|
|
||||||
|
|
||||||
See [ChromiumBrowser], [FirefoxBrowser] and [WebKitBrowser] for browser-specific features. Note that [browserType.connect(options)](#browsertypeconnectoptions) and [browserType.launch(options)](#browsertypelaunchoptions) always return a specific browser instance, based on the browser being connected to or launched.
|
See [ChromiumBrowser], [FirefoxBrowser] and [WebKitBrowser] for browser-specific features. Note that [browserType.connect(options)](#browsertypeconnectoptions) and [browserType.launch(options)](#browsertypelaunchoptions) always return a specific browser instance, based on the browser being connected to or launched.
|
||||||
|
|
||||||
<!-- GEN:toc -->
|
<!-- GEN:toc -->
|
||||||
- [event: 'disconnected'](#event-disconnected)
|
- [event: 'disconnected'](#event-disconnected)
|
||||||
- [browser.browserContexts()](#browserbrowsercontexts)
|
- [browser.browserContexts()](#browserbrowsercontexts)
|
||||||
- [browser.close()](#browserclose)
|
- [browser.close()](#browserclose)
|
||||||
- [browser.defaultContext()](#browserdefaultcontext)
|
|
||||||
- [browser.disconnect()](#browserdisconnect)
|
- [browser.disconnect()](#browserdisconnect)
|
||||||
- [browser.isConnected()](#browserisconnected)
|
- [browser.isConnected()](#browserisconnected)
|
||||||
- [browser.newContext(options)](#browsernewcontextoptions)
|
- [browser.newContext(options)](#browsernewcontextoptions)
|
||||||
|
- [browser.newPage(url, [options])](#browsernewpageurl-options)
|
||||||
|
- [browser.pages()](#browserpages)
|
||||||
<!-- GEN:stop -->
|
<!-- GEN:stop -->
|
||||||
|
|
||||||
#### event: 'disconnected'
|
#### event: 'disconnected'
|
||||||
|
|
@ -186,11 +170,6 @@ a single instance of [BrowserContext].
|
||||||
|
|
||||||
Closes browser and all of its pages (if any were opened). The [Browser] object itself is considered to be disposed and cannot be used anymore.
|
Closes browser and all of its pages (if any were opened). The [Browser] object itself is considered to be disposed and cannot be used anymore.
|
||||||
|
|
||||||
#### browser.defaultContext()
|
|
||||||
- returns: <[BrowserContext]>
|
|
||||||
|
|
||||||
Returns the default browser context. The default browser context can not be closed.
|
|
||||||
|
|
||||||
#### browser.disconnect()
|
#### browser.disconnect()
|
||||||
- returns: <[Promise]>
|
- returns: <[Promise]>
|
||||||
|
|
||||||
|
|
@ -233,6 +212,33 @@ Creates a new browser context. It won't share cookies/cache with other browser c
|
||||||
})();
|
})();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### browser.newPage(url, [options])
|
||||||
|
- `url` <?[string]> Optional url to navigate the page to.
|
||||||
|
- `options` <[Object]>
|
||||||
|
- `ignoreHTTPSErrors` <?[boolean]> Whether to ignore HTTPS errors during navigation. Defaults to `false`.
|
||||||
|
- `bypassCSP` <?[boolean]> Toggles bypassing page's Content-Security-Policy.
|
||||||
|
- `viewport` <?[Object]> Sets a consistent viewport for each page. Defaults to an 800x600 viewport. `null` disables the default viewport.
|
||||||
|
- `width` <[number]> page width in pixels.
|
||||||
|
- `height` <[number]> page height in pixels.
|
||||||
|
- `deviceScaleFactor` <[number]> Specify device scale factor (can be thought of as dpr). Defaults to `1`.
|
||||||
|
- `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`.
|
||||||
|
- `userAgent` <?[string]> Specific user agent to use in this context.
|
||||||
|
- `javaScriptEnabled` <?[boolean]> Whether or not to enable or disable JavaScript in the context. Defaults to true.
|
||||||
|
- `timezoneId` <?[string]> Changes the timezone of the context. See [ICU’s `metaZones.txt`](https://cs.chromium.org/chromium/src/third_party/icu/source/data/misc/metaZones.txt?rcl=faee8bc70570192d82d2978a71e2a615788597d1) for a list of supported timezone IDs.
|
||||||
|
- `geolocation` <[Object]>
|
||||||
|
- `latitude` <[number]> Latitude between -90 and 90.
|
||||||
|
- `longitude` <[number]> Longitude between -180 and 180.
|
||||||
|
- `accuracy` <[number]> Optional non-negative accuracy value.
|
||||||
|
- `permissions` <[Object]> A map from origin keys to permissions values. See [browserContext.setPermissions](#browsercontextsetpermissionsorigin-permissions) for more details.
|
||||||
|
- returns: <[Promise]<[Page]>>
|
||||||
|
|
||||||
|
Creates a new page in a new browser context and optionally navigates it to the specified URL.
|
||||||
|
|
||||||
|
#### browser.pages()
|
||||||
|
- returns: <[Promise]<[Array]<[Page]>>> Promise which resolves to an array of all open pages.
|
||||||
|
|
||||||
|
An array of all the pages inside all the browser contexts.
|
||||||
|
|
||||||
### 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)
|
||||||
|
|
@ -277,7 +283,7 @@ Clears context bookies.
|
||||||
Clears all permission overrides for the browser context.
|
Clears all permission overrides for the browser context.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const context = browser.defaultContext();
|
const context = await browser.newContext();
|
||||||
context.setPermissions('https://example.com', ['clipboard-read']);
|
context.setPermissions('https://example.com', ['clipboard-read']);
|
||||||
// do stuff ..
|
// do stuff ..
|
||||||
context.clearPermissions();
|
context.clearPermissions();
|
||||||
|
|
@ -376,7 +382,7 @@ await browserContext.setGeolocation({latitude: 59.95, longitude: 30.31667});
|
||||||
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const context = browser.defaultContext();
|
const context = await browser.newContext();
|
||||||
await context.setPermissions('https://html5demos.com', ['geolocation']);
|
await context.setPermissions('https://html5demos.com', ['geolocation']);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -951,8 +957,7 @@ const crypto = require('crypto');
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await firefox.launch();
|
const browser = await firefox.launch();
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage();
|
||||||
const page = await context.newPage();
|
|
||||||
page.on('console', msg => console.log(msg.text()));
|
page.on('console', msg => console.log(msg.text()));
|
||||||
await page.exposeFunction('md5', text =>
|
await page.exposeFunction('md5', text =>
|
||||||
crypto.createHash('md5').update(text).digest('hex')
|
crypto.createHash('md5').update(text).digest('hex')
|
||||||
|
|
@ -975,8 +980,7 @@ const fs = require('fs');
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await chromium.launch();
|
const browser = await chromium.launch();
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage();
|
||||||
const page = await context.newPage();
|
|
||||||
page.on('console', msg => console.log(msg.text()));
|
page.on('console', msg => console.log(msg.text()));
|
||||||
await page.exposeFunction('readfile', async filePath => {
|
await page.exposeFunction('readfile', async filePath => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
@ -1491,8 +1495,7 @@ const { webkit } = require('playwright'); // Or 'chromium' or 'firefox'.
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await webkit.launch();
|
const browser = await webkit.launch();
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage();
|
||||||
const page = await context.newPage();
|
|
||||||
const watchDog = page.waitForFunction('window.innerWidth < 100');
|
const watchDog = page.waitForFunction('window.innerWidth < 100');
|
||||||
await page.setViewport({width: 50, height: 50});
|
await page.setViewport({width: 50, height: 50});
|
||||||
await watchDog;
|
await watchDog;
|
||||||
|
|
@ -1599,8 +1602,7 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'.
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await chromium.launch();
|
const browser = await chromium.launch();
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage();
|
||||||
const page = await context.newPage();
|
|
||||||
let currentURL;
|
let currentURL;
|
||||||
page
|
page
|
||||||
.waitForSelector('img')
|
.waitForSelector('img')
|
||||||
|
|
@ -1636,8 +1638,7 @@ const { firefox } = require('playwright'); // Or 'chromium' or 'webkit'.
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await firefox.launch();
|
const browser = await firefox.launch();
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage('https://www.google.com/chrome/browser/canary.html');
|
||||||
const page = await context.newPage('https://www.google.com/chrome/browser/canary.html');
|
|
||||||
dumpFrameTree(page.mainFrame(), '');
|
dumpFrameTree(page.mainFrame(), '');
|
||||||
await browser.close();
|
await browser.close();
|
||||||
|
|
||||||
|
|
@ -2117,8 +2118,7 @@ const { firefox } = require('playwright'); // Or 'chromium' or 'webkit'.
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await firefox.launch();
|
const browser = await firefox.launch();
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage();
|
||||||
const page = await context.newPage();
|
|
||||||
const watchDog = page.mainFrame().waitForFunction('window.innerWidth < 100');
|
const watchDog = page.mainFrame().waitForFunction('window.innerWidth < 100');
|
||||||
page.setViewport({width: 50, height: 50});
|
page.setViewport({width: 50, height: 50});
|
||||||
await watchDog;
|
await watchDog;
|
||||||
|
|
@ -2192,8 +2192,7 @@ const { webkit } = require('playwright'); // Or 'chromium' or 'firefox'.
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await webkit.launch();
|
const browser = await webkit.launch();
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage();
|
||||||
const page = await context.newPage();
|
|
||||||
let currentURL;
|
let currentURL;
|
||||||
page.mainFrame()
|
page.mainFrame()
|
||||||
.waitForSelector('img')
|
.waitForSelector('img')
|
||||||
|
|
@ -2216,8 +2215,7 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'.
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await chromium.launch();
|
const browser = await chromium.launch();
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage('https://example.com');
|
||||||
const page = await context.newPage('https://example.com');
|
|
||||||
const hrefElement = await page.$('a');
|
const hrefElement = await page.$('a');
|
||||||
await hrefElement.click();
|
await hrefElement.click();
|
||||||
// ...
|
// ...
|
||||||
|
|
@ -2633,8 +2631,7 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'.
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await chromium.launch();
|
const browser = await chromium.launch();
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage();
|
||||||
const page = await context.newPage();
|
|
||||||
page.on('dialog', async dialog => {
|
page.on('dialog', async dialog => {
|
||||||
console.log(dialog.message());
|
console.log(dialog.message());
|
||||||
await dialog.dismiss();
|
await dialog.dismiss();
|
||||||
|
|
@ -3137,8 +3134,7 @@ const { selectors, firefox } = require('playwright'); // Or 'chromium' or 'webk
|
||||||
await selectors.register(createTagNameEngine);
|
await selectors.register(createTagNameEngine);
|
||||||
|
|
||||||
const browser = await firefox.launch();
|
const browser = await firefox.launch();
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage('http://example.com');
|
||||||
const page = await context.newPage('http://example.com');
|
|
||||||
|
|
||||||
// Use the selector prefixed with its name.
|
// Use the selector prefixed with its name.
|
||||||
const button = await page.$('tag=button');
|
const button = await page.$('tag=button');
|
||||||
|
|
@ -3408,33 +3404,33 @@ If the function passed to the `worker.evaluateHandle` returns a [Promise], then
|
||||||
- returns: <[string]>
|
- returns: <[string]>
|
||||||
|
|
||||||
|
|
||||||
### class: BrowserApp
|
### class: BrowserServer
|
||||||
|
|
||||||
<!-- GEN:toc -->
|
<!-- GEN:toc -->
|
||||||
- [event: 'close'](#event-close-2)
|
- [event: 'close'](#event-close-2)
|
||||||
- [browserApp.close()](#browserappclose)
|
- [browserServer.close()](#browserserverclose)
|
||||||
- [browserApp.kill()](#browserappkill)
|
- [browserServer.kill()](#browserserverkill)
|
||||||
- [browserApp.process()](#browserappprocess)
|
- [browserServer.process()](#browserserverprocess)
|
||||||
- [browserApp.wsEndpoint()](#browserappwsendpoint)
|
- [browserServer.wsEndpoint()](#browserserverwsendpoint)
|
||||||
<!-- GEN:stop -->
|
<!-- GEN:stop -->
|
||||||
|
|
||||||
#### event: 'close'
|
#### event: 'close'
|
||||||
|
|
||||||
Emitted when the browser app closes.
|
Emitted when the browser app closes.
|
||||||
|
|
||||||
#### browserApp.close()
|
#### browserServer.close()
|
||||||
- returns: <[Promise]>
|
- returns: <[Promise]>
|
||||||
|
|
||||||
Closes the browser gracefully and makes sure the process is terminated.
|
Closes the browser gracefully and makes sure the process is terminated.
|
||||||
|
|
||||||
#### browserApp.kill()
|
#### browserServer.kill()
|
||||||
|
|
||||||
Kills the browser process.
|
Kills the browser process.
|
||||||
|
|
||||||
#### browserApp.process()
|
#### browserServer.process()
|
||||||
- returns: <?[ChildProcess]> Spawned browser application process.
|
- returns: <?[ChildProcess]> Spawned browser application process.
|
||||||
|
|
||||||
#### browserApp.wsEndpoint()
|
#### browserServer.wsEndpoint()
|
||||||
- returns: <[string]> Browser websocket url.
|
- returns: <[string]> Browser websocket url.
|
||||||
|
|
||||||
Browser websocket endpoint which can be used as an argument to [browserType.connect(options)](#browsertypeconnectoptions) to establish connection to the browser.
|
Browser websocket endpoint which can be used as an argument to [browserType.connect(options)](#browsertypeconnectoptions) to establish connection to the browser.
|
||||||
|
|
@ -3448,8 +3444,7 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'.
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
const browser = await chromium.launch();
|
const browser = await chromium.launch();
|
||||||
const context = await browser.newContext();
|
const page = await browser.newPage('http://example.com');
|
||||||
const page = await context.newPage('http://example.com');
|
|
||||||
// other actions...
|
// other actions...
|
||||||
await browser.close();
|
await browser.close();
|
||||||
})();
|
})();
|
||||||
|
|
@ -3462,7 +3457,8 @@ const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'.
|
||||||
- [browserType.errors](#browsertypeerrors)
|
- [browserType.errors](#browsertypeerrors)
|
||||||
- [browserType.executablePath()](#browsertypeexecutablepath)
|
- [browserType.executablePath()](#browsertypeexecutablepath)
|
||||||
- [browserType.launch([options])](#browsertypelaunchoptions)
|
- [browserType.launch([options])](#browsertypelaunchoptions)
|
||||||
- [browserType.launchBrowserApp([options])](#browsertypelaunchbrowserappoptions)
|
- [browserType.launchPersistent([options])](#browsertypelaunchpersistentoptions)
|
||||||
|
- [browserType.launchServer([options])](#browsertypelaunchserveroptions)
|
||||||
- [browserType.name()](#browsertypename)
|
- [browserType.name()](#browsertypename)
|
||||||
<!-- GEN:stop -->
|
<!-- GEN:stop -->
|
||||||
|
|
||||||
|
|
@ -3478,7 +3474,6 @@ This methods attaches Playwright to an existing browser instance.
|
||||||
- `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 browser in headless mode. More details for [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true` unless the `devtools` option is `true`.
|
- `headless` <[boolean]> Whether to run browser in headless mode. More details for [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true` unless the `devtools` option is `true`.
|
||||||
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
||||||
- `userDataDir` <[string]> Path to a [User Data Directory](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md).
|
|
||||||
- `devtools` <[boolean]> **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
|
- `devtools` <[boolean]> **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
|
||||||
- returns: <[Array]<[string]>>
|
- returns: <[Array]<[string]>>
|
||||||
|
|
||||||
|
|
@ -3487,8 +3482,7 @@ The default flags that browser will be launched with.
|
||||||
#### browserType.devices
|
#### browserType.devices
|
||||||
- returns: <[Object]>
|
- returns: <[Object]>
|
||||||
|
|
||||||
Returns a list of devices to be used with [`browser.newContext(options)`](#browsernewcontextoptions). Actual list of
|
Returns a list of devices to be used with [`browser.newContext(options)`](#browsernewcontextoptions) and [`browser.newPage(options)`](#browsernewpageoptions). Actual list of devices can be found in [src/deviceDescriptors.ts](https://github.com/Microsoft/playwright/blob/master/src/deviceDescriptors.ts).
|
||||||
devices can be found in [src/deviceDescriptors.ts](https://github.com/Microsoft/playwright/blob/master/src/deviceDescriptors.ts).
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const { webkit } = require('playwright');
|
const { webkit } = require('playwright');
|
||||||
|
|
@ -3542,9 +3536,9 @@ try {
|
||||||
- `handleSIGHUP` <[boolean]> Close the browser process on SIGHUP. Defaults to `true`.
|
- `handleSIGHUP` <[boolean]> Close the browser process on SIGHUP. Defaults to `true`.
|
||||||
- `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`.
|
||||||
- `userDataDir` <[string]> Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for [Chromium](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md) and [Firefox](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`.
|
||||||
- `devtools` <[boolean]> **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
|
- `devtools` <[boolean]> **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
|
||||||
|
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||||
- returns: <[Promise]<[Browser]>> Promise which resolves to browser instance.
|
- returns: <[Promise]<[Browser]>> Promise which resolves to browser instance.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -3563,11 +3557,11 @@ const browser = await chromium.launch({ // Or 'firefox' or 'webkit'.
|
||||||
>
|
>
|
||||||
> 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.
|
||||||
|
|
||||||
#### browserType.launchBrowserApp([options])
|
#### browserType.launchPersistent([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:
|
||||||
|
- `userDataDir` <[string]> Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for [Chromium](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md) and [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Command_Line_Options#User_Profile).
|
||||||
- `headless` <[boolean]> Whether to run browser in headless mode. More details for [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true` unless the `devtools` option is `true`.
|
- `headless` <[boolean]> Whether to run browser in headless mode. More details for [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true` unless the `devtools` option is `true`.
|
||||||
- `executablePath` <[string]> Path to a browser executable to run instead of the bundled one. 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, Firefox or WebKit, use at your own risk.
|
- `executablePath` <[string]> Path to a browser executable to run instead of the bundled one. 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, Firefox or WebKit, use at your own risk.
|
||||||
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
|
||||||
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
||||||
- `ignoreDefaultArgs` <[boolean]|[Array]<[string]>> If `true`, then do not use [`browserType.defaultArgs()`](#browsertypedefaultargsoptions). If an array is given, then filter out the given default arguments. Dangerous option; use with care. Defaults to `false`.
|
- `ignoreDefaultArgs` <[boolean]|[Array]<[string]>> If `true`, then do not use [`browserType.defaultArgs()`](#browsertypedefaultargsoptions). If an array is given, then filter out the given default arguments. Dangerous option; use with care. Defaults to `false`.
|
||||||
- `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`.
|
- `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`.
|
||||||
|
|
@ -3575,10 +3569,43 @@ const browser = await chromium.launch({ // Or 'firefox' or 'webkit'.
|
||||||
- `handleSIGHUP` <[boolean]> Close the browser process on SIGHUP. Defaults to `true`.
|
- `handleSIGHUP` <[boolean]> Close the browser process on SIGHUP. Defaults to `true`.
|
||||||
- `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`.
|
||||||
- `userDataDir` <[string]> Path to a User Data Directory, which stores browser session data like cookies and local storage. More details for [Chromium](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md) and [Firefox](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`.
|
||||||
- `devtools` <[boolean]> **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
|
- `devtools` <[boolean]> **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
|
||||||
- returns: <[Promise]<[BrowserApp]>> Promise which resolves to the browser app instance.
|
- returns: <[Promise]<[BrowserServer]>> Promise which resolves to the browser app instance.
|
||||||
|
|
||||||
|
Launches browser instance that uses persistent storage located at `userDataDir`. If `userDataDir` is not specified, temporary folder is created for the persistent storage. That folder is deleted when browser closes.
|
||||||
|
|
||||||
|
#### browserType.launchServer([options])
|
||||||
|
- `options` <[Object]> Set of configurable options to set on the browser. Can have the following fields:
|
||||||
|
- `headless` <[boolean]> Whether to run browser in headless mode. More details for [Chromium](https://developers.google.com/web/updates/2017/04/headless-chrome) and [Firefox](https://developer.mozilla.org/en-US/docs/Mozilla/Firefox/Headless_mode). Defaults to `true` unless the `devtools` option is `true`.
|
||||||
|
- `port` <[number]> Port to use for the web socket. Defaults to 0 that picks any available port.
|
||||||
|
- `executablePath` <[string]> Path to a browser executable to run instead of the bundled one. 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, Firefox or WebKit, use at your own risk.
|
||||||
|
- `args` <[Array]<[string]>> Additional arguments to pass to the browser instance. The list of Chromium flags can be found [here](http://peter.sh/experiments/chromium-command-line-switches/).
|
||||||
|
- `ignoreDefaultArgs` <[boolean]|[Array]<[string]>> If `true`, then do not use [`browserType.defaultArgs()`](#browsertypedefaultargsoptions). If an array is given, then filter out the given default arguments. Dangerous option; use with care. Defaults to `false`.
|
||||||
|
- `handleSIGINT` <[boolean]> Close the browser process on Ctrl-C. Defaults to `true`.
|
||||||
|
- `handleSIGTERM` <[boolean]> Close the browser process on SIGTERM. Defaults to `true`.
|
||||||
|
- `handleSIGHUP` <[boolean]> Close the browser process on SIGHUP. Defaults to `true`.
|
||||||
|
- `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`.
|
||||||
|
- `env` <[Object]> Specify environment variables that will be visible to the browser. Defaults to `process.env`.
|
||||||
|
- `devtools` <[boolean]> **Chromium-only** Whether to auto-open a Developer Tools panel for each tab. If this option is `true`, the `headless` option will be set `false`.
|
||||||
|
- returns: <[Promise]<[BrowserServer]>> Promise which resolves to the browser app instance.
|
||||||
|
|
||||||
|
Launches browser server that client can connect to. An example of launching a browser executable and connecting to it later:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const { chromium } = require('playwright'); // Or 'webkit' or 'firefox'.
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const browserServer = await chromium.launchServer();
|
||||||
|
const wsEndpoint = browserServer.wsEndpoint();
|
||||||
|
// Use web socket endpoint later to establish a connection.
|
||||||
|
const browser = await chromium.connect({ wsEndpoint });
|
||||||
|
// Close browser instance.
|
||||||
|
await browserServer.close();
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
#### browserType.name()
|
#### browserType.name()
|
||||||
- returns: <[string]>
|
- returns: <[string]>
|
||||||
|
|
@ -3614,10 +3641,11 @@ await browser.stopTracing();
|
||||||
- [event: 'disconnected'](#event-disconnected)
|
- [event: 'disconnected'](#event-disconnected)
|
||||||
- [browser.browserContexts()](#browserbrowsercontexts)
|
- [browser.browserContexts()](#browserbrowsercontexts)
|
||||||
- [browser.close()](#browserclose)
|
- [browser.close()](#browserclose)
|
||||||
- [browser.defaultContext()](#browserdefaultcontext)
|
|
||||||
- [browser.disconnect()](#browserdisconnect)
|
- [browser.disconnect()](#browserdisconnect)
|
||||||
- [browser.isConnected()](#browserisconnected)
|
- [browser.isConnected()](#browserisconnected)
|
||||||
- [browser.newContext(options)](#browsernewcontextoptions)
|
- [browser.newContext(options)](#browsernewcontextoptions)
|
||||||
|
- [browser.newPage(url, [options])](#browsernewpageurl-options)
|
||||||
|
- [browser.pages()](#browserpages)
|
||||||
<!-- GEN:stop -->
|
<!-- GEN:stop -->
|
||||||
|
|
||||||
#### event: 'targetchanged'
|
#### event: 'targetchanged'
|
||||||
|
|
@ -3781,10 +3809,11 @@ Firefox browser instance does not expose Firefox-specific features.
|
||||||
- [event: 'disconnected'](#event-disconnected)
|
- [event: 'disconnected'](#event-disconnected)
|
||||||
- [browser.browserContexts()](#browserbrowsercontexts)
|
- [browser.browserContexts()](#browserbrowsercontexts)
|
||||||
- [browser.close()](#browserclose)
|
- [browser.close()](#browserclose)
|
||||||
- [browser.defaultContext()](#browserdefaultcontext)
|
|
||||||
- [browser.disconnect()](#browserdisconnect)
|
- [browser.disconnect()](#browserdisconnect)
|
||||||
- [browser.isConnected()](#browserisconnected)
|
- [browser.isConnected()](#browserisconnected)
|
||||||
- [browser.newContext(options)](#browsernewcontextoptions)
|
- [browser.newContext(options)](#browsernewcontextoptions)
|
||||||
|
- [browser.newPage(url, [options])](#browsernewpageurl-options)
|
||||||
|
- [browser.pages()](#browserpages)
|
||||||
<!-- GEN:stop -->
|
<!-- GEN:stop -->
|
||||||
|
|
||||||
### class: WebKitBrowser
|
### class: WebKitBrowser
|
||||||
|
|
@ -3797,10 +3826,11 @@ WebKit browser instance does not expose WebKit-specific features.
|
||||||
- [event: 'disconnected'](#event-disconnected)
|
- [event: 'disconnected'](#event-disconnected)
|
||||||
- [browser.browserContexts()](#browserbrowsercontexts)
|
- [browser.browserContexts()](#browserbrowsercontexts)
|
||||||
- [browser.close()](#browserclose)
|
- [browser.close()](#browserclose)
|
||||||
- [browser.defaultContext()](#browserdefaultcontext)
|
|
||||||
- [browser.disconnect()](#browserdisconnect)
|
- [browser.disconnect()](#browserdisconnect)
|
||||||
- [browser.isConnected()](#browserisconnected)
|
- [browser.isConnected()](#browserisconnected)
|
||||||
- [browser.newContext(options)](#browsernewcontextoptions)
|
- [browser.newContext(options)](#browsernewcontextoptions)
|
||||||
|
- [browser.newPage(url, [options])](#browsernewpageurl-options)
|
||||||
|
- [browser.pages()](#browserpages)
|
||||||
<!-- GEN:stop -->
|
<!-- GEN:stop -->
|
||||||
|
|
||||||
### Working with selectors
|
### Working with selectors
|
||||||
|
|
@ -3874,7 +3904,7 @@ const { chromium } = require('playwright');
|
||||||
[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"
|
[BrowserServer]: #class-browserapp "BrowserServer"
|
||||||
[BrowserContext]: #class-browsercontext "BrowserContext"
|
[BrowserContext]: #class-browsercontext "BrowserContext"
|
||||||
[BrowserType]: #class-browsertype "BrowserType"
|
[BrowserType]: #class-browsertype "BrowserType"
|
||||||
[Browser]: #class-browser "Browser"
|
[Browser]: #class-browser "Browser"
|
||||||
|
|
|
||||||
|
|
@ -37,4 +37,4 @@ export { FFBrowser as FirefoxBrowser } from './firefox/ffBrowser';
|
||||||
export { WKBrowser as WebKitBrowser } from './webkit/wkBrowser';
|
export { WKBrowser as WebKitBrowser } from './webkit/wkBrowser';
|
||||||
|
|
||||||
export { BrowserType } from './server/browserType';
|
export { BrowserType } from './server/browserType';
|
||||||
export { BrowserApp } from './server/browserApp';
|
export { BrowserServer } from './server/browserServer';
|
||||||
|
|
|
||||||
|
|
@ -16,12 +16,13 @@
|
||||||
|
|
||||||
import { BrowserContext, BrowserContextOptions } from './browserContext';
|
import { BrowserContext, BrowserContextOptions } from './browserContext';
|
||||||
import * as platform from './platform';
|
import * as platform from './platform';
|
||||||
|
import { Page } from './page';
|
||||||
|
|
||||||
export interface Browser extends platform.EventEmitterType {
|
export interface Browser extends platform.EventEmitterType {
|
||||||
newContext(options?: BrowserContextOptions): Promise<BrowserContext>;
|
newContext(options?: BrowserContextOptions): Promise<BrowserContext>;
|
||||||
browserContexts(): BrowserContext[];
|
browserContexts(): BrowserContext[];
|
||||||
defaultContext(): BrowserContext;
|
pages(): Promise<Page[]>;
|
||||||
|
newPage(url?: string, options?: BrowserContextOptions): Promise<Page>;
|
||||||
disconnect(): Promise<void>;
|
disconnect(): Promise<void>;
|
||||||
isConnected(): boolean;
|
isConnected(): boolean;
|
||||||
close(): Promise<void>;
|
close(): Promise<void>;
|
||||||
|
|
@ -31,3 +32,15 @@ export type ConnectOptions = {
|
||||||
slowMo?: number,
|
slowMo?: number,
|
||||||
wsEndpoint: string
|
wsEndpoint: string
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export async function collectPages(browser: Browser): Promise<Page[]> {
|
||||||
|
const result: Promise<Page[]>[] = [];
|
||||||
|
for (const browserContext of browser.browserContexts())
|
||||||
|
result.push(browserContext.pages());
|
||||||
|
const pages: Page[] = [];
|
||||||
|
for (const group of await Promise.all(result))
|
||||||
|
pages.push(...group);
|
||||||
|
return pages;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type LaunchType = 'local' | 'server' | 'persistent';
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ 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, collectPages } 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';
|
||||||
|
|
@ -34,7 +34,7 @@ import { ConnectionTransport, SlowMoTransport } from '../transport';
|
||||||
export class CRBrowser extends platform.EventEmitter implements Browser {
|
export class CRBrowser extends platform.EventEmitter implements Browser {
|
||||||
_connection: CRConnection;
|
_connection: CRConnection;
|
||||||
_client: CRSession;
|
_client: CRSession;
|
||||||
private _defaultContext: BrowserContext;
|
readonly _defaultContext: BrowserContext;
|
||||||
private _contexts = new Map<string, BrowserContext>();
|
private _contexts = new Map<string, BrowserContext>();
|
||||||
_targets = new Map<string, CRTarget>();
|
_targets = new Map<string, CRTarget>();
|
||||||
|
|
||||||
|
|
@ -165,11 +165,16 @@ export class CRBrowser extends platform.EventEmitter implements Browser {
|
||||||
}
|
}
|
||||||
|
|
||||||
browserContexts(): BrowserContext[] {
|
browserContexts(): BrowserContext[] {
|
||||||
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
return Array.from(this._contexts.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultContext(): BrowserContext {
|
async pages(): Promise<Page[]> {
|
||||||
return this._defaultContext;
|
return collectPages(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
async newPage(url?: string, options?: BrowserContextOptions): Promise<Page> {
|
||||||
|
const browserContext = await this.newContext(options);
|
||||||
|
return browserContext.newPage(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _targetCreated(event: Protocol.Target.targetCreatedPayload) {
|
async _targetCreated(event: Protocol.Target.targetCreatedPayload) {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export const Events = {
|
||||||
Disconnected: 'disconnected'
|
Disconnected: 'disconnected'
|
||||||
},
|
},
|
||||||
|
|
||||||
BrowserApp: {
|
BrowserServer: {
|
||||||
Close: 'close',
|
Close: 'close',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Browser } from '../browser';
|
import { Browser, collectPages } 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';
|
||||||
|
|
@ -31,7 +31,7 @@ import { ConnectionTransport, SlowMoTransport } from '../transport';
|
||||||
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>;
|
||||||
private _defaultContext: BrowserContext;
|
readonly _defaultContext: BrowserContext;
|
||||||
private _contexts: Map<string, BrowserContext>;
|
private _contexts: Map<string, BrowserContext>;
|
||||||
private _eventListeners: RegisteredListener[];
|
private _eventListeners: RegisteredListener[];
|
||||||
|
|
||||||
|
|
@ -84,12 +84,17 @@ export class FFBrowser extends platform.EventEmitter implements Browser {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
browserContexts(): Array<BrowserContext> {
|
browserContexts(): BrowserContext[] {
|
||||||
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
return Array.from(this._contexts.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultContext() {
|
async pages(): Promise<Page[]> {
|
||||||
return this._defaultContext;
|
return collectPages(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
async newPage(url?: string, options?: BrowserContextOptions): Promise<Page> {
|
||||||
|
const browserContext = await this.newContext(options);
|
||||||
|
return browserContext.newPage(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _waitForTarget(predicate: (target: Target) => boolean, options: { timeout?: number; } = {}): Promise<Target> {
|
async _waitForTarget(predicate: (target: Target) => boolean, options: { timeout?: number; } = {}): Promise<Target> {
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
import { ChildProcess, execSync } from 'child_process';
|
import { ChildProcess, execSync } from 'child_process';
|
||||||
import * as platform from '../platform';
|
import * as platform from '../platform';
|
||||||
|
|
||||||
export class BrowserApp extends platform.EventEmitter {
|
export class BrowserServer extends platform.EventEmitter {
|
||||||
private _process: ChildProcess;
|
private _process: ChildProcess;
|
||||||
private _gracefullyClose: () => Promise<void>;
|
private _gracefullyClose: () => Promise<void>;
|
||||||
private _browserWSEndpoint: string | null = null;
|
private _browserWSEndpoint: string | null = null;
|
||||||
|
|
@ -17,12 +17,12 @@
|
||||||
import * as types from '../types';
|
import * as types from '../types';
|
||||||
import { TimeoutError } from '../errors';
|
import { TimeoutError } from '../errors';
|
||||||
import { Browser, ConnectOptions } from '../browser';
|
import { Browser, ConnectOptions } from '../browser';
|
||||||
import { BrowserApp } from './browserApp';
|
import { BrowserContext } from '../browserContext';
|
||||||
|
import { BrowserServer } from './browserServer';
|
||||||
|
|
||||||
export type BrowserArgOptions = {
|
export type BrowserArgOptions = {
|
||||||
headless?: boolean,
|
headless?: boolean,
|
||||||
args?: string[],
|
args?: string[],
|
||||||
userDataDir?: string,
|
|
||||||
devtools?: boolean,
|
devtools?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -40,8 +40,9 @@ export type LaunchOptions = BrowserArgOptions & {
|
||||||
export interface BrowserType {
|
export interface BrowserType {
|
||||||
executablePath(): string;
|
executablePath(): string;
|
||||||
name(): string;
|
name(): string;
|
||||||
launchBrowserApp(options?: LaunchOptions): Promise<BrowserApp>;
|
|
||||||
launch(options?: LaunchOptions & { slowMo?: number }): Promise<Browser>;
|
launch(options?: LaunchOptions & { slowMo?: number }): Promise<Browser>;
|
||||||
|
launchServer(options?: LaunchOptions & { port?: number }): Promise<BrowserServer>;
|
||||||
|
launchPersistent(options?: LaunchOptions & { userDataDir: string }): Promise<BrowserContext>;
|
||||||
defaultArgs(options?: BrowserArgOptions): string[];
|
defaultArgs(options?: BrowserArgOptions): string[];
|
||||||
connect(options: ConnectOptions): Promise<Browser>;
|
connect(options: ConnectOptions): Promise<Browser>;
|
||||||
devices: types.Devices;
|
devices: types.Devices;
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,11 @@ import { launchProcess, waitForLine } from '../server/processLauncher';
|
||||||
import { kBrowserCloseMessageId } from '../chromium/crConnection';
|
import { kBrowserCloseMessageId } from '../chromium/crConnection';
|
||||||
import { PipeTransport } from './pipeTransport';
|
import { PipeTransport } from './pipeTransport';
|
||||||
import { LaunchOptions, BrowserArgOptions, BrowserType } from './browserType';
|
import { LaunchOptions, BrowserArgOptions, BrowserType } from './browserType';
|
||||||
import { ConnectOptions } from '../browser';
|
import { ConnectOptions, LaunchType } from '../browser';
|
||||||
import { BrowserApp } from './browserApp';
|
import { BrowserServer } from './browserServer';
|
||||||
import { Events } from '../events';
|
import { Events } from '../events';
|
||||||
import { ConnectionTransport } from '../transport';
|
import { ConnectionTransport } from '../transport';
|
||||||
|
import { BrowserContext } from '../browserContext';
|
||||||
|
|
||||||
export class Chromium implements BrowserType {
|
export class Chromium implements BrowserType {
|
||||||
private _projectRoot: string;
|
private _projectRoot: string;
|
||||||
|
|
@ -49,19 +50,28 @@ export class Chromium implements BrowserType {
|
||||||
}
|
}
|
||||||
|
|
||||||
async launch(options?: LaunchOptions & { slowMo?: number }): Promise<CRBrowser> {
|
async launch(options?: LaunchOptions & { slowMo?: number }): Promise<CRBrowser> {
|
||||||
const { browserApp, transport } = await this._launchBrowserApp(options, false);
|
const { browserServer, transport } = await this._launchServer(options, 'local');
|
||||||
const browser = await CRBrowser.connect(transport!, options && options.slowMo);
|
const browser = await CRBrowser.connect(transport!, options && options.slowMo);
|
||||||
// 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 = () => browserApp.close();
|
browser.close = () => browserServer.close();
|
||||||
(browser as any)['__app__'] = browserApp;
|
(browser as any)['__server__'] = browserServer;
|
||||||
return browser;
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
async launchBrowserApp(options?: LaunchOptions): Promise<BrowserApp> {
|
async launchServer(options?: LaunchOptions & { port?: number }): Promise<BrowserServer> {
|
||||||
return (await this._launchBrowserApp(options, true)).browserApp;
|
return (await this._launchServer(options, 'server', undefined, options && options.port)).browserServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _launchBrowserApp(options: LaunchOptions = {}, isServer: boolean): Promise<{ browserApp: BrowserApp, transport?: ConnectionTransport }> {
|
async launchPersistent(options?: LaunchOptions & { userDataDir?: string }): Promise<BrowserContext> {
|
||||||
|
const { browserServer, transport } = await this._launchServer(options, 'persistent', options && options.userDataDir);
|
||||||
|
const browser = await CRBrowser.connect(transport!);
|
||||||
|
// Hack: for typical launch scenario, ensure that close waits for actual process termination.
|
||||||
|
const browserContext = browser._defaultContext;
|
||||||
|
browserContext.close = () => browserServer.close();
|
||||||
|
return browserContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _launchServer(options: LaunchOptions = {}, launchType: LaunchType, userDataDir?: string, port?: number): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport }> {
|
||||||
const {
|
const {
|
||||||
ignoreDefaultArgs = false,
|
ignoreDefaultArgs = false,
|
||||||
args = [],
|
args = [],
|
||||||
|
|
@ -82,14 +92,19 @@ export class Chromium implements BrowserType {
|
||||||
else
|
else
|
||||||
chromeArguments.push(...args);
|
chromeArguments.push(...args);
|
||||||
|
|
||||||
let temporaryUserDataDir: string | null = null;
|
const userDataDirArg = chromeArguments.find(arg => arg.startsWith('--user-data-dir='));
|
||||||
|
if (userDataDirArg)
|
||||||
|
throw new Error('Pass userDataDir parameter instead of specifying --user-data-dir argument');
|
||||||
|
if (chromeArguments.find(arg => arg.startsWith('--remote-debugging-')))
|
||||||
|
throw new Error('Can\' use --remote-debugging-* args. Playwright manages remote debugging connection itself');
|
||||||
|
|
||||||
if (!chromeArguments.some(argument => argument.startsWith('--remote-debugging-')))
|
let temporaryUserDataDir: string | null = null;
|
||||||
chromeArguments.push(isServer ? '--remote-debugging-port=0' : '--remote-debugging-pipe');
|
if (!userDataDir) {
|
||||||
if (!chromeArguments.some(arg => arg.startsWith('--user-data-dir'))) {
|
userDataDir = await mkdtempAsync(CHROMIUM_PROFILE_PATH);
|
||||||
temporaryUserDataDir = await mkdtempAsync(CHROMIUM_PROFILE_PATH);
|
temporaryUserDataDir = await mkdtempAsync(CHROMIUM_PROFILE_PATH);
|
||||||
chromeArguments.push(`--user-data-dir=${temporaryUserDataDir}`);
|
|
||||||
}
|
}
|
||||||
|
chromeArguments.push(`--user-data-dir=${userDataDir}`);
|
||||||
|
chromeArguments.push(launchType === 'server' ? `--remote-debugging-port=${port || 0}` : '--remote-debugging-pipe');
|
||||||
|
|
||||||
let chromeExecutable = executablePath;
|
let chromeExecutable = executablePath;
|
||||||
if (!executablePath) {
|
if (!executablePath) {
|
||||||
|
|
@ -99,11 +114,7 @@ export class Chromium implements BrowserType {
|
||||||
chromeExecutable = executablePath;
|
chromeExecutable = executablePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
const usePipe = chromeArguments.includes('--remote-debugging-pipe');
|
let browserServer: BrowserServer | undefined = undefined;
|
||||||
if (usePipe && isServer)
|
|
||||||
throw new Error(`Argument "--remote-debugging-pipe" is not compatible with the launchBrowserApp.`);
|
|
||||||
|
|
||||||
let browserApp: BrowserApp | undefined = undefined;
|
|
||||||
const { launchedProcess, gracefullyClose } = await launchProcess({
|
const { launchedProcess, gracefullyClose } = await launchProcess({
|
||||||
executablePath: chromeExecutable!,
|
executablePath: chromeExecutable!,
|
||||||
args: chromeArguments,
|
args: chromeArguments,
|
||||||
|
|
@ -112,10 +123,10 @@ export class Chromium implements BrowserType {
|
||||||
handleSIGTERM,
|
handleSIGTERM,
|
||||||
handleSIGHUP,
|
handleSIGHUP,
|
||||||
dumpio,
|
dumpio,
|
||||||
pipe: usePipe,
|
pipe: launchType !== 'server',
|
||||||
tempDir: temporaryUserDataDir || undefined,
|
tempDir: temporaryUserDataDir || undefined,
|
||||||
attemptToGracefullyClose: async () => {
|
attemptToGracefullyClose: async () => {
|
||||||
if (!browserApp)
|
if (!browserServer)
|
||||||
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
|
// Note that it's fine to reuse the pipe transport, since
|
||||||
|
|
@ -125,14 +136,14 @@ export class Chromium implements BrowserType {
|
||||||
t.send(JSON.stringify(message));
|
t.send(JSON.stringify(message));
|
||||||
},
|
},
|
||||||
onkill: (exitCode, signal) => {
|
onkill: (exitCode, signal) => {
|
||||||
if (browserApp)
|
if (browserServer)
|
||||||
browserApp.emit(Events.BrowserApp.Close, exitCode, signal);
|
browserServer.emit(Events.BrowserServer.Close, exitCode, signal);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let transport: ConnectionTransport | undefined;
|
let transport: ConnectionTransport | undefined;
|
||||||
let browserWSEndpoint: string | null;
|
let browserWSEndpoint: string | null;
|
||||||
if (isServer) {
|
if (launchType === 'server') {
|
||||||
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);
|
||||||
browserWSEndpoint = match[1];
|
browserWSEndpoint = match[1];
|
||||||
|
|
@ -140,8 +151,8 @@ export class Chromium implements BrowserType {
|
||||||
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);
|
||||||
browserWSEndpoint = null;
|
browserWSEndpoint = null;
|
||||||
}
|
}
|
||||||
browserApp = new BrowserApp(launchedProcess, gracefullyClose, browserWSEndpoint);
|
browserServer = new BrowserServer(launchedProcess, gracefullyClose, browserWSEndpoint);
|
||||||
return { browserApp, transport };
|
return { browserServer, transport };
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(options: ConnectOptions): Promise<CRBrowser> {
|
async connect(options: ConnectOptions): Promise<CRBrowser> {
|
||||||
|
|
@ -166,11 +177,8 @@ export class Chromium implements BrowserType {
|
||||||
devtools = false,
|
devtools = false,
|
||||||
headless = !devtools,
|
headless = !devtools,
|
||||||
args = [],
|
args = [],
|
||||||
userDataDir = null
|
|
||||||
} = options;
|
} = options;
|
||||||
const chromeArguments = [...DEFAULT_ARGS];
|
const chromeArguments = [...DEFAULT_ARGS];
|
||||||
if (userDataDir)
|
|
||||||
chromeArguments.push(`--user-data-dir=${userDataDir}`);
|
|
||||||
if (devtools)
|
if (devtools)
|
||||||
chromeArguments.push('--auto-open-devtools-for-tabs');
|
chromeArguments.push('--auto-open-devtools-for-tabs');
|
||||||
if (headless) {
|
if (headless) {
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,11 @@ import * as util from 'util';
|
||||||
import { TimeoutError } from '../errors';
|
import { TimeoutError } from '../errors';
|
||||||
import { assert } from '../helper';
|
import { assert } from '../helper';
|
||||||
import { LaunchOptions, BrowserArgOptions, BrowserType } from './browserType';
|
import { LaunchOptions, BrowserArgOptions, BrowserType } from './browserType';
|
||||||
import { ConnectOptions } from '../browser';
|
import { ConnectOptions, LaunchType } from '../browser';
|
||||||
import { BrowserApp } from './browserApp';
|
import { BrowserServer } from './browserServer';
|
||||||
import { Events } from '../events';
|
import { Events } from '../events';
|
||||||
import { ConnectionTransport } from '../transport';
|
import { ConnectionTransport } from '../transport';
|
||||||
|
import { BrowserContext } from '../browserContext';
|
||||||
|
|
||||||
export class Firefox implements BrowserType {
|
export class Firefox implements BrowserType {
|
||||||
private _projectRoot: string;
|
private _projectRoot: string;
|
||||||
|
|
@ -48,19 +49,28 @@ export class Firefox implements BrowserType {
|
||||||
}
|
}
|
||||||
|
|
||||||
async launch(options?: LaunchOptions & { slowMo?: number }): Promise<FFBrowser> {
|
async launch(options?: LaunchOptions & { slowMo?: number }): Promise<FFBrowser> {
|
||||||
const { browserApp, transport } = await this._launchBrowserApp(options, false);
|
const { browserServer, transport } = await this._launchServer(options, 'local');
|
||||||
const browser = await FFBrowser.connect(transport!, options && options.slowMo);
|
const browser = await FFBrowser.connect(transport!, options && options.slowMo);
|
||||||
// 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 = () => browserApp.close();
|
browser.close = () => browserServer.close();
|
||||||
(browser as any)['__app__'] = browserApp;
|
(browser as any)['__server__'] = browserServer;
|
||||||
return browser;
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
async launchBrowserApp(options?: LaunchOptions): Promise<BrowserApp> {
|
async launchServer(options?: LaunchOptions & { port?: number }): Promise<BrowserServer> {
|
||||||
return (await this._launchBrowserApp(options, true)).browserApp;
|
return (await this._launchServer(options, 'server', undefined, options && options.port)).browserServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _launchBrowserApp(options: LaunchOptions = {}, isServer: boolean): Promise<{ browserApp: BrowserApp, transport?: ConnectionTransport }> {
|
async launchPersistent(options?: LaunchOptions & { userDataDir?: string }): Promise<BrowserContext> {
|
||||||
|
const { browserServer, transport } = await this._launchServer(options, 'persistent', options && options.userDataDir);
|
||||||
|
const browser = await FFBrowser.connect(transport!);
|
||||||
|
// Hack: for typical launch scenario, ensure that close waits for actual process termination.
|
||||||
|
const browserContext = browser._defaultContext;
|
||||||
|
browserContext.close = () => browserServer.close();
|
||||||
|
return browserContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _launchServer(options: LaunchOptions = {}, launchType: LaunchType, userDataDir?: string, port?: number): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport }> {
|
||||||
const {
|
const {
|
||||||
ignoreDefaultArgs = false,
|
ignoreDefaultArgs = false,
|
||||||
args = [],
|
args = [],
|
||||||
|
|
@ -81,14 +91,20 @@ export class Firefox implements BrowserType {
|
||||||
else
|
else
|
||||||
firefoxArguments.push(...args);
|
firefoxArguments.push(...args);
|
||||||
|
|
||||||
if (!firefoxArguments.includes('-juggler'))
|
const userDataDirArg = firefoxArguments.find(arg => arg.startsWith('-profile') || arg.startsWith('--profile'));
|
||||||
firefoxArguments.unshift('-juggler', '0');
|
if (userDataDirArg)
|
||||||
|
throw new Error('Pass userDataDir parameter instead of specifying -profile argument');
|
||||||
|
|
||||||
let temporaryProfileDir = null;
|
let temporaryProfileDir = null;
|
||||||
if (!firefoxArguments.includes('-profile') && !firefoxArguments.includes('--profile')) {
|
if (!userDataDir) {
|
||||||
temporaryProfileDir = await createProfile();
|
userDataDir = await createProfile();
|
||||||
firefoxArguments.unshift(`-profile`, temporaryProfileDir);
|
temporaryProfileDir = userDataDirArg;
|
||||||
}
|
}
|
||||||
|
firefoxArguments.unshift(`-profile`, userDataDir);
|
||||||
|
|
||||||
|
if (firefoxArguments.find(arg => arg.startsWith('-juggler')))
|
||||||
|
throw new Error('Use the port parameter instead of -juggler argument');
|
||||||
|
firefoxArguments.unshift('-juggler', String(port || 0));
|
||||||
|
|
||||||
let firefoxExecutable = executablePath;
|
let firefoxExecutable = executablePath;
|
||||||
if (!firefoxExecutable) {
|
if (!firefoxExecutable) {
|
||||||
|
|
@ -98,7 +114,7 @@ export class Firefox implements BrowserType {
|
||||||
firefoxExecutable = executablePath;
|
firefoxExecutable = executablePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
let browserApp: BrowserApp | undefined = undefined;
|
let browserServer: BrowserServer | undefined = undefined;
|
||||||
const { launchedProcess, gracefullyClose } = await launchProcess({
|
const { launchedProcess, gracefullyClose } = await launchProcess({
|
||||||
executablePath: firefoxExecutable,
|
executablePath: firefoxExecutable,
|
||||||
args: firefoxArguments,
|
args: firefoxArguments,
|
||||||
|
|
@ -114,7 +130,7 @@ export class Firefox implements BrowserType {
|
||||||
pipe: false,
|
pipe: false,
|
||||||
tempDir: temporaryProfileDir || undefined,
|
tempDir: temporaryProfileDir || undefined,
|
||||||
attemptToGracefullyClose: async () => {
|
attemptToGracefullyClose: async () => {
|
||||||
if (!browserApp)
|
if (!browserServer)
|
||||||
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
|
// Note that it's fine to reuse the pipe transport, since
|
||||||
|
|
@ -124,16 +140,16 @@ export class Firefox implements BrowserType {
|
||||||
transport.send(JSON.stringify(message));
|
transport.send(JSON.stringify(message));
|
||||||
},
|
},
|
||||||
onkill: (exitCode, signal) => {
|
onkill: (exitCode, signal) => {
|
||||||
if (browserApp)
|
if (browserServer)
|
||||||
browserApp.emit(Events.BrowserApp.Close, exitCode, signal);
|
browserServer.emit(Events.BrowserServer.Close, exitCode, signal);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
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 browserWSEndpoint = match[1];
|
const browserWSEndpoint = match[1];
|
||||||
browserApp = new BrowserApp(launchedProcess, gracefullyClose, isServer ? browserWSEndpoint : null);
|
browserServer = new BrowserServer(launchedProcess, gracefullyClose, launchType === 'server' ? browserWSEndpoint : null);
|
||||||
return { browserApp, transport: isServer ? undefined : await platform.createWebSocketTransport(browserWSEndpoint) };
|
return { browserServer, transport: launchType === 'server' ? undefined : await platform.createWebSocketTransport(browserWSEndpoint) };
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(options: ConnectOptions): Promise<FFBrowser> {
|
async connect(options: ConnectOptions): Promise<FFBrowser> {
|
||||||
|
|
@ -158,13 +174,10 @@ export class Firefox implements BrowserType {
|
||||||
devtools = false,
|
devtools = false,
|
||||||
headless = !devtools,
|
headless = !devtools,
|
||||||
args = [],
|
args = [],
|
||||||
userDataDir = null,
|
|
||||||
} = options;
|
} = options;
|
||||||
if (devtools)
|
if (devtools)
|
||||||
throw new Error('Option "devtools" is not supported by Firefox');
|
throw new Error('Option "devtools" is not supported by Firefox');
|
||||||
const firefoxArguments = [...DEFAULT_ARGS];
|
const firefoxArguments = [...DEFAULT_ARGS];
|
||||||
if (userDataDir)
|
|
||||||
firefoxArguments.push('-profile', userDataDir);
|
|
||||||
if (headless)
|
if (headless)
|
||||||
firefoxArguments.push('-headless');
|
firefoxArguments.push('-headless');
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,10 @@ import { LaunchOptions, BrowserArgOptions, BrowserType } from './browserType';
|
||||||
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 { ConnectOptions, LaunchType } from '../browser';
|
||||||
import { BrowserApp } from './browserApp';
|
import { BrowserServer } from './browserServer';
|
||||||
import { Events } from '../events';
|
import { Events } from '../events';
|
||||||
|
import { BrowserContext } from '../browserContext';
|
||||||
|
|
||||||
export class WebKit implements BrowserType {
|
export class WebKit implements BrowserType {
|
||||||
private _projectRoot: string;
|
private _projectRoot: string;
|
||||||
|
|
@ -52,19 +53,28 @@ export class WebKit implements BrowserType {
|
||||||
}
|
}
|
||||||
|
|
||||||
async launch(options?: LaunchOptions & { slowMo?: number }): Promise<WKBrowser> {
|
async launch(options?: LaunchOptions & { slowMo?: number }): Promise<WKBrowser> {
|
||||||
const { browserApp, transport } = await this._launchBrowserApp(options, false);
|
const { browserServer, transport } = await this._launchServer(options, 'local', null);
|
||||||
const browser = await WKBrowser.connect(transport!, options && options.slowMo);
|
const browser = await WKBrowser.connect(transport!, options && options.slowMo);
|
||||||
// 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 = () => browserApp.close();
|
browser.close = () => browserServer.close();
|
||||||
(browser as any)['__app__'] = browserApp;
|
(browser as any)['__server__'] = browserServer;
|
||||||
return browser;
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
async launchBrowserApp(options?: LaunchOptions): Promise<BrowserApp> {
|
async launchServer(options?: LaunchOptions & { port?: number }): Promise<BrowserServer> {
|
||||||
return (await this._launchBrowserApp(options, true)).browserApp;
|
return (await this._launchServer(options, 'server', undefined, options && options.port)).browserServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _launchBrowserApp(options: LaunchOptions = {}, isServer: boolean): Promise<{ browserApp: BrowserApp, transport?: ConnectionTransport }> {
|
async launchPersistent(options?: LaunchOptions & { userDataDir?: string }): Promise<BrowserContext> {
|
||||||
|
const { browserServer, transport } = await this._launchServer(options, 'persistent', options && options.userDataDir);
|
||||||
|
const browser = await WKBrowser.connect(transport!);
|
||||||
|
// Hack: for typical launch scenario, ensure that close waits for actual process termination.
|
||||||
|
const browserContext = browser._defaultContext;
|
||||||
|
browserContext.close = () => browserServer.close();
|
||||||
|
return browserContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _launchServer(options: LaunchOptions = {}, launchType: LaunchType, userDataDir?: string, port?: number): Promise<{ browserServer: BrowserServer, transport?: ConnectionTransport }> {
|
||||||
const {
|
const {
|
||||||
ignoreDefaultArgs = false,
|
ignoreDefaultArgs = false,
|
||||||
args = [],
|
args = [],
|
||||||
|
|
@ -84,16 +94,16 @@ export class WebKit implements BrowserType {
|
||||||
else
|
else
|
||||||
webkitArguments.push(...args);
|
webkitArguments.push(...args);
|
||||||
|
|
||||||
let userDataDir: string;
|
|
||||||
let temporaryUserDataDir: string | null = null;
|
|
||||||
const userDataDirArg = webkitArguments.find(arg => arg.startsWith('--user-data-dir='));
|
const userDataDirArg = webkitArguments.find(arg => arg.startsWith('--user-data-dir='));
|
||||||
if (userDataDirArg) {
|
if (userDataDirArg)
|
||||||
userDataDir = userDataDirArg.substr('--user-data-dir='.length).trim();
|
throw new Error('Pass userDataDir parameter instead of specifying --user-data-dir argument');
|
||||||
} else {
|
|
||||||
|
let temporaryUserDataDir: string | null = null;
|
||||||
|
if (!userDataDir) {
|
||||||
userDataDir = await mkdtempAsync(WEBKIT_PROFILE_PATH);
|
userDataDir = await mkdtempAsync(WEBKIT_PROFILE_PATH);
|
||||||
temporaryUserDataDir = userDataDir;
|
temporaryUserDataDir = userDataDir!;
|
||||||
webkitArguments.push(`--user-data-dir=${temporaryUserDataDir}`);
|
|
||||||
}
|
}
|
||||||
|
webkitArguments.push(`--user-data-dir=${userDataDir}`);
|
||||||
|
|
||||||
let webkitExecutable = executablePath;
|
let webkitExecutable = executablePath;
|
||||||
if (!executablePath) {
|
if (!executablePath) {
|
||||||
|
|
@ -104,11 +114,11 @@ export class WebKit implements BrowserType {
|
||||||
}
|
}
|
||||||
|
|
||||||
let transport: PipeTransport | undefined = undefined;
|
let transport: PipeTransport | undefined = undefined;
|
||||||
let browserApp: BrowserApp | undefined = undefined;
|
let browserServer: BrowserServer | undefined = undefined;
|
||||||
const { launchedProcess, gracefullyClose } = await launchProcess({
|
const { launchedProcess, gracefullyClose } = await launchProcess({
|
||||||
executablePath: webkitExecutable!,
|
executablePath: webkitExecutable!,
|
||||||
args: webkitArguments,
|
args: webkitArguments,
|
||||||
env: { ...env, CURL_COOKIE_JAR_PATH: path.join(userDataDir, 'cookiejar.db') },
|
env: { ...env, CURL_COOKIE_JAR_PATH: path.join(userDataDir!, 'cookiejar.db') },
|
||||||
handleSIGINT,
|
handleSIGINT,
|
||||||
handleSIGTERM,
|
handleSIGTERM,
|
||||||
handleSIGHUP,
|
handleSIGHUP,
|
||||||
|
|
@ -125,14 +135,14 @@ export class WebKit implements BrowserType {
|
||||||
transport.send(message);
|
transport.send(message);
|
||||||
},
|
},
|
||||||
onkill: (exitCode, signal) => {
|
onkill: (exitCode, signal) => {
|
||||||
if (browserApp)
|
if (browserServer)
|
||||||
browserApp.emit(Events.BrowserApp.Close, exitCode, signal);
|
browserServer.emit(Events.BrowserServer.Close, exitCode, signal);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
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);
|
||||||
browserApp = new BrowserApp(launchedProcess, gracefullyClose, isServer ? wrapTransportWithWebSocket(transport) : null);
|
browserServer = new BrowserServer(launchedProcess, gracefullyClose, launchType === 'server' ? wrapTransportWithWebSocket(transport, port || 0) : null);
|
||||||
return { browserApp, transport };
|
return { browserServer, transport };
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(options: ConnectOptions): Promise<WKBrowser> {
|
async connect(options: ConnectOptions): Promise<WKBrowser> {
|
||||||
|
|
@ -157,13 +167,10 @@ export class WebKit implements BrowserType {
|
||||||
devtools = false,
|
devtools = false,
|
||||||
headless = !devtools,
|
headless = !devtools,
|
||||||
args = [],
|
args = [],
|
||||||
userDataDir = null
|
|
||||||
} = options;
|
} = options;
|
||||||
if (devtools)
|
if (devtools)
|
||||||
throw new Error('Option "devtools" is not supported by WebKit');
|
throw new Error('Option "devtools" is not supported by WebKit');
|
||||||
const webkitArguments = ['--inspector-pipe'];
|
const webkitArguments = ['--inspector-pipe'];
|
||||||
if (userDataDir)
|
|
||||||
webkitArguments.push(`--user-data-dir=${userDataDir}`);
|
|
||||||
if (headless)
|
if (headless)
|
||||||
webkitArguments.push('--headless');
|
webkitArguments.push('--headless');
|
||||||
webkitArguments.push(...args);
|
webkitArguments.push(...args);
|
||||||
|
|
@ -230,8 +237,8 @@ function getMacVersion(): string {
|
||||||
return cachedMacVersion;
|
return cachedMacVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
function wrapTransportWithWebSocket(transport: ConnectionTransport) {
|
function wrapTransportWithWebSocket(transport: ConnectionTransport, port: number) {
|
||||||
const server = new ws.Server({ port: 0 });
|
const server = new ws.Server({ port });
|
||||||
let socket: ws | undefined;
|
let socket: ws | undefined;
|
||||||
const guid = uuidv4();
|
const guid = uuidv4();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Browser } from '../browser';
|
import { Browser, collectPages } 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';
|
||||||
|
|
@ -33,7 +33,7 @@ const DEFAULT_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_2) Appl
|
||||||
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;
|
||||||
private readonly _defaultContext: BrowserContext;
|
readonly _defaultContext: BrowserContext;
|
||||||
private readonly _contexts = new Map<string, BrowserContext>();
|
private readonly _contexts = new Map<string, BrowserContext>();
|
||||||
private readonly _pageProxies = new Map<string, WKPageProxy>();
|
private readonly _pageProxies = new Map<string, WKPageProxy>();
|
||||||
private readonly _eventListeners: RegisteredListener[];
|
private readonly _eventListeners: RegisteredListener[];
|
||||||
|
|
@ -84,11 +84,16 @@ export class WKBrowser extends platform.EventEmitter implements Browser {
|
||||||
}
|
}
|
||||||
|
|
||||||
browserContexts(): BrowserContext[] {
|
browserContexts(): BrowserContext[] {
|
||||||
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
return Array.from(this._contexts.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultContext(): BrowserContext {
|
async pages(): Promise<Page[]> {
|
||||||
return this._defaultContext;
|
return collectPages(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
async newPage(url?: string, options?: BrowserContextOptions): Promise<Page> {
|
||||||
|
const browserContext = await this.newContext(options);
|
||||||
|
return browserContext.newPage(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
async _waitForFirstPageTarget(timeout: number): Promise<void> {
|
async _waitForFirstPageTarget(timeout: number): Promise<void> {
|
||||||
|
|
|
||||||
46
test/browser.spec.js
Normal file
46
test/browser.spec.js
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
/**
|
||||||
|
* Copyright 2020 Microsoft Corporation. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const utils = require('./utils');
|
||||||
|
|
||||||
|
module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WEBKIT}) {
|
||||||
|
const {describe, xdescribe, fdescribe} = testRunner;
|
||||||
|
const {it, fit, xit, dit} = testRunner;
|
||||||
|
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
||||||
|
|
||||||
|
describe('Browser', function() {
|
||||||
|
it('should create new page', async function({browser}) {
|
||||||
|
expect((await browser.pages()).length).toBe(0);
|
||||||
|
const page1 = await browser.newPage();
|
||||||
|
expect((await browser.pages()).length).toBe(1);
|
||||||
|
expect(browser.browserContexts().length).toBe(1);
|
||||||
|
|
||||||
|
const page2 = await browser.newPage();
|
||||||
|
expect((await browser.pages()).length).toBe(2);
|
||||||
|
expect(browser.browserContexts().length).toBe(2);
|
||||||
|
|
||||||
|
await page1.close();
|
||||||
|
expect((await browser.pages()).length).toBe(1);
|
||||||
|
expect(browser.browserContexts().length).toBe(2);
|
||||||
|
|
||||||
|
await page2.browserContext().close();
|
||||||
|
expect((await browser.pages()).length).toBe(0);
|
||||||
|
expect(browser.browserContexts().length).toBe(1);
|
||||||
|
await page1.browserContext().close();
|
||||||
|
expect(browser.browserContexts().length).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
@ -23,21 +23,13 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WE
|
||||||
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
||||||
|
|
||||||
describe('BrowserContext', function() {
|
describe('BrowserContext', function() {
|
||||||
it('should have default context', async function({browser, server}) {
|
it('should create new context', async function({browser, newContext}) {
|
||||||
expect(browser.browserContexts().length).toBe(1);
|
expect(browser.browserContexts().length).toBe(0);
|
||||||
const defaultContext = browser.browserContexts()[0];
|
|
||||||
let error = null;
|
|
||||||
await defaultContext.close().catch(e => error = e);
|
|
||||||
expect(browser.defaultContext()).toBe(defaultContext);
|
|
||||||
expect(error.message).toContain('cannot be closed');
|
|
||||||
});
|
|
||||||
it('should create new incognito context', async function({browser, newContext}) {
|
|
||||||
expect(browser.browserContexts().length).toBe(1);
|
|
||||||
const context = await newContext();
|
const context = await newContext();
|
||||||
expect(browser.browserContexts().length).toBe(2);
|
expect(browser.browserContexts().length).toBe(1);
|
||||||
expect(browser.browserContexts().indexOf(context) !== -1).toBe(true);
|
expect(browser.browserContexts().indexOf(context) !== -1).toBe(true);
|
||||||
await context.close();
|
await context.close();
|
||||||
expect(browser.browserContexts().length).toBe(1);
|
expect(browser.browserContexts().length).toBe(0);
|
||||||
});
|
});
|
||||||
it('window.open should use parent tab context', async function({newContext, server}) {
|
it('window.open should use parent tab context', async function({newContext, server}) {
|
||||||
const context = await newContext();
|
const context = await newContext();
|
||||||
|
|
@ -91,7 +83,7 @@ module.exports.describe = function({testRunner, expect, playwright, CHROMIUM, WE
|
||||||
context1.close(),
|
context1.close(),
|
||||||
context2.close()
|
context2.close()
|
||||||
]);
|
]);
|
||||||
expect(browser.browserContexts().length).toBe(1);
|
expect(browser.browserContexts().length).toBe(0);
|
||||||
});
|
});
|
||||||
it('should propagate default viewport to the page', async({ newPage }) => {
|
it('should propagate default viewport to the page', async({ newPage }) => {
|
||||||
const page = await newPage({ viewport: { width: 456, height: 789 } });
|
const page = await newPage({ viewport: { width: 456, height: 789 } });
|
||||||
|
|
|
||||||
|
|
@ -41,12 +41,6 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
|
||||||
const browserTarget = targets.find(target => target.type() === 'browser');
|
const browserTarget = targets.find(target => target.type() === 'browser');
|
||||||
expect(browserTarget).toBe(browser.browserTarget());
|
expect(browserTarget).toBe(browser.browserTarget());
|
||||||
});
|
});
|
||||||
it('should be able to use the default page in the browser', async({page, server, browser}) => {
|
|
||||||
// The pages will be the testing page and the original newtab page
|
|
||||||
const originalPage = (await browser.defaultContext().pages())[0];
|
|
||||||
expect(await originalPage.evaluate(() => ['Hello', 'world'].join(' '))).toBe('Hello world');
|
|
||||||
expect(await originalPage.$('body')).toBeTruthy();
|
|
||||||
});
|
|
||||||
it('should report when a new page is created and closed', async({browser, page, server, context}) => {
|
it('should report when a new page is created and closed', async({browser, page, server, context}) => {
|
||||||
const [otherPage] = await Promise.all([
|
const [otherPage] = await Promise.all([
|
||||||
browser.waitForTarget(target => target.url() === server.CROSS_PROCESS_PREFIX + '/empty.html').then(target => target.page()),
|
browser.waitForTarget(target => target.url() === server.CROSS_PROCESS_PREFIX + '/empty.html').then(target => target.page()),
|
||||||
|
|
@ -182,30 +176,21 @@ module.exports.describe = function({testRunner, expect, playwright, FFOX, CHROMI
|
||||||
const target = await targetPromise;
|
const target = await targetPromise;
|
||||||
expect(await target.page()).toBe(page);
|
expect(await target.page()).toBe(page);
|
||||||
});
|
});
|
||||||
it('should timeout waiting for a non-existent target', async function({browser, server, newContext}) {
|
it('should timeout waiting for a non-existent target', async function({browser, context, server}) {
|
||||||
const context = await newContext();
|
|
||||||
const error = await browser.waitForTarget(target => target.browserContext() === context && target.url() === server.EMPTY_PAGE, {timeout: 1}).catch(e => e);
|
const error = await browser.waitForTarget(target => target.browserContext() === context && target.url() === server.EMPTY_PAGE, {timeout: 1}).catch(e => e);
|
||||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
||||||
await context.close();
|
|
||||||
});
|
});
|
||||||
it('should wait for a target', async function({browser, server}) {
|
it('should wait for a target', async function({browser, server}) {
|
||||||
let resolved = false;
|
let resolved = false;
|
||||||
const targetPromise = browser.waitForTarget(target => target.url() === server.EMPTY_PAGE);
|
const targetPromise = browser.waitForTarget(target => target.url() === server.EMPTY_PAGE);
|
||||||
targetPromise.then(() => resolved = true);
|
targetPromise.then(() => resolved = true);
|
||||||
const page = await browser.defaultContext().newPage();
|
const page = await browser.newPage();
|
||||||
expect(resolved).toBe(false);
|
expect(resolved).toBe(false);
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
const target = await targetPromise;
|
const target = await targetPromise;
|
||||||
expect(await target.page()).toBe(page);
|
expect(await target.page()).toBe(page);
|
||||||
await page.close();
|
await page.close();
|
||||||
});
|
});
|
||||||
it('should timeout waiting for a non-existent target', async function({browser, server}) {
|
|
||||||
let error = null;
|
|
||||||
await browser.waitForTarget(target => target.url() === server.EMPTY_PAGE, {
|
|
||||||
timeout: 1
|
|
||||||
}).catch(e => error = e);
|
|
||||||
expect(error).toBeInstanceOf(playwright.errors.TimeoutError);
|
|
||||||
});
|
|
||||||
it('should fire target events', async function({browser, newContext, server}) {
|
it('should fire target events', async function({browser, newContext, server}) {
|
||||||
const context = await newContext();
|
const context = await newContext();
|
||||||
const events = [];
|
const events = [];
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ module.exports.describe = function({testRunner, expect, playwright, defaultBrows
|
||||||
describe('ChromiumHeadful', function() {
|
describe('ChromiumHeadful', function() {
|
||||||
it('background_page target type should be available', async() => {
|
it('background_page target type should be available', async() => {
|
||||||
const browserWithExtension = await playwright.launch(extensionOptions);
|
const browserWithExtension = await playwright.launch(extensionOptions);
|
||||||
const page = await browserWithExtension.defaultContext().newPage();
|
const page = await browserWithExtension.newPage();
|
||||||
const backgroundPageTarget = await browserWithExtension.waitForTarget(target => target.type() === 'background_page');
|
const backgroundPageTarget = await browserWithExtension.waitForTarget(target => target.type() === 'background_page');
|
||||||
await page.close();
|
await page.close();
|
||||||
await browserWithExtension.close();
|
await browserWithExtension.close();
|
||||||
|
|
@ -65,7 +65,7 @@ module.exports.describe = function({testRunner, expect, playwright, defaultBrows
|
||||||
xit('OOPIF: should report google.com frame', async({server}) => {
|
xit('OOPIF: should report google.com frame', async({server}) => {
|
||||||
// https://google.com is isolated by default in Chromium embedder.
|
// https://google.com is isolated by default in Chromium embedder.
|
||||||
const browser = await playwright.launch(headfulOptions);
|
const browser = await playwright.launch(headfulOptions);
|
||||||
const page = await browser.defaultContext().newPage();
|
const page = await browser.newPage();
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await page.interception.enable();
|
await page.interception.enable();
|
||||||
page.on('request', r => page.interception.fulfill(r, {body: 'YO, GOOGLE.COM'}));
|
page.on('request', r => page.interception.fulfill(r, {body: 'YO, GOOGLE.COM'}));
|
||||||
|
|
|
||||||
|
|
@ -34,20 +34,19 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
describe('Playwright.launch webSocket option', function() {
|
describe('Playwright.launch webSocket option', function() {
|
||||||
it('should support the remote-debugging-port argument', async() => {
|
it('should support the remote-debugging-port argument', async() => {
|
||||||
const options = Object.assign({}, defaultBrowserOptions);
|
const options = Object.assign({}, defaultBrowserOptions);
|
||||||
options.args = ['--remote-debugging-port=0'].concat(options.args || []);
|
const browserServer = await playwright.launchServer({ ...options, port: 0 });
|
||||||
const browserApp = await playwright.launchBrowserApp(options);
|
const browser = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const browser = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
expect(browserServer.wsEndpoint()).not.toBe(null);
|
||||||
expect(browserApp.wsEndpoint()).not.toBe(null);
|
const page = await browser.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 browserServer.close();
|
||||||
});
|
});
|
||||||
it('should throw with remote-debugging-pipe argument and webSocket', async() => {
|
it('should throw with remote-debugging-pipe argument and webSocket', 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 error = await playwright.launchBrowserApp(options).catch(e => e);
|
const error = await playwright.launchServer(options).catch(e => e);
|
||||||
expect(error.message).toBe('Argument "--remote-debugging-pipe" is not compatible with the launchBrowserApp.');
|
expect(error.message).toContain('Playwright manages remote debugging connection itself');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -59,7 +58,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
browser.on('targetcreated', () => events.push('CREATED'));
|
browser.on('targetcreated', () => events.push('CREATED'));
|
||||||
browser.on('targetchanged', () => events.push('CHANGED'));
|
browser.on('targetchanged', () => events.push('CHANGED'));
|
||||||
browser.on('targetdestroyed', () => events.push('DESTROYED'));
|
browser.on('targetdestroyed', () => events.push('DESTROYED'));
|
||||||
const page = await browser.defaultContext().newPage();
|
const page = await browser.newPage();
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await page.close();
|
await page.close();
|
||||||
expect(events).toEqual(['CREATED', 'CHANGED', 'DESTROYED']);
|
expect(events).toEqual(['CREATED', 'CHANGED', 'DESTROYED']);
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
beforeEach(async function(state) {
|
beforeEach(async function(state) {
|
||||||
state.outputFile = path.join(ASSETS_DIR, `trace-${state.parallelIndex}.json`);
|
state.outputFile = path.join(ASSETS_DIR, `trace-${state.parallelIndex}.json`);
|
||||||
state.browser = await playwright.launch(defaultBrowserOptions);
|
state.browser = await playwright.launch(defaultBrowserOptions);
|
||||||
state.page = await state.browser.defaultContext().newPage();
|
state.page = await state.browser.newPage();
|
||||||
});
|
});
|
||||||
afterEach(async function(state) {
|
afterEach(async function(state) {
|
||||||
await state.browser.close();
|
await state.browser.close();
|
||||||
|
|
@ -52,7 +52,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
});
|
});
|
||||||
it('should throw if tracing on two pages', async({browser, page, server, outputFile}) => {
|
it('should throw if tracing on two pages', async({browser, page, server, outputFile}) => {
|
||||||
await browser.startTracing(page, {path: outputFile});
|
await browser.startTracing(page, {path: outputFile});
|
||||||
const newPage = await browser.defaultContext().newPage();
|
const newPage = await browser.newPage();
|
||||||
let error = null;
|
let error = null;
|
||||||
await browser.startTracing(newPage, {path: outputFile}).catch(e => error = e);
|
await browser.startTracing(newPage, {path: outputFile}).catch(e => error = e);
|
||||||
await newPage.close();
|
await newPage.close();
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,14 @@ module.exports.describe = function ({ testRunner, expect, defaultBrowserOptions,
|
||||||
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('defaultContext()', function() {
|
describe('launchPersistent()', function() {
|
||||||
beforeEach(async state => {
|
beforeEach(async state => {
|
||||||
state.browser = await playwright.launch(defaultBrowserOptions);
|
state.browserContext = await playwright.launchPersistent(defaultBrowserOptions);
|
||||||
state.page = await state.browser.defaultContext().newPage();
|
state.page = await state.browserContext.newPage();
|
||||||
});
|
});
|
||||||
afterEach(async state => {
|
afterEach(async state => {
|
||||||
await state.browser.close();
|
await state.browserContext.close();
|
||||||
delete state.browser;
|
delete state.browserContext;
|
||||||
delete state.page;
|
delete state.page;
|
||||||
});
|
});
|
||||||
it('context.cookies() should work', async({page, server}) => {
|
it('context.cookies() should work', async({page, server}) => {
|
||||||
|
|
|
||||||
|
|
@ -66,18 +66,17 @@ module.exports.describe = function({testRunner, expect, product, playwright, pla
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('Fixtures', function() {
|
describe('Fixtures', function() {
|
||||||
it('dumpio option should work with webSocket option', async({server}) => {
|
xit('should dump browser process stderr', async({server}) => {
|
||||||
|
const browserServer = await playwright.launchServer({ dumpio: true });
|
||||||
let dumpioData = '';
|
let dumpioData = '';
|
||||||
const res = spawn('node', [path.join(__dirname, 'fixtures', 'dumpio.js'), playwrightPath, product, 'usewebsocket']);
|
browserServer.process().stdout.on('data', data => dumpioData += data.toString('utf8'));
|
||||||
res.stderr.on('data', data => dumpioData += data.toString('utf8'));
|
browserServer.process().stderr.on('data', data => dumpioData += data.toString('utf8'));
|
||||||
await new Promise(resolve => res.on('close', resolve));
|
const browser = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
expect(dumpioData).toContain('message from dumpio');
|
const page = await browser.newPage();
|
||||||
});
|
await page.goto(`data:text/html,<script>console.error('message from dumpio')</script>`);
|
||||||
it('should dump browser process stderr', async({server}) => {
|
await new Promise(f => setTimeout(f, 1000));
|
||||||
let dumpioData = '';
|
await page.close();
|
||||||
const res = spawn('node', [path.join(__dirname, 'fixtures', 'dumpio.js'), playwrightPath, product]);
|
await browserServer.close();
|
||||||
res.stderr.on('data', data => dumpioData += data.toString('utf8'));
|
|
||||||
await new Promise(resolve => res.on('close', resolve));
|
|
||||||
expect(dumpioData).toContain('message from dumpio');
|
expect(dumpioData).toContain('message from dumpio');
|
||||||
});
|
});
|
||||||
it('should close the browser when the node process closes', async () => {
|
it('should close the browser when the node process closes', async () => {
|
||||||
|
|
|
||||||
8
test/fixtures/closeme.js
vendored
8
test/fixtures/closeme.js
vendored
|
|
@ -1,9 +1,9 @@
|
||||||
(async() => {
|
(async() => {
|
||||||
const [, , playwrightRoot, product, options] = process.argv;
|
const [, , playwrightRoot, product, options] = process.argv;
|
||||||
const browserApp = await require(playwrightRoot)[product.toLowerCase()].launchBrowserApp(JSON.parse(options));
|
const browserServer = await require(playwrightRoot)[product.toLowerCase()].launchServer(JSON.parse(options));
|
||||||
browserApp.on('close', (exitCode, signal) => {
|
browserServer.on('close', (exitCode, signal) => {
|
||||||
console.log(`browserClose:${exitCode}:${signal}:browserClose`);
|
console.log(`browserClose:${exitCode}:${signal}:browserClose`);
|
||||||
});
|
});
|
||||||
console.log(`browserPid:${browserApp.process().pid}:browserPid`);
|
console.log(`browserPid:${browserServer.process().pid}:browserPid`);
|
||||||
console.log(`browserWS:${browserApp.wsEndpoint()}:browserWS`);
|
console.log(`browserWS:${browserServer.wsEndpoint()}:browserWS`);
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
24
test/fixtures/dumpio.js
vendored
24
test/fixtures/dumpio.js
vendored
|
|
@ -1,24 +0,0 @@
|
||||||
(async() => {
|
|
||||||
process.on('unhandledRejection', error => {
|
|
||||||
// Catch various errors as we launch non-browser binary.
|
|
||||||
console.log('unhandledRejection', error.message);
|
|
||||||
});
|
|
||||||
|
|
||||||
const [, , playwrightRoot, product, useWebSocket] = process.argv;
|
|
||||||
const options = {
|
|
||||||
webSocket: useWebSocket === 'usewebsocket',
|
|
||||||
ignoreDefaultArgs: true,
|
|
||||||
dumpio: true,
|
|
||||||
timeout: 1,
|
|
||||||
executablePath: 'node',
|
|
||||||
args: ['-e', 'console.error("message from dumpio")', '--']
|
|
||||||
}
|
|
||||||
console.error('using web socket: ' + options.webSocket);
|
|
||||||
if (product.toLowerCase() === 'firefox')
|
|
||||||
options.args.push('-juggler', '-profile');
|
|
||||||
try {
|
|
||||||
await require(playwrightRoot)[product.toLowerCase()].launchBrowserApp(options);
|
|
||||||
console.error('Browser launch unexpectedly succeeded.');
|
|
||||||
} catch (e) {
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
@ -38,38 +38,38 @@ module.exports.describe = function({testRunner, expect, playwright, defaultBrows
|
||||||
|
|
||||||
describe('Headful', function() {
|
describe('Headful', function() {
|
||||||
it('should have default url when launching browser', async function() {
|
it('should have default url when launching browser', async function() {
|
||||||
const browser = await playwright.launch(headfulOptions);
|
const browserContext = await playwright.launchPersistent(headfulOptions);
|
||||||
const pages = (await browser.defaultContext().pages()).map(page => page.url());
|
const pages = (await browserContext.pages()).map(page => page.url());
|
||||||
expect(pages).toEqual(['about:blank']);
|
expect(pages).toEqual(['about:blank']);
|
||||||
await browser.close();
|
await browserContext.close();
|
||||||
});
|
});
|
||||||
// see https://github.com/microsoft/playwright/issues/717
|
// see https://github.com/microsoft/playwright/issues/717
|
||||||
it.skip((WIN && CHROMIUM) || FFOX)('headless should be able to read cookies written by headful', async({server}) => {
|
it.skip((WIN && CHROMIUM) || FFOX)('headless should be able to read cookies written by headful', async({server}) => {
|
||||||
const userDataDir = await mkdtempAsync(TMP_FOLDER);
|
const userDataDir = await mkdtempAsync(TMP_FOLDER);
|
||||||
// Write a cookie in headful chrome
|
// Write a cookie in headful chrome
|
||||||
const headfulBrowser = await playwright.launch(Object.assign({userDataDir}, headfulOptions));
|
const headfulContext = await playwright.launchPersistent(Object.assign({userDataDir}, headfulOptions));
|
||||||
const headfulPage = await headfulBrowser.defaultContext().newPage();
|
const headfulPage = await headfulContext.newPage();
|
||||||
await headfulPage.goto(server.EMPTY_PAGE);
|
await headfulPage.goto(server.EMPTY_PAGE);
|
||||||
await headfulPage.evaluate(() => document.cookie = 'foo=true; expires=Fri, 31 Dec 9999 23:59:59 GMT');
|
await headfulPage.evaluate(() => document.cookie = 'foo=true; expires=Fri, 31 Dec 9999 23:59:59 GMT');
|
||||||
await headfulBrowser.close();
|
await headfulContext.close();
|
||||||
// Read the cookie from headless chrome
|
// Read the cookie from headless chrome
|
||||||
const headlessBrowser = await playwright.launch(Object.assign({userDataDir}, headlessOptions));
|
const headlessContext = await playwright.launchPersistent(Object.assign({userDataDir}, headlessOptions));
|
||||||
const headlessPage = await headlessBrowser.defaultContext().newPage();
|
const headlessPage = await headlessContext.newPage();
|
||||||
await headlessPage.goto(server.EMPTY_PAGE);
|
await headlessPage.goto(server.EMPTY_PAGE);
|
||||||
const cookie = await headlessPage.evaluate(() => document.cookie);
|
const cookie = await headlessPage.evaluate(() => document.cookie);
|
||||||
await headlessBrowser.close();
|
await headlessContext.close();
|
||||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||||
await rmAsync(userDataDir).catch(e => {});
|
await rmAsync(userDataDir).catch(e => {});
|
||||||
expect(cookie).toBe('foo=true');
|
expect(cookie).toBe('foo=true');
|
||||||
});
|
});
|
||||||
it.skip(FFOX)('should close browser with beforeunload page', async({server}) => {
|
it.skip(FFOX)('should close browser with beforeunload page', async({server}) => {
|
||||||
const browser = await playwright.launch(headfulOptions);
|
const browserContext = await playwright.launchPersistent(headfulOptions);
|
||||||
const page = await browser.defaultContext().newPage();
|
const page = await browserContext.newPage();
|
||||||
await page.goto(server.PREFIX + '/beforeunload.html');
|
await page.goto(server.PREFIX + '/beforeunload.html');
|
||||||
// We have to interact with a page so that 'beforeunload' handlers
|
// We have to interact with a page so that 'beforeunload' handlers
|
||||||
// fire.
|
// fire.
|
||||||
await page.click('body');
|
await page.click('body');
|
||||||
await browser.close();
|
await browserContext.close();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
describe('Playwright.launch', function() {
|
describe('Playwright.launch', function() {
|
||||||
it('should reject all promises when browser is closed', async() => {
|
it('should reject all promises when browser is closed', async() => {
|
||||||
const browser = await playwright.launch(defaultBrowserOptions);
|
const browser = await playwright.launch(defaultBrowserOptions);
|
||||||
const page = await browser.defaultContext().newPage();
|
const page = await (await browser.newContext()).newPage();
|
||||||
let error = null;
|
let error = null;
|
||||||
const neverResolves = page.evaluate(() => new Promise(r => {})).catch(e => error = e);
|
const neverResolves = page.evaluate(() => new Promise(r => {})).catch(e => error = e);
|
||||||
await browser.close();
|
await browser.close();
|
||||||
|
|
@ -48,28 +48,28 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
expect(waitError.message).toContain('Failed to launch');
|
expect(waitError.message).toContain('Failed to launch');
|
||||||
});
|
});
|
||||||
it('should have default URL when launching browser', async function() {
|
it('should have default URL when launching browser', async function() {
|
||||||
const browser = await playwright.launch(defaultBrowserOptions);
|
const browserContext = await playwright.launchPersistent(defaultBrowserOptions);
|
||||||
const pages = (await browser.defaultContext().pages()).map(page => page.url());
|
const pages = (await browserContext.pages()).map(page => page.url());
|
||||||
expect(pages).toEqual(['about:blank']);
|
expect(pages).toEqual(['about:blank']);
|
||||||
await browser.close();
|
await browserContext.close();
|
||||||
});
|
});
|
||||||
it('should have custom URL when launching browser', async function({server}) {
|
it('should have custom URL when launching browser', async function({server}) {
|
||||||
const options = Object.assign({}, defaultBrowserOptions);
|
const options = Object.assign({}, defaultBrowserOptions);
|
||||||
options.args = [server.EMPTY_PAGE].concat(options.args || []);
|
options.args = [server.EMPTY_PAGE].concat(options.args || []);
|
||||||
const browser = await playwright.launch(options);
|
const browserContext = await playwright.launchPersistent(options);
|
||||||
const pages = await browser.defaultContext().pages();
|
const pages = await browserContext.pages();
|
||||||
expect(pages.length).toBe(1);
|
expect(pages.length).toBe(1);
|
||||||
const page = pages[0];
|
const page = pages[0];
|
||||||
if (page.url() !== server.EMPTY_PAGE) {
|
if (page.url() !== server.EMPTY_PAGE) {
|
||||||
await page.waitForNavigation();
|
await page.waitForNavigation();
|
||||||
}
|
}
|
||||||
expect(page.url()).toBe(server.EMPTY_PAGE);
|
expect(page.url()).toBe(server.EMPTY_PAGE);
|
||||||
await browser.close();
|
await browserContext.close();
|
||||||
});
|
});
|
||||||
it('should return child_process instance', async () => {
|
it('should return child_process instance', async () => {
|
||||||
const browserApp = await playwright.launchBrowserApp(defaultBrowserOptions);
|
const browserServer = await playwright.launchServer(defaultBrowserOptions);
|
||||||
expect(browserApp.process().pid).toBeGreaterThan(0);
|
expect(browserServer.process().pid).toBeGreaterThan(0);
|
||||||
await browserApp.close();
|
await browserServer.close();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -100,19 +100,18 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
expect(playwright.defaultArgs()).toContain('--no-first-run');
|
expect(playwright.defaultArgs()).toContain('--no-first-run');
|
||||||
expect(playwright.defaultArgs()).toContain(FFOX ? '-headless' : '--headless');
|
expect(playwright.defaultArgs()).toContain(FFOX ? '-headless' : '--headless');
|
||||||
expect(playwright.defaultArgs({headless: false})).not.toContain(FFOX ? '-headless' : '--headless');
|
expect(playwright.defaultArgs({headless: false})).not.toContain(FFOX ? '-headless' : '--headless');
|
||||||
expect(playwright.defaultArgs({userDataDir: 'foo'})).toContain(FFOX ? 'foo' : '--user-data-dir=foo');
|
|
||||||
});
|
});
|
||||||
it('should filter out ignored default arguments', async() => {
|
it('should filter out ignored default arguments', async() => {
|
||||||
const defaultArgsWithoutUserDataDir = playwright.defaultArgs(defaultBrowserOptions);
|
const defaultArgsWithoutUserDataDir = playwright.defaultArgs(defaultBrowserOptions);
|
||||||
const defaultArgsWithUserDataDir = playwright.defaultArgs({...defaultBrowserOptions, userDataDir: 'fake-profile'});
|
const defaultArgsWithUserDataDir = playwright.defaultArgs({...defaultBrowserOptions, userDataDir: 'fake-profile'});
|
||||||
const browserApp = await playwright.launchBrowserApp(Object.assign({}, defaultBrowserOptions, {
|
const browserServer = await playwright.launchServer(Object.assign({}, defaultBrowserOptions, {
|
||||||
userDataDir: 'fake-profile',
|
userDataDir: 'fake-profile',
|
||||||
// Filter out any of the args added by the fake profile
|
// Filter out any of the args added by the fake profile
|
||||||
ignoreDefaultArgs: defaultArgsWithUserDataDir.filter(x => !defaultArgsWithoutUserDataDir.includes(x))
|
ignoreDefaultArgs: defaultArgsWithUserDataDir.filter(x => !defaultArgsWithoutUserDataDir.includes(x))
|
||||||
}));
|
}));
|
||||||
const spawnargs = browserApp.process().spawnargs;
|
const spawnargs = browserServer.process().spawnargs;
|
||||||
expect(spawnargs.some(x => x.includes('fake-profile'))).toBe(false);
|
expect(spawnargs.some(x => x.includes('fake-profile'))).toBe(false);
|
||||||
await browserApp.close();
|
await browserServer.close();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
@ -132,19 +131,19 @@ 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 browserServer = await playwright.launchServer({...defaultBrowserOptions });
|
||||||
const remote = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const remote = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
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 browserServer.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 browserServer = await playwright.launchServer({...defaultBrowserOptions });
|
||||||
const remote = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const remote = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const page = await remote.defaultContext().newPage();
|
const page = await remote.newPage();
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
browserApp.close(),
|
browserServer.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);
|
||||||
|
|
@ -156,21 +155,21 @@ 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 browserServer = await playwright.launchServer({...defaultBrowserOptions });
|
||||||
const remote = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const remote = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const page = await remote.defaultContext().newPage();
|
const page = await remote.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 browserApp.close();
|
await browserServer.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 browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions });
|
const browserServer = await playwright.launchServer({...defaultBrowserOptions });
|
||||||
const remote = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const remote = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const page = await remote.defaultContext().newPage();
|
const page = await remote.newPage();
|
||||||
const watchdog = page.waitForSelector('div', { timeout: 60000 }).catch(e => e);
|
const watchdog = page.waitForSelector('div', { timeout: 60000 }).catch(e => e);
|
||||||
|
|
||||||
// Make sure the previous waitForSelector has time to make it to the browser before we disconnect.
|
// Make sure the previous waitForSelector has time to make it to the browser before we disconnect.
|
||||||
|
|
@ -179,28 +178,28 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
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 browserApp.close();
|
await browserServer.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 browserServer = await playwright.launchServer({...defaultBrowserOptions });
|
||||||
const remote = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const remote = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const page = await remote.defaultContext().newPage();
|
const page = await remote.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 browserApp.close();
|
await browserServer.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 browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions });
|
const browserServer = await playwright.launchServer({...defaultBrowserOptions });
|
||||||
const remote = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const remote = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const newPage = await remote.defaultContext().newPage();
|
const newPage = await remote.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),
|
||||||
browserApp.close()
|
browserServer.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;
|
||||||
|
|
@ -209,10 +208,10 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
it('should be able to close remote browser', async({server}) => {
|
it('should be able to close remote browser', async({server}) => {
|
||||||
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions });
|
const browserServer = await playwright.launchServer({...defaultBrowserOptions });
|
||||||
const remote = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const remote = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
new Promise(f => browserApp.once('close', f)),
|
new Promise(f => browserServer.once('close', f)),
|
||||||
remote.close(),
|
remote.close(),
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
@ -220,36 +219,36 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
|
|
||||||
describe('Playwright.launch |webSocket| option', function() {
|
describe('Playwright.launch |webSocket| option', function() {
|
||||||
it('should support the webSocket option', async() => {
|
it('should support the webSocket option', async() => {
|
||||||
const options = Object.assign({}, defaultBrowserOptions);
|
const browserServer = await playwright.launchServer(defaultBrowserOptions);
|
||||||
const browserApp = await playwright.launchBrowserApp(options);
|
const browser = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const browser = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const browserContext = await browser.newContext();
|
||||||
expect((await browser.defaultContext().pages()).length).toBe(1);
|
expect((await browserContext.pages()).length).toBe(0);
|
||||||
expect(browserApp.wsEndpoint()).not.toBe(null);
|
expect(browserServer.wsEndpoint()).not.toBe(null);
|
||||||
const page = await browser.defaultContext().newPage();
|
const page = await browserContext.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 browserServer.close();
|
||||||
});
|
});
|
||||||
it('should fire "disconnected" when closing with webSocket', async() => {
|
it('should fire "disconnected" when closing with webSocket', async() => {
|
||||||
const options = Object.assign({}, defaultBrowserOptions);
|
const browserServer = await playwright.launchServer(defaultBrowserOptions);
|
||||||
const browserApp = await playwright.launchBrowserApp(options);
|
const browser = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const browser = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
|
||||||
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
|
const disconnectedEventPromise = new Promise(resolve => browser.once('disconnected', resolve));
|
||||||
browserApp.kill();
|
browserServer.kill();
|
||||||
await disconnectedEventPromise;
|
await disconnectedEventPromise;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
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 browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions });
|
const browserServer = await playwright.launchServer(defaultBrowserOptions);
|
||||||
const browser = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const browser = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const page = await browser.defaultContext().newPage();
|
const browserContext = await browser.newContext();
|
||||||
|
const page = await browserContext.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({ wsEndpoint: browserApp.wsEndpoint() });
|
const remote = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const pages = await remote.defaultContext().pages();
|
const pages = await remote.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([
|
||||||
'http://localhost:<PORT>/frames/nested-frames.html',
|
'http://localhost:<PORT>/frames/nested-frames.html',
|
||||||
|
|
@ -259,36 +258,19 @@ 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 browserApp.close();
|
await browserServer.close();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Playwright.launch({userDataDir})', function() {
|
describe('Playwright.launchPersistent', 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);
|
||||||
const browser = await playwright.launch(options);
|
const browserContext = await playwright.launchPersistent(options);
|
||||||
// Open a page to make sure its functional.
|
// Open a page to make sure its functional.
|
||||||
await browser.defaultContext().newPage();
|
await browserContext.newPage();
|
||||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||||
await browser.close();
|
await browserContext.close();
|
||||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
|
||||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
|
||||||
await rmAsync(userDataDir).catch(e => {});
|
|
||||||
});
|
|
||||||
it('userDataDir argument', async({server}) => {
|
|
||||||
const userDataDir = await mkdtempAsync(TMP_FOLDER);
|
|
||||||
const options = Object.assign({}, defaultBrowserOptions);
|
|
||||||
options.args = [...(defaultBrowserOptions.args || [])];
|
|
||||||
if (FFOX)
|
|
||||||
options.args.push('-profile', userDataDir);
|
|
||||||
else
|
|
||||||
options.args.push(`--user-data-dir=${userDataDir}`);
|
|
||||||
const browser = await playwright.launch(options);
|
|
||||||
// Open a page to make sure its functional.
|
|
||||||
await browser.defaultContext().newPage();
|
|
||||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
|
||||||
await browser.close();
|
|
||||||
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
expect(fs.readdirSync(userDataDir).length).toBeGreaterThan(0);
|
||||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||||
await rmAsync(userDataDir).catch(e => {});
|
await rmAsync(userDataDir).catch(e => {});
|
||||||
|
|
@ -296,23 +278,23 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
it.skip(FFOX)('userDataDir option should restore state', async({server}) => {
|
it.skip(FFOX)('userDataDir option should restore state', 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);
|
||||||
const browser = await playwright.launch(options);
|
const browserContext = await playwright.launchPersistent(options);
|
||||||
const page = await browser.defaultContext().newPage();
|
const page = await browserContext.newPage();
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await page.evaluate(() => localStorage.hey = 'hello');
|
await page.evaluate(() => localStorage.hey = 'hello');
|
||||||
await browser.close();
|
await browserContext.close();
|
||||||
|
|
||||||
const browser2 = await playwright.launch(options);
|
const browserContext2 = await playwright.launchPersistent(options);
|
||||||
const page2 = await browser2.defaultContext().newPage();
|
const page2 = await browserContext2.newPage();
|
||||||
await page2.goto(server.EMPTY_PAGE);
|
await page2.goto(server.EMPTY_PAGE);
|
||||||
expect(await page2.evaluate(() => localStorage.hey)).toBe('hello');
|
expect(await page2.evaluate(() => localStorage.hey)).toBe('hello');
|
||||||
await browser2.close();
|
await browserContext2.close();
|
||||||
|
|
||||||
const browser3 = await playwright.launch(defaultBrowserOptions);
|
const browserContext3 = await playwright.launchPersistent(defaultBrowserOptions);
|
||||||
const page3 = await browser3.defaultContext().newPage();
|
const page3 = await browserContext3.newPage();
|
||||||
await page3.goto(server.EMPTY_PAGE);
|
await page3.goto(server.EMPTY_PAGE);
|
||||||
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('hello');
|
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('hello');
|
||||||
await browser3.close();
|
await browserContext3.close();
|
||||||
|
|
||||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||||
await rmAsync(userDataDir).catch(e => {});
|
await rmAsync(userDataDir).catch(e => {});
|
||||||
|
|
@ -321,23 +303,23 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
it.skip(FFOX || (WIN && CHROMIUM))('userDataDir option should restore cookies', async({server}) => {
|
it.skip(FFOX || (WIN && CHROMIUM))('userDataDir option should restore cookies', 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);
|
||||||
const browser = await playwright.launch(options);
|
const browserContext = await playwright.launchPersistent(options);
|
||||||
const page = await browser.defaultContext().newPage();
|
const page = await browserContext.newPage();
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await page.evaluate(() => document.cookie = 'doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT');
|
await page.evaluate(() => document.cookie = 'doSomethingOnlyOnce=true; expires=Fri, 31 Dec 9999 23:59:59 GMT');
|
||||||
await browser.close();
|
await browserContext.close();
|
||||||
|
|
||||||
const browser2 = await playwright.launch(options);
|
const browserContext2 = await playwright.launchPersistent(options);
|
||||||
const page2 = await browser2.defaultContext().newPage();
|
const page2 = await browserContext2.newPage();
|
||||||
await page2.goto(server.EMPTY_PAGE);
|
await page2.goto(server.EMPTY_PAGE);
|
||||||
expect(await page2.evaluate(() => document.cookie)).toBe('doSomethingOnlyOnce=true');
|
expect(await page2.evaluate(() => document.cookie)).toBe('doSomethingOnlyOnce=true');
|
||||||
await browser2.close();
|
await browserContext2.close();
|
||||||
|
|
||||||
const browser3 = await playwright.launch(defaultBrowserOptions);
|
const browserContext3 = await playwright.launchPersistent(defaultBrowserOptions);
|
||||||
const page3 = await browser3.defaultContext().newPage();
|
const page3 = await browserContext3.newPage();
|
||||||
await page3.goto(server.EMPTY_PAGE);
|
await page3.goto(server.EMPTY_PAGE);
|
||||||
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('doSomethingOnlyOnce=true');
|
expect(await page3.evaluate(() => localStorage.hey)).not.toBe('doSomethingOnlyOnce=true');
|
||||||
await browser3.close();
|
await browserContext3.close();
|
||||||
|
|
||||||
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
// This might throw. See https://github.com/GoogleChrome/puppeteer/issues/2778
|
||||||
await rmAsync(userDataDir).catch(e => {});
|
await rmAsync(userDataDir).catch(e => {});
|
||||||
|
|
|
||||||
|
|
@ -24,23 +24,23 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
|
|
||||||
describe('BrowserContext', function() {
|
describe('BrowserContext', function() {
|
||||||
it('should work across sessions', async () => {
|
it('should work across sessions', async () => {
|
||||||
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions });
|
const browserServer = await playwright.launchServer(defaultBrowserOptions);
|
||||||
const browser = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const browser = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
expect(browser.browserContexts().length).toBe(1);
|
expect(browser.browserContexts().length).toBe(0);
|
||||||
await browser.newContext();
|
await browser.newContext();
|
||||||
expect(browser.browserContexts().length).toBe(2);
|
expect(browser.browserContexts().length).toBe(1);
|
||||||
const remoteBrowser = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const remoteBrowser = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const contexts = remoteBrowser.browserContexts();
|
const contexts = remoteBrowser.browserContexts();
|
||||||
expect(contexts.length).toBe(2);
|
expect(contexts.length).toBe(1);
|
||||||
await browserApp.close();
|
await browserServer.close();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
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 browserServer = await playwright.launchServer(defaultBrowserOptions);
|
||||||
const originalBrowser = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const originalBrowser = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const wsEndpoint = browserApp.wsEndpoint();
|
const wsEndpoint = browserServer.wsEndpoint();
|
||||||
const remoteBrowser1 = await playwright.connect({ wsEndpoint });
|
const remoteBrowser1 = await playwright.connect({ wsEndpoint });
|
||||||
const remoteBrowser2 = await playwright.connect({ wsEndpoint });
|
const remoteBrowser2 = await playwright.connect({ wsEndpoint });
|
||||||
|
|
||||||
|
|
@ -63,7 +63,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
utils.waitEvent(remoteBrowser1, 'disconnected'),
|
utils.waitEvent(remoteBrowser1, 'disconnected'),
|
||||||
utils.waitEvent(originalBrowser, 'disconnected'),
|
utils.waitEvent(originalBrowser, 'disconnected'),
|
||||||
browserApp.close(),
|
browserServer.close(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(disconnectedOriginal).toBe(1);
|
expect(disconnectedOriginal).toBe(1);
|
||||||
|
|
@ -74,21 +74,21 @@ 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 browserServer = await playwright.launchServer({...defaultBrowserOptions });
|
||||||
const local = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const local = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const remote = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const remote = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const page = await remote.defaultContext().newPage();
|
const page = await remote.newPage();
|
||||||
expect(await page.evaluate(() => 7 * 8)).toBe(56);
|
expect(await page.evaluate(() => 7 * 8)).toBe(56);
|
||||||
remote.disconnect();
|
remote.disconnect();
|
||||||
|
|
||||||
const secondPage = await local.defaultContext().newPage();
|
const secondPage = await local.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 browserApp.close();
|
await browserServer.close();
|
||||||
});
|
});
|
||||||
it('should be able to close remote browser', async({server}) => {
|
it('should be able to close remote browser', async({server}) => {
|
||||||
const browserApp = await playwright.launchBrowserApp({...defaultBrowserOptions });
|
const browserServer = await playwright.launchServer({...defaultBrowserOptions });
|
||||||
const local = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const local = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const remote = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const remote = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
utils.waitEvent(local, 'disconnected'),
|
utils.waitEvent(local, 'disconnected'),
|
||||||
remote.close(),
|
remote.close(),
|
||||||
|
|
@ -96,15 +96,15 @@ 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 browserServer = await playwright.launchServer({...defaultBrowserOptions });
|
||||||
const browser1 = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const browser1 = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const page1 = await browser1.defaultContext().newPage();
|
const page1 = await browser1.newPage();
|
||||||
await page1.goto(server.EMPTY_PAGE);
|
await page1.goto(server.EMPTY_PAGE);
|
||||||
const browser2 = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
const browser2 = await playwright.connect({ wsEndpoint: browserServer.wsEndpoint() });
|
||||||
const page2 = (await browser2.defaultContext().pages()).find(page => page.url() === server.EMPTY_PAGE);
|
const page2 = (await browser2.pages()).find(page => page.url() === server.EMPTY_PAGE);
|
||||||
expect(await page1.evaluate(() => 7 * 8)).toBe(56);
|
expect(await page1.evaluate(() => 7 * 8)).toBe(56);
|
||||||
expect(await page2.evaluate(() => 7 * 6)).toBe(42);
|
expect(await page2.evaluate(() => 7 * 6)).toBe(42);
|
||||||
await browserApp.close();
|
await browserServer.close();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -92,14 +92,14 @@ module.exports.describe = ({testRunner, product, playwrightPath}) => {
|
||||||
|
|
||||||
describe('Browser', function() {
|
describe('Browser', function() {
|
||||||
beforeAll(async state => {
|
beforeAll(async state => {
|
||||||
state.browser = await playwright.launch();
|
state.browser = await playwright.launch(defaultBrowserOptions);
|
||||||
state.browserApp = state.browser.__app__;
|
state.browserServer = state.browser.__server__;
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async state => {
|
afterAll(async state => {
|
||||||
await state.browserApp.close();
|
await state.browserServer.close();
|
||||||
state.browser = null;
|
state.browser = null;
|
||||||
state.browserApp = null;
|
state.browserServer = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async(state, test) => {
|
beforeEach(async(state, test) => {
|
||||||
|
|
@ -107,8 +107,8 @@ 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 (state.browserApp.process().stderr) {
|
if (state.browserServer.process().stderr) {
|
||||||
rl = require('readline').createInterface({ input: state.browserApp.process().stderr });
|
rl = require('readline').createInterface({ input: state.browserServer.process().stderr });
|
||||||
test.output = '';
|
test.output = '';
|
||||||
rl.on('line', onLine);
|
rl.on('line', onLine);
|
||||||
}
|
}
|
||||||
|
|
@ -190,6 +190,7 @@ module.exports.describe = ({testRunner, product, playwrightPath}) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Browser-level tests that are given a browser.
|
// Browser-level tests that are given a browser.
|
||||||
|
testRunner.loadTests(require('./browser.spec.js'), testOptions);
|
||||||
testRunner.loadTests(require('./browsercontext.spec.js'), testOptions);
|
testRunner.loadTests(require('./browsercontext.spec.js'), testOptions);
|
||||||
testRunner.loadTests(require('./ignorehttpserrors.spec.js'), testOptions);
|
testRunner.loadTests(require('./ignorehttpserrors.spec.js'), testOptions);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -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);
|
state.controlledBrowserApp = await playwright.launchServer(defaultBrowserOptions);
|
||||||
state.hostBrowser = await playwright.launch(defaultBrowserOptions);
|
state.hostBrowser = await playwright.launch(defaultBrowserOptions);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -35,7 +35,7 @@ module.exports.describe = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(async state => {
|
beforeEach(async state => {
|
||||||
state.page = await state.hostBrowser.defaultContext().newPage();
|
state.page = await state.hostBrowser.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, wsEndpoint) => setup(product, wsEndpoint), product.toLowerCase(), state.controlledBrowserApp.wsEndpoint());
|
await state.page.evaluate((product, wsEndpoint) => setup(product, wsEndpoint), product.toLowerCase(), state.controlledBrowserApp.wsEndpoint());
|
||||||
|
|
|
||||||
|
|
@ -30,12 +30,12 @@ const {describe, xdescribe, fdescribe} = runner;
|
||||||
const {it, fit, xit} = runner;
|
const {it, fit, xit} = runner;
|
||||||
const {beforeAll, beforeEach, afterAll, afterEach} = runner;
|
const {beforeAll, beforeEach, afterAll, afterEach} = runner;
|
||||||
|
|
||||||
let browser;
|
let browserContext;
|
||||||
let page;
|
let page;
|
||||||
|
|
||||||
beforeAll(async function() {
|
beforeAll(async function() {
|
||||||
browser = await playwright.launch();
|
browser = await playwright.launch();
|
||||||
page = await browser.defaultContext().newPage();
|
page = await browser.newPage();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterAll(async function() {
|
afterAll(async function() {
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ async function run() {
|
||||||
messages.push(...await preprocessor.ensureReleasedAPILinks([readme], VERSION));
|
messages.push(...await preprocessor.ensureReleasedAPILinks([readme], VERSION));
|
||||||
|
|
||||||
const browser = await playwright.launch();
|
const browser = await playwright.launch();
|
||||||
const page = await browser.defaultContext().newPage();
|
const page = await browser.newPage();
|
||||||
const checkPublicAPI = require('./check_public_api');
|
const checkPublicAPI = require('./check_public_api');
|
||||||
const jsSources = await Source.readdir(path.join(PROJECT_DIR, 'src'));
|
const jsSources = await Source.readdir(path.join(PROJECT_DIR, 'src'));
|
||||||
const externalDependencies = Object.keys(require('../../src/web.webpack.config').externals);
|
const externalDependencies = Object.keys(require('../../src/web.webpack.config').externals);
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,11 @@ 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 browserContext = await playwright.launchPersistent({ executablePath: revision.executablePath });
|
||||||
const origin = browserApp.wsEndpoint().match(/ws:\/\/([0-9A-Za-z:\.]*)\//)[1];
|
const page = await browserContext.newPage();
|
||||||
const browser = await playwright.connect({ wsEndpoint: browserApp.wsEndpoint() });
|
|
||||||
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 browserApp.close();
|
await browserContext.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)}`);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue