feat(context): start moving overrides to the context level
This commit is contained in:
parent
f56726759b
commit
6d0dfd0abf
225
docs/api.md
225
docs/api.md
|
|
@ -32,15 +32,13 @@
|
||||||
* [browser.browserContexts()](#browserbrowsercontexts)
|
* [browser.browserContexts()](#browserbrowsercontexts)
|
||||||
* [browser.chromium](#browserchromium)
|
* [browser.chromium](#browserchromium)
|
||||||
* [browser.close()](#browserclose)
|
* [browser.close()](#browserclose)
|
||||||
* [browser.defaultBrowserContext()](#browserdefaultbrowsercontext)
|
* [browser.defaultContext()](#browserdefaultContext())
|
||||||
* [browser.disconnect()](#browserdisconnect)
|
* [browser.disconnect()](#browserdisconnect)
|
||||||
* [browser.isConnected()](#browserisconnected)
|
* [browser.isConnected()](#browserisconnected)
|
||||||
* [browser.newContext()](#browsernewcontext)
|
* [browser.newContext(options)](#browsernewcontextoptions)
|
||||||
* [browser.newPage()](#browsernewpage)
|
* [browser.newPage(options)](#browsernewpageoptions)
|
||||||
* [browser.pages()](#browserpages)
|
* [browser.pages()](#browserpages)
|
||||||
* [browser.process()](#browserprocess)
|
* [browser.process()](#browserprocess)
|
||||||
* [browser.userAgent()](#browseruseragent)
|
|
||||||
* [browser.version()](#browserversion)
|
|
||||||
- [class: BrowserContext](#class-browsercontext)
|
- [class: BrowserContext](#class-browsercontext)
|
||||||
* [browserContext.browser()](#browsercontextbrowser)
|
* [browserContext.browser()](#browsercontextbrowser)
|
||||||
* [browserContext.clearCookies()](#browsercontextclearcookies)
|
* [browserContext.clearCookies()](#browsercontextclearcookies)
|
||||||
|
|
@ -48,12 +46,12 @@
|
||||||
* [browserContext.cookies([...urls])](#browsercontextcookiesurls)
|
* [browserContext.cookies([...urls])](#browsercontextcookiesurls)
|
||||||
* [browserContext.isIncognito()](#browsercontextisincognito)
|
* [browserContext.isIncognito()](#browsercontextisincognito)
|
||||||
* [browserContext.newPage()](#browsercontextnewpage)
|
* [browserContext.newPage()](#browsercontextnewpage)
|
||||||
|
* [browserContext.overrides](#browsercontextoverrides)
|
||||||
* [browserContext.pages()](#browsercontextpages)
|
* [browserContext.pages()](#browsercontextpages)
|
||||||
* [browserContext.permissions](#browsercontextpermissions)
|
* [browserContext.permissions](#browsercontextpermissions)
|
||||||
* [browserContext.setCookies(cookies)](#browsercontextsetcookiescookies)
|
* [browserContext.setCookies(cookies)](#browsercontextsetcookiescookies)
|
||||||
- [class: Overrides](#class-overrides)
|
- [class: Overrides](#class-overrides)
|
||||||
* [overrides.setGeolocation(options)](#overridessetgeolocationoptions)
|
* [overrides.setGeolocation(options)](#overridessetgeolocationoptions)
|
||||||
* [overrides.setTimezone(timezoneId)](#overridessettimezonetimezoneid)
|
|
||||||
- [class: Permissions](#class-permissions)
|
- [class: Permissions](#class-permissions)
|
||||||
* [permissions.clearOverrides()](#permissionsclearoverrides)
|
* [permissions.clearOverrides()](#permissionsclearoverrides)
|
||||||
* [permissions.override(origin, permissions)](#permissionsoverrideorigin-permissions)
|
* [permissions.override(origin, permissions)](#permissionsoverrideorigin-permissions)
|
||||||
|
|
@ -88,8 +86,6 @@
|
||||||
* [page.content()](#pagecontent)
|
* [page.content()](#pagecontent)
|
||||||
* [page.coverage](#pagecoverage)
|
* [page.coverage](#pagecoverage)
|
||||||
* [page.dblclick(selector[, options])](#pagedblclickselector-options)
|
* [page.dblclick(selector[, options])](#pagedblclickselector-options)
|
||||||
* [page.emulate(options)](#pageemulateoptions)
|
|
||||||
* [page.emulateMedia(options)](#pageemulatemediaoptions)
|
|
||||||
* [page.evaluate(pageFunction[, ...args])](#pageevaluatepagefunction-args)
|
* [page.evaluate(pageFunction[, ...args])](#pageevaluatepagefunction-args)
|
||||||
* [page.evaluateHandle(pageFunction[, ...args])](#pageevaluatehandlepagefunction-args)
|
* [page.evaluateHandle(pageFunction[, ...args])](#pageevaluatehandlepagefunction-args)
|
||||||
* [page.evaluateOnNewDocument(pageFunction[, ...args])](#pageevaluateonnewdocumentpagefunction-args)
|
* [page.evaluateOnNewDocument(pageFunction[, ...args])](#pageevaluateonnewdocumentpagefunction-args)
|
||||||
|
|
@ -106,25 +102,19 @@
|
||||||
* [page.keyboard](#pagekeyboard)
|
* [page.keyboard](#pagekeyboard)
|
||||||
* [page.mainFrame()](#pagemainframe)
|
* [page.mainFrame()](#pagemainframe)
|
||||||
* [page.mouse](#pagemouse)
|
* [page.mouse](#pagemouse)
|
||||||
* [page.overrides](#pageoverrides)
|
|
||||||
* [page.pdf](#pagepdf)
|
* [page.pdf](#pagepdf)
|
||||||
* [page.reload([options])](#pagereloadoptions)
|
* [page.reload([options])](#pagereloadoptions)
|
||||||
* [page.screenshot([options])](#pagescreenshotoptions)
|
* [page.screenshot([options])](#pagescreenshotoptions)
|
||||||
* [page.select(selector, ...values)](#pageselectselector-values)
|
* [page.select(selector, ...values)](#pageselectselector-values)
|
||||||
* [page.setBypassCSP(enabled)](#pagesetbypasscspenabled)
|
|
||||||
* [page.setCacheEnabled([enabled])](#pagesetcacheenabledenabled)
|
* [page.setCacheEnabled([enabled])](#pagesetcacheenabledenabled)
|
||||||
* [page.setContent(html[, options])](#pagesetcontenthtml-options)
|
* [page.setContent(html[, options])](#pagesetcontenthtml-options)
|
||||||
* [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout)
|
* [page.setDefaultNavigationTimeout(timeout)](#pagesetdefaultnavigationtimeouttimeout)
|
||||||
* [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout)
|
* [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout)
|
||||||
* [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders)
|
* [page.setExtraHTTPHeaders(headers)](#pagesetextrahttpheadersheaders)
|
||||||
* [page.setJavaScriptEnabled(enabled)](#pagesetjavascriptenabledenabled)
|
|
||||||
* [page.setUserAgent(userAgent)](#pagesetuseragentuseragent)
|
|
||||||
* [page.setViewport(viewport)](#pagesetviewportviewport)
|
|
||||||
* [page.title()](#pagetitle)
|
* [page.title()](#pagetitle)
|
||||||
* [page.tripleclick(selector[, options])](#pagetripleclickselector-options)
|
* [page.tripleclick(selector[, options])](#pagetripleclickselector-options)
|
||||||
* [page.type(selector, text[, options])](#pagetypeselector-text-options)
|
* [page.type(selector, text[, options])](#pagetypeselector-text-options)
|
||||||
* [page.url()](#pageurl)
|
* [page.url()](#pageurl)
|
||||||
* [page.viewport()](#pageviewport)
|
|
||||||
* [page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#pagewaitforselectororfunctionortimeout-options-args)
|
* [page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])](#pagewaitforselectororfunctionortimeout-options-args)
|
||||||
* [page.waitForFileChooser([options])](#pagewaitforfilechooseroptions)
|
* [page.waitForFileChooser([options])](#pagewaitforfilechooseroptions)
|
||||||
* [page.waitForFunction(pageFunction[, options[, ...args]])](#pagewaitforfunctionpagefunction-options-args)
|
* [page.waitForFunction(pageFunction[, options[, ...args]])](#pagewaitforfunctionpagefunction-options-args)
|
||||||
|
|
@ -381,14 +371,6 @@ const playwright = require('playwright');
|
||||||
- `options` <[Object]>
|
- `options` <[Object]>
|
||||||
- `browserWSEndpoint` <?[string]> a [browser websocket endpoint](#browserwsendpoint) to connect to.
|
- `browserWSEndpoint` <?[string]> a [browser websocket endpoint](#browserwsendpoint) to connect to.
|
||||||
- `browserURL` <?[string]> a browser url to connect to, in format `http://${host}:${port}`. Use interchangeably with `browserWSEndpoint` to let Playwright fetch it from [metadata endpoint](https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target).
|
- `browserURL` <?[string]> a browser url to connect to, in format `http://${host}:${port}`. Use interchangeably with `browserWSEndpoint` to let Playwright fetch it from [metadata endpoint](https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target).
|
||||||
- `ignoreHTTPSErrors` <[boolean]> Whether to ignore HTTPS errors during navigation. Defaults to `false`.
|
|
||||||
- `defaultViewport` <?[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`.
|
|
||||||
- `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
|
|
||||||
- `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
|
|
||||||
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||||
- `transport` <[ConnectionTransport]> **Experimental** Specify a custom transport object for Playwright to use.
|
- `transport` <[ConnectionTransport]> **Experimental** Specify a custom transport object for Playwright to use.
|
||||||
- returns: <[Promise]<[Browser]>>
|
- returns: <[Promise]<[Browser]>>
|
||||||
|
|
@ -476,17 +458,9 @@ try {
|
||||||
|
|
||||||
#### playwright.launch([options])
|
#### playwright.launch([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:
|
||||||
- `ignoreHTTPSErrors` <[boolean]> Whether to ignore HTTPS errors during navigation. Defaults to `false`.
|
|
||||||
- `headless` <[boolean]> Whether to run browser in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). Defaults to `true` unless the `devtools` option is `true`.
|
- `headless` <[boolean]> Whether to run browser in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). Defaults to `true` unless the `devtools` option is `true`.
|
||||||
- `executablePath` <[string]> Path to a Chromium or Chrome executable to run instead of the bundled Chromium. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only [guaranteed to work](https://github.com/Microsoft/playwright/#q-why-doesnt-playwright-vxxx-work-with-chromium-vyyy) with the bundled Chromium, use at your own risk.
|
- `executablePath` <[string]> Path to a Chromium or Chrome executable to run instead of the bundled Chromium. If `executablePath` is a relative path, then it is resolved relative to [current working directory](https://nodejs.org/api/process.html#process_process_cwd). **BEWARE**: Playwright is only [guaranteed to work](https://github.com/Microsoft/playwright/#q-why-doesnt-playwright-vxxx-work-with-chromium-vyyy) with the bundled Chromium, use at your own risk.
|
||||||
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
- `slowMo` <[number]> Slows down Playwright operations by the specified amount of milliseconds. Useful so that you can see what is going on.
|
||||||
- `defaultViewport` <?[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`.
|
|
||||||
- `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
|
|
||||||
- `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
|
|
||||||
- `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 [`playwright.defaultArgs()`](#playwrightdefaultargsoptions). 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 [`playwright.defaultArgs()`](#playwrightdefaultargsoptions). 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`.
|
||||||
|
|
@ -627,7 +601,7 @@ a single instance of [BrowserContext].
|
||||||
|
|
||||||
Closes Chromium and all of its pages (if any were opened). The [Browser] object itself is considered to be disposed and cannot be used anymore.
|
Closes Chromium and all of its pages (if any were opened). The [Browser] object itself is considered to be disposed and cannot be used anymore.
|
||||||
|
|
||||||
#### browser.defaultBrowserContext()
|
#### browser.defaultContext()
|
||||||
- returns: <[BrowserContext]>
|
- returns: <[BrowserContext]>
|
||||||
|
|
||||||
Returns the default browser context. The default browser context can not be closed.
|
Returns the default browser context. The default browser context can not be closed.
|
||||||
|
|
@ -642,7 +616,23 @@ Disconnects Playwright from the browser, but leaves the Chromium process running
|
||||||
|
|
||||||
Indicates that the browser is connected.
|
Indicates that the browser is connected.
|
||||||
|
|
||||||
#### browser.newContext()
|
#### browser.newContext(options)
|
||||||
|
- `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`.
|
||||||
|
- `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
|
||||||
|
- `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
|
||||||
|
- `userAgent` <?[string]> Specific user agent to use in this page
|
||||||
|
- `mediaType` <?[string]> Changes the CSS media type of the page. The only allowed values are `'screen'`, `'print'` and `null`. Passing `null` disables CSS media emulation.
|
||||||
|
- `colorScheme` <?"dark"|"light"|"no-preference"> Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`.
|
||||||
|
- `javaScriptEnabled` <?[boolean]> Whether or not to enable or disable JavaScript in the page. Defaults to true.
|
||||||
|
- `timezoneId` <?[string]> Changes the timezone of the page. 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.
|
||||||
|
|
||||||
- returns: <[Promise]<[BrowserContext]>>
|
- returns: <[Promise]<[BrowserContext]>>
|
||||||
|
|
||||||
Creates a new browser context. It won't share cookies/cache with other browser contexts.
|
Creates a new browser context. It won't share cookies/cache with other browser contexts.
|
||||||
|
|
@ -659,10 +649,24 @@ Creates a new browser context. It won't share cookies/cache with other browser c
|
||||||
})();
|
})();
|
||||||
```
|
```
|
||||||
|
|
||||||
#### browser.newPage()
|
#### browser.newPage(options)
|
||||||
- returns: <[Promise]<[Page]>>
|
- `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`.
|
||||||
|
- `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
|
||||||
|
- `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
|
||||||
|
- `userAgent` <?[string]> Specific user agent to use in this page
|
||||||
|
- `mediaType` <?[string]> Changes the CSS media type of the page. The only allowed values are `'screen'`, `'print'` and `null`. Passing `null` disables CSS media emulation.
|
||||||
|
- `colorScheme` <?"dark"|"light"|"no-preference"> Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`.
|
||||||
|
- `javaScriptEnabled` <?[boolean]> Whether or not to enable or disable JavaScript in the page. Defaults to true.
|
||||||
|
- `timezoneId` <?[string]> Changes the timezone of the page. 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.
|
||||||
|
|
||||||
Promise which resolves to a new [Page] object. The [Page] is created in a default browser context.
|
Promise which resolves to a new [Page] object. The [Page] is created in a new browser context that it will own. Closing this page will close the context.
|
||||||
|
|
||||||
#### browser.pages()
|
#### browser.pages()
|
||||||
- returns: <[Promise]<[Array]<[Page]>>> Promise which resolves to an array of all open pages. Non visible pages, such as `"background_page"`, will not be listed here. You can find them using [target.page()](#targetpage).
|
- returns: <[Promise]<[Array]<[Page]>>> Promise which resolves to an array of all open pages. Non visible pages, such as `"background_page"`, will not be listed here. You can find them using [target.page()](#targetpage).
|
||||||
|
|
@ -673,16 +677,6 @@ the method will return an array with all the pages in all browser contexts.
|
||||||
#### browser.process()
|
#### browser.process()
|
||||||
- returns: <?[ChildProcess]> Spawned browser process. Returns `null` if the browser instance was created with [`playwright.connect`](#playwrightconnectoptions) method.
|
- returns: <?[ChildProcess]> Spawned browser process. Returns `null` if the browser instance was created with [`playwright.connect`](#playwrightconnectoptions) method.
|
||||||
|
|
||||||
#### browser.userAgent()
|
|
||||||
- returns: <[Promise]<[string]>> Promise which resolves to the browser's original user agent.
|
|
||||||
|
|
||||||
> **NOTE** Pages can override browser user agent with [page.setUserAgent](#pagesetuseragentuseragent)
|
|
||||||
|
|
||||||
#### browser.version()
|
|
||||||
- returns: <[Promise]<[string]>> For headless Chromium, this is similar to `HeadlessChrome/61.0.3153.0`. For non-headless, this is similar to `Chrome/61.0.3153.0`.
|
|
||||||
|
|
||||||
> **NOTE** the format of browser.version() might change with future releases of Chromium.
|
|
||||||
|
|
||||||
### 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)
|
||||||
|
|
@ -755,6 +749,9 @@ The default browser context is the only non-incognito browser context.
|
||||||
|
|
||||||
Creates a new page in the browser context.
|
Creates a new page in the browser context.
|
||||||
|
|
||||||
|
#### browserContext.overrides
|
||||||
|
- returns: <[Overrides]>
|
||||||
|
|
||||||
#### browserContext.pages()
|
#### browserContext.pages()
|
||||||
- returns: <[Promise]<[Array]<[Page]>>> Promise which resolves to an array of all open pages. Non visible pages, such as `"background_page"`, will not be listed here. You can find them using [target.page()](#targetpage).
|
- returns: <[Promise]<[Array]<[Page]>>> Promise which resolves to an array of all open pages. Non visible pages, such as `"background_page"`, will not be listed here. You can find them using [target.page()](#targetpage).
|
||||||
|
|
||||||
|
|
@ -792,15 +789,11 @@ await browserContext.setCookies([cookieObject1, cookieObject2]);
|
||||||
Sets the page's geolocation.
|
Sets the page's geolocation.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
await page.overrides.setGeolocation({latitude: 59.95, longitude: 30.31667});
|
await browserContext.overrides.setGeolocation({latitude: 59.95, longitude: 30.31667});
|
||||||
```
|
```
|
||||||
|
|
||||||
> **NOTE** Consider using [browserContext.permissions.override](#permissionsoverrideorigin-permissions) to grant permissions for the page to read its geolocation.
|
> **NOTE** Consider using [browserContext.permissions.override](#permissionsoverrideorigin-permissions) to grant permissions for the page to read its geolocation.
|
||||||
|
|
||||||
#### overrides.setTimezone(timezoneId)
|
|
||||||
- `timezoneId` <?[string]> Changes the timezone of the page. 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. Passing `null` disables timezone emulation.
|
|
||||||
- returns: <[Promise]>
|
|
||||||
|
|
||||||
### class: Permissions
|
### class: Permissions
|
||||||
|
|
||||||
#### permissions.clearOverrides()
|
#### permissions.clearOverrides()
|
||||||
|
|
@ -809,7 +802,7 @@ await page.overrides.setGeolocation({latitude: 59.95, longitude: 30.31667});
|
||||||
Clears all permission overrides for the browser context.
|
Clears all permission overrides for the browser context.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const context = browser.defaultBrowserContext();
|
const context = browser.defaultContext();
|
||||||
context.permissions.override('https://example.com', ['clipboard-read']);
|
context.permissions.override('https://example.com', ['clipboard-read']);
|
||||||
// do stuff ..
|
// do stuff ..
|
||||||
context.permissions.clearOverrides();
|
context.permissions.clearOverrides();
|
||||||
|
|
@ -838,7 +831,7 @@ context.permissions.clearOverrides();
|
||||||
|
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const context = browser.defaultBrowserContext();
|
const context = browser.defaultContext();
|
||||||
await context.permissions.override('https://html5demos.com', ['geolocation']);
|
await context.permissions.override('https://html5demos.com', ['geolocation']);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -1150,77 +1143,6 @@ Bear in mind that if the first click of the `dblclick()` triggers a navigation e
|
||||||
|
|
||||||
Shortcut for [page.mainFrame().dblclick(selector[, options])](#framedblclickselector-options).
|
Shortcut for [page.mainFrame().dblclick(selector[, options])](#framedblclickselector-options).
|
||||||
|
|
||||||
#### page.emulate(options)
|
|
||||||
- `options` <[Object]>
|
|
||||||
- `viewport` <[Object]>
|
|
||||||
- `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`.
|
|
||||||
- `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
|
|
||||||
- `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
|
|
||||||
- `userAgent` <[string]>
|
|
||||||
- returns: <[Promise]>
|
|
||||||
|
|
||||||
Emulates given device metrics and user agent. This method is a shortcut for calling two methods:
|
|
||||||
- [page.setUserAgent(userAgent)](#pagesetuseragentuseragent)
|
|
||||||
- [page.setViewport(viewport)](#pagesetviewportviewport)
|
|
||||||
|
|
||||||
To aid emulation, playwright provides a list of device descriptors which can be obtained via the [`playwright.devices`](#playwrightdevices).
|
|
||||||
|
|
||||||
`page.emulate` will resize the page. A lot of websites don't expect phones to change size, so you should emulate before navigating to the page.
|
|
||||||
|
|
||||||
```js
|
|
||||||
const playwright = require('playwright');
|
|
||||||
const iPhone = playwright.devices['iPhone 6'];
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
const browser = await playwright.launch();
|
|
||||||
const page = await browser.newPage();
|
|
||||||
await page.emulate(iPhone);
|
|
||||||
await page.goto('https://www.google.com');
|
|
||||||
// other actions...
|
|
||||||
await browser.close();
|
|
||||||
})();
|
|
||||||
```
|
|
||||||
|
|
||||||
List of all available devices is available in the source code: [DeviceDescriptors.js](https://github.com/Microsoft/playwright/blob/master/lib/DeviceDescriptors.js).
|
|
||||||
|
|
||||||
#### page.emulateMedia(options)
|
|
||||||
- `options` <[Object]>
|
|
||||||
- `type` <?[string]> Optional. Changes the CSS media type of the page. The only allowed values are `'screen'`, `'print'` and `null`. Passing `null` disables CSS media emulation.
|
|
||||||
- `colorScheme` <"dark"|"light"|"no-preference"> Optional. Emulates `'prefers-colors-scheme'` media feature, supported values are `'light'`, `'dark'`, `'no-preference'`.
|
|
||||||
- returns: <[Promise]>
|
|
||||||
|
|
||||||
```js
|
|
||||||
await page.evaluate(() => matchMedia('screen').matches));
|
|
||||||
// → true
|
|
||||||
await page.evaluate(() => matchMedia('print').matches));
|
|
||||||
// → true
|
|
||||||
|
|
||||||
await page.emulateMedia({ type: 'print' });
|
|
||||||
await page.evaluate(() => matchMedia('screen').matches));
|
|
||||||
// → false
|
|
||||||
await page.evaluate(() => matchMedia('print').matches));
|
|
||||||
// → true
|
|
||||||
|
|
||||||
await page.emulateMedia({});
|
|
||||||
await page.evaluate(() => matchMedia('screen').matches));
|
|
||||||
// → true
|
|
||||||
await page.evaluate(() => matchMedia('print').matches));
|
|
||||||
// → true
|
|
||||||
```
|
|
||||||
|
|
||||||
```js
|
|
||||||
await page.emulateMedia({ colorScheme: 'dark' }] });
|
|
||||||
await page.evaluate(() => matchMedia('(prefers-color-scheme: dark)').matches));
|
|
||||||
// → true
|
|
||||||
await page.evaluate(() => matchMedia('(prefers-color-scheme: light)').matches));
|
|
||||||
// → false
|
|
||||||
await page.evaluate(() => matchMedia('(prefers-color-scheme: no-preference)').matches));
|
|
||||||
// → false
|
|
||||||
```
|
|
||||||
|
|
||||||
#### page.evaluate(pageFunction[, ...args])
|
#### page.evaluate(pageFunction[, ...args])
|
||||||
- `pageFunction` <[function]|[string]> Function to be evaluated in the page context
|
- `pageFunction` <[function]|[string]> Function to be evaluated in the page context
|
||||||
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
|
- `...args` <...[Serializable]|[JSHandle]> Arguments to pass to `pageFunction`
|
||||||
|
|
@ -1480,9 +1402,6 @@ Page is guaranteed to have a main frame which persists during navigations.
|
||||||
|
|
||||||
- returns: <[Mouse]>
|
- returns: <[Mouse]>
|
||||||
|
|
||||||
#### page.overrides
|
|
||||||
- returns: <[Overrides]>
|
|
||||||
|
|
||||||
#### page.pdf
|
#### page.pdf
|
||||||
- returns: <[PDF]>
|
- returns: <[PDF]>
|
||||||
|
|
||||||
|
|
@ -1539,15 +1458,6 @@ page.select('select#colors', { value: 'blue' }, { index: 2 }, 'red');
|
||||||
|
|
||||||
Shortcut for [page.mainFrame().select()](#frameselectselector-values)
|
Shortcut for [page.mainFrame().select()](#frameselectselector-values)
|
||||||
|
|
||||||
#### page.setBypassCSP(enabled)
|
|
||||||
- `enabled` <[boolean]> sets bypassing of page's Content-Security-Policy.
|
|
||||||
- returns: <[Promise]>
|
|
||||||
|
|
||||||
Toggles bypassing page's Content-Security-Policy.
|
|
||||||
|
|
||||||
> **NOTE** CSP bypassing happens at the moment of CSP initialization rather then evaluation. Usually this means
|
|
||||||
that `page.setBypassCSP` should be called before navigating to the domain.
|
|
||||||
|
|
||||||
#### page.setCacheEnabled([enabled])
|
#### page.setCacheEnabled([enabled])
|
||||||
- `enabled` <[boolean]> sets the `enabled` state of the cache.
|
- `enabled` <[boolean]> sets the `enabled` state of the cache.
|
||||||
- returns: <[Promise]>
|
- returns: <[Promise]>
|
||||||
|
|
@ -1607,42 +1517,6 @@ The extra HTTP headers will be sent with every request the page initiates.
|
||||||
|
|
||||||
> **NOTE** page.setExtraHTTPHeaders does not guarantee the order of headers in the outgoing requests.
|
> **NOTE** page.setExtraHTTPHeaders does not guarantee the order of headers in the outgoing requests.
|
||||||
|
|
||||||
#### page.setJavaScriptEnabled(enabled)
|
|
||||||
- `enabled` <[boolean]> Whether or not to enable JavaScript on the page.
|
|
||||||
- returns: <[Promise]>
|
|
||||||
|
|
||||||
> **NOTE** changing this value won't affect scripts that have already been run. It will take full effect on the next [navigation](#pagegotourl-options).
|
|
||||||
|
|
||||||
#### page.setUserAgent(userAgent)
|
|
||||||
- `userAgent` <[string]> Specific user agent to use in this page
|
|
||||||
- returns: <[Promise]> Promise which resolves when the user agent is set.
|
|
||||||
|
|
||||||
#### page.setViewport(viewport)
|
|
||||||
- `viewport` <[Object]>
|
|
||||||
- `width` <[number]> page width in pixels. **required**
|
|
||||||
- `height` <[number]> page height in pixels. **required**
|
|
||||||
- `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`.
|
|
||||||
- `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
|
|
||||||
- `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
|
|
||||||
- returns: <[Promise]>
|
|
||||||
|
|
||||||
> **NOTE** in certain cases, setting viewport will reload the page in order to set the `isMobile` or `hasTouch` properties.
|
|
||||||
|
|
||||||
In the case of multiple pages in a single browser, each page can have its own viewport size.
|
|
||||||
|
|
||||||
`page.setViewport` will resize the page. A lot of websites don't expect phones to change size, so you should set the viewport before navigating to the page.
|
|
||||||
|
|
||||||
```js
|
|
||||||
const page = await browser.newPage();
|
|
||||||
await page.setViewport({
|
|
||||||
width: 640,
|
|
||||||
height: 480,
|
|
||||||
deviceScaleFactor: 1,
|
|
||||||
});
|
|
||||||
await page.goto('https://example.com');
|
|
||||||
```
|
|
||||||
|
|
||||||
#### page.title()
|
#### page.title()
|
||||||
- returns: <[Promise]<[string]>> The page's title.
|
- returns: <[Promise]<[string]>> The page's title.
|
||||||
|
|
||||||
|
|
@ -1691,15 +1565,6 @@ Shortcut for [page.mainFrame().type(selector, text[, options])](#frametypeselect
|
||||||
|
|
||||||
This is a shortcut for [page.mainFrame().url()](#frameurl)
|
This is a shortcut for [page.mainFrame().url()](#frameurl)
|
||||||
|
|
||||||
#### page.viewport()
|
|
||||||
- returns: <?[Object]>
|
|
||||||
- `width` <[number]> page width in pixels.
|
|
||||||
- `height` <[number]> page height in pixels.
|
|
||||||
- `deviceScaleFactor` <[number]> Specify device scale factor (can be though of as dpr). Defaults to `1`.
|
|
||||||
- `isMobile` <[boolean]> Whether the `meta viewport` tag is taken into account. Defaults to `false`.
|
|
||||||
- `hasTouch`<[boolean]> Specifies if viewport supports touch events. Defaults to `false`
|
|
||||||
- `isLandscape` <[boolean]> Specifies if viewport is in landscape mode. Defaults to `false`.
|
|
||||||
|
|
||||||
#### page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])
|
#### page.waitFor(selectorOrFunctionOrTimeout[, options[, ...args]])
|
||||||
- `selectorOrFunctionOrTimeout` <[string]|[number]|[function]> A [selector], predicate or timeout to wait for
|
- `selectorOrFunctionOrTimeout` <[string]|[number]|[function]> A [selector], predicate or timeout to wait for
|
||||||
- `options` <[Object]> Optional waiting parameters
|
- `options` <[Object]> Optional waiting parameters
|
||||||
|
|
|
||||||
|
|
@ -17,14 +17,16 @@
|
||||||
|
|
||||||
import { assert } from './helper';
|
import { assert } from './helper';
|
||||||
import { Page } from './page';
|
import { Page } from './page';
|
||||||
|
import * as input from './input';
|
||||||
import * as network from './network';
|
import * as network from './network';
|
||||||
|
import * as types from './types';
|
||||||
import * as childProcess from 'child_process';
|
import * as childProcess from 'child_process';
|
||||||
|
|
||||||
export interface BrowserInterface {
|
export interface BrowserInterface {
|
||||||
browserContexts(): BrowserContext[];
|
browserContexts(): BrowserContext[];
|
||||||
close(): Promise<void>;
|
close(): Promise<void>;
|
||||||
newContext(): Promise<BrowserContext>;
|
newContext(): Promise<BrowserContext>;
|
||||||
defaultBrowserContext(): BrowserContext;
|
defaultContext(): BrowserContext;
|
||||||
newPage(): Promise<Page>;
|
newPage(): Promise<Page>;
|
||||||
pages(): Promise<Page[]>;
|
pages(): Promise<Page[]>;
|
||||||
process(): childProcess.ChildProcess | null;
|
process(): childProcess.ChildProcess | null;
|
||||||
|
|
@ -41,15 +43,30 @@ export interface BrowserDelegate {
|
||||||
setContextCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
|
setContextCookies(cookies: network.SetNetworkCookieParam[]): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type BrowserContextOptions = {
|
||||||
|
viewport?: types.Viewport | null,
|
||||||
|
ignoreHTTPSErrors?: boolean,
|
||||||
|
javaScriptEnabled?: boolean,
|
||||||
|
bypassCSP?: boolean,
|
||||||
|
mediaType?: input.MediaType,
|
||||||
|
colorScheme?: input.ColorScheme,
|
||||||
|
userAgent?: string,
|
||||||
|
timezoneId?: string
|
||||||
|
};
|
||||||
|
|
||||||
export class BrowserContext {
|
export class BrowserContext {
|
||||||
private readonly _delegate: BrowserDelegate;
|
private readonly _delegate: BrowserDelegate;
|
||||||
private readonly _browser: BrowserInterface;
|
private readonly _browser: BrowserInterface;
|
||||||
private readonly _isIncognito: boolean;
|
private readonly _isIncognito: boolean;
|
||||||
|
readonly _options: BrowserContextOptions;
|
||||||
|
|
||||||
constructor(delegate: BrowserDelegate, browser: BrowserInterface, isIncognito: boolean) {
|
constructor(delegate: BrowserDelegate, browser: BrowserInterface, isIncognito: boolean, options: BrowserContextOptions) {
|
||||||
this._delegate = delegate;
|
this._delegate = delegate;
|
||||||
this._browser = browser;
|
this._browser = browser;
|
||||||
this._isIncognito = isIncognito;
|
this._isIncognito = isIncognito;
|
||||||
|
this._options = options;
|
||||||
|
if (!options.viewport && options.viewport !== null)
|
||||||
|
options.viewport = { width: 800, height: 600 };
|
||||||
}
|
}
|
||||||
|
|
||||||
async pages(): Promise<Page[]> {
|
async pages(): Promise<Page[]> {
|
||||||
|
|
@ -64,6 +81,12 @@ export class BrowserContext {
|
||||||
return this._delegate.createPageInContext();
|
return this._delegate.createPageInContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async _createOwnerPage(): Promise<Page> {
|
||||||
|
const page = await this._delegate.createPageInContext();
|
||||||
|
page._isContextOwner = true;
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
browser(): BrowserInterface {
|
browser(): BrowserInterface {
|
||||||
return this._browser;
|
return this._browser;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,20 +19,19 @@ import * as childProcess from 'child_process';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { Events } from './events';
|
import { Events } from './events';
|
||||||
import { assert, helper } from '../helper';
|
import { assert, helper } from '../helper';
|
||||||
import { BrowserContext, BrowserInterface } from '../browserContext';
|
import { BrowserContext, BrowserInterface, BrowserContextOptions } from '../browserContext';
|
||||||
import { Connection, ConnectionEvents, CDPSession } from './Connection';
|
import { Connection, ConnectionEvents, CDPSession } from './Connection';
|
||||||
import { Page } from '../page';
|
import { Page } from '../page';
|
||||||
import { Target } from './Target';
|
import { Target } from './Target';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import { Chromium } from './features/chromium';
|
import { Chromium } from './features/chromium';
|
||||||
import * as types from '../types';
|
|
||||||
import { FrameManager } from './FrameManager';
|
import { FrameManager } from './FrameManager';
|
||||||
|
import * as events from '../events';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import { Permissions } from './features/permissions';
|
import { Permissions } from './features/permissions';
|
||||||
|
import { Overrides } from './features/overrides';
|
||||||
|
|
||||||
export class Browser extends EventEmitter implements BrowserInterface {
|
export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
private _ignoreHTTPSErrors: boolean;
|
|
||||||
private _defaultViewport: types.Viewport;
|
|
||||||
private _process: childProcess.ChildProcess;
|
private _process: childProcess.ChildProcess;
|
||||||
_connection: Connection;
|
_connection: Connection;
|
||||||
_client: CDPSession;
|
_client: CDPSession;
|
||||||
|
|
@ -45,11 +44,9 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
static async create(
|
static async create(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
contextIds: string[],
|
contextIds: string[],
|
||||||
ignoreHTTPSErrors: boolean,
|
|
||||||
defaultViewport: types.Viewport | null,
|
|
||||||
process: childProcess.ChildProcess | null,
|
process: childProcess.ChildProcess | null,
|
||||||
closeCallback?: (() => Promise<void>)) {
|
closeCallback?: (() => Promise<void>)) {
|
||||||
const browser = new Browser(connection, contextIds, ignoreHTTPSErrors, defaultViewport, process, closeCallback);
|
const browser = new Browser(connection, contextIds, process, closeCallback);
|
||||||
await connection.rootSession.send('Target.setDiscoverTargets', { discover: true });
|
await connection.rootSession.send('Target.setDiscoverTargets', { discover: true });
|
||||||
return browser;
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
@ -57,22 +54,18 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
constructor(
|
constructor(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
contextIds: string[],
|
contextIds: string[],
|
||||||
ignoreHTTPSErrors: boolean,
|
|
||||||
defaultViewport: types.Viewport | null,
|
|
||||||
process: childProcess.ChildProcess | null,
|
process: childProcess.ChildProcess | null,
|
||||||
closeCallback?: (() => Promise<void>)) {
|
closeCallback?: (() => Promise<void>)) {
|
||||||
super();
|
super();
|
||||||
this._connection = connection;
|
this._connection = connection;
|
||||||
this._client = connection.rootSession;
|
this._client = connection.rootSession;
|
||||||
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
|
|
||||||
this._defaultViewport = defaultViewport;
|
|
||||||
this._process = process;
|
this._process = process;
|
||||||
this._closeCallback = closeCallback || (() => Promise.resolve());
|
this._closeCallback = closeCallback || (() => Promise.resolve());
|
||||||
this.chromium = new Chromium(this);
|
this.chromium = new Chromium(this);
|
||||||
|
|
||||||
this._defaultContext = this._createBrowserContext(null);
|
this._defaultContext = this._createBrowserContext(null, {});
|
||||||
for (const contextId of contextIds)
|
for (const contextId of contextIds)
|
||||||
this._contexts.set(contextId, this._createBrowserContext(contextId));
|
this._contexts.set(contextId, this._createBrowserContext(contextId, {}));
|
||||||
|
|
||||||
this._connection.on(ConnectionEvents.Disconnected, () => this.emit(Events.Browser.Disconnected));
|
this._connection.on(ConnectionEvents.Disconnected, () => this.emit(Events.Browser.Disconnected));
|
||||||
this._client.on('Target.targetCreated', this._targetCreated.bind(this));
|
this._client.on('Target.targetCreated', this._targetCreated.bind(this));
|
||||||
|
|
@ -80,8 +73,9 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
this._client.on('Target.targetInfoChanged', this._targetInfoChanged.bind(this));
|
this._client.on('Target.targetInfoChanged', this._targetInfoChanged.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
_createBrowserContext(contextId: string | null): BrowserContext {
|
_createBrowserContext(contextId: string | null, options: BrowserContextOptions): BrowserContext {
|
||||||
const isIncognito = !!contextId;
|
const isIncognito = !!contextId;
|
||||||
|
let overrides: Overrides | null = null;
|
||||||
const context = new BrowserContext({
|
const context = new BrowserContext({
|
||||||
contextPages: async (): Promise<Page[]> => {
|
contextPages: async (): Promise<Page[]> => {
|
||||||
const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page');
|
const targets = this._allTargets().filter(target => target.browserContext() === context && target.type() === 'page');
|
||||||
|
|
@ -94,6 +88,25 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
const target = this._targets.get(targetId);
|
const target = this._targets.get(targetId);
|
||||||
assert(await target._initializedPromise, 'Failed to create target for page');
|
assert(await target._initializedPromise, 'Failed to create target for page');
|
||||||
const page = await target.page();
|
const page = await target.page();
|
||||||
|
const session = (page._delegate as FrameManager)._client;
|
||||||
|
const promises: Promise<any>[] = [ overrides._applyOverrides(page) ];
|
||||||
|
if (options.bypassCSP)
|
||||||
|
promises.push(session.send('Page.setBypassCSP', { enabled: true }));
|
||||||
|
if (options.ignoreHTTPSErrors)
|
||||||
|
promises.push(session.send('Security.setIgnoreCertificateErrors', { ignore: true }));
|
||||||
|
if (options.viewport)
|
||||||
|
promises.push(page._delegate.setViewport(options.viewport));
|
||||||
|
if (options.javaScriptEnabled === false)
|
||||||
|
promises.push(session.send('Emulation.setScriptExecutionDisabled', { value: true }));
|
||||||
|
if (options.userAgent)
|
||||||
|
(page._delegate as FrameManager)._networkManager.setUserAgent(options.userAgent);
|
||||||
|
if (options.mediaType || options.colorScheme) {
|
||||||
|
const features = options.colorScheme ? [{ name: 'prefers-color-scheme', value: options.colorScheme }] : [];
|
||||||
|
promises.push(session.send('Emulation.setEmulatedMedia', { media: options.mediaType || '', features }));
|
||||||
|
}
|
||||||
|
if (options.timezoneId)
|
||||||
|
promises.push(emulateTimezone(session, options.timezoneId));
|
||||||
|
await Promise.all(promises);
|
||||||
return page;
|
return page;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -119,8 +132,10 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
setContextCookies: async (cookies: network.SetNetworkCookieParam[]): Promise<void> => {
|
setContextCookies: async (cookies: network.SetNetworkCookieParam[]): Promise<void> => {
|
||||||
await this._client.send('Storage.setCookies', { cookies, browserContextId: contextId || undefined });
|
await this._client.send('Storage.setCookies', { cookies, browserContextId: contextId || undefined });
|
||||||
},
|
},
|
||||||
}, this, isIncognito);
|
}, this, isIncognito, options);
|
||||||
|
overrides = new Overrides(context);
|
||||||
(context as any).permissions = new Permissions(this._client, contextId);
|
(context as any).permissions = new Permissions(this._client, contextId);
|
||||||
|
(context as any).overrides = overrides;
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,9 +143,9 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
return this._process;
|
return this._process;
|
||||||
}
|
}
|
||||||
|
|
||||||
async newContext(): Promise<BrowserContext> {
|
async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> {
|
||||||
const {browserContextId} = await this._client.send('Target.createBrowserContext');
|
const { browserContextId } = await this._client.send('Target.createBrowserContext');
|
||||||
const context = this._createBrowserContext(browserContextId);
|
const context = this._createBrowserContext(browserContextId, options);
|
||||||
this._contexts.set(browserContextId, context);
|
this._contexts.set(browserContextId, context);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +154,7 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultBrowserContext(): BrowserContext {
|
defaultContext(): BrowserContext {
|
||||||
return this._defaultContext;
|
return this._defaultContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,7 +163,7 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
const {browserContextId} = targetInfo;
|
const {browserContextId} = targetInfo;
|
||||||
const context = (browserContextId && this._contexts.has(browserContextId)) ? this._contexts.get(browserContextId) : this._defaultContext;
|
const context = (browserContextId && this._contexts.has(browserContextId)) ? this._contexts.get(browserContextId) : this._defaultContext;
|
||||||
|
|
||||||
const target = new Target(targetInfo, context, () => this._connection.createSession(targetInfo), this._ignoreHTTPSErrors, this._defaultViewport);
|
const target = new Target(targetInfo, context, () => this._connection.createSession(targetInfo));
|
||||||
assert(!this._targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated');
|
assert(!this._targets.has(event.targetInfo.targetId), 'Target should not exist before targetCreated');
|
||||||
this._targets.set(event.targetInfo.targetId, target);
|
this._targets.set(event.targetInfo.targetId, target);
|
||||||
|
|
||||||
|
|
@ -175,8 +190,9 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
this.chromium.emit(Events.Chromium.TargetChanged, target);
|
this.chromium.emit(Events.Chromium.TargetChanged, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
async newPage(): Promise<Page> {
|
async newPage(options?: BrowserContextOptions): Promise<Page> {
|
||||||
return this._defaultContext.newPage();
|
const context = await this.newContext(options);
|
||||||
|
return context._createOwnerPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
async _closePage(page: Page) {
|
async _closePage(page: Page) {
|
||||||
|
|
@ -250,3 +266,13 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
return this._client.send('Browser.getVersion');
|
return this._client.send('Browser.getVersion');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function emulateTimezone(session: CDPSession, timezoneId: string) {
|
||||||
|
try {
|
||||||
|
await session.send('Emulation.setTimezoneOverride', { timezoneId: timezoneId });
|
||||||
|
} catch (exception) {
|
||||||
|
if (exception.message.includes('Invalid timezone'))
|
||||||
|
throw new Error(`Invalid timezone ID: ${timezoneId}`);
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ import { Accessibility } from './features/accessibility';
|
||||||
import { Coverage } from './features/coverage';
|
import { Coverage } from './features/coverage';
|
||||||
import { PDF } from './features/pdf';
|
import { PDF } from './features/pdf';
|
||||||
import { Workers } from './features/workers';
|
import { Workers } from './features/workers';
|
||||||
import { Overrides } from './features/overrides';
|
|
||||||
import { Interception } from './features/interception';
|
import { Interception } from './features/interception';
|
||||||
import { Browser } from './Browser';
|
import { Browser } from './Browser';
|
||||||
import { BrowserContext } from '../browserContext';
|
import { BrowserContext } from '../browserContext';
|
||||||
|
|
@ -46,24 +45,23 @@ const UTILITY_WORLD_NAME = '__playwright_utility_world__';
|
||||||
export class FrameManager implements PageDelegate {
|
export class FrameManager implements PageDelegate {
|
||||||
_client: CDPSession;
|
_client: CDPSession;
|
||||||
private _page: Page;
|
private _page: Page;
|
||||||
private _networkManager: NetworkManager;
|
readonly _networkManager: NetworkManager;
|
||||||
private _contextIdToContext = new Map<number, dom.FrameExecutionContext>();
|
private _contextIdToContext = new Map<number, dom.FrameExecutionContext>();
|
||||||
private _isolatedWorlds = new Set<string>();
|
private _isolatedWorlds = new Set<string>();
|
||||||
private _eventListeners: RegisteredListener[];
|
private _eventListeners: RegisteredListener[];
|
||||||
rawMouse: RawMouseImpl;
|
rawMouse: RawMouseImpl;
|
||||||
rawKeyboard: RawKeyboardImpl;
|
rawKeyboard: RawKeyboardImpl;
|
||||||
|
|
||||||
constructor(client: CDPSession, browserContext: BrowserContext, ignoreHTTPSErrors: boolean) {
|
constructor(client: CDPSession, browserContext: BrowserContext) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this.rawKeyboard = new RawKeyboardImpl(client);
|
this.rawKeyboard = new RawKeyboardImpl(client);
|
||||||
this.rawMouse = new RawMouseImpl(client);
|
this.rawMouse = new RawMouseImpl(client);
|
||||||
this._page = new Page(this, browserContext);
|
this._page = new Page(this, browserContext);
|
||||||
this._networkManager = new NetworkManager(client, ignoreHTTPSErrors, this._page);
|
this._networkManager = new NetworkManager(client, this._page);
|
||||||
(this._page as any).accessibility = new Accessibility(client);
|
(this._page as any).accessibility = new Accessibility(client);
|
||||||
(this._page as any).coverage = new Coverage(client);
|
(this._page as any).coverage = new Coverage(client);
|
||||||
(this._page as any).pdf = new PDF(client);
|
(this._page as any).pdf = new PDF(client);
|
||||||
(this._page as any).workers = new Workers(client, this._page._addConsoleMessage.bind(this._page), error => this._page.emit(Events.Page.PageError, error));
|
(this._page as any).workers = new Workers(client, this._page._addConsoleMessage.bind(this._page), error => this._page.emit(Events.Page.PageError, error));
|
||||||
(this._page as any).overrides = new Overrides(client);
|
|
||||||
(this._page as any).interception = new Interception(this._networkManager);
|
(this._page as any).interception = new Interception(this._networkManager);
|
||||||
|
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
|
|
@ -276,18 +274,6 @@ export class FrameManager implements PageDelegate {
|
||||||
await this._client.send('Network.setExtraHTTPHeaders', { headers });
|
await this._client.send('Network.setExtraHTTPHeaders', { headers });
|
||||||
}
|
}
|
||||||
|
|
||||||
setUserAgent(userAgent: string): Promise<void> {
|
|
||||||
return this._networkManager.setUserAgent(userAgent);
|
|
||||||
}
|
|
||||||
|
|
||||||
async setJavaScriptEnabled(enabled: boolean): Promise<void> {
|
|
||||||
await this._client.send('Emulation.setScriptExecutionDisabled', { value: !enabled });
|
|
||||||
}
|
|
||||||
|
|
||||||
async setBypassCSP(enabled: boolean): Promise<void> {
|
|
||||||
await this._client.send('Page.setBypassCSP', { enabled });
|
|
||||||
}
|
|
||||||
|
|
||||||
async setViewport(viewport: types.Viewport): Promise<void> {
|
async setViewport(viewport: types.Viewport): Promise<void> {
|
||||||
const {
|
const {
|
||||||
width,
|
width,
|
||||||
|
|
@ -306,7 +292,7 @@ export class FrameManager implements PageDelegate {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setEmulateMedia(mediaType: input.MediaType | null, mediaColorScheme: input.MediaColorScheme | null): Promise<void> {
|
async setEmulateMedia(mediaType: input.MediaType | null, mediaColorScheme: input.ColorScheme | null): Promise<void> {
|
||||||
const features = mediaColorScheme ? [{ name: 'prefers-color-scheme', value: mediaColorScheme }] : [];
|
const features = mediaColorScheme ? [{ name: 'prefers-color-scheme', value: mediaColorScheme }] : [];
|
||||||
await this._client.send('Emulation.setEmulatedMedia', { media: mediaType || '', features });
|
await this._client.send('Emulation.setEmulatedMedia', { media: mediaType || '', features });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ import { BrowserFetcher, BrowserFetcherOptions } from '../browserFetcher';
|
||||||
import { Connection } from './Connection';
|
import { Connection } from './Connection';
|
||||||
import { TimeoutError } from '../errors';
|
import { TimeoutError } from '../errors';
|
||||||
import { assert, debugError, helper } from '../helper';
|
import { assert, debugError, helper } from '../helper';
|
||||||
import * as types from '../types';
|
|
||||||
import { ConnectionTransport, WebSocketTransport, PipeTransport } from '../transport';
|
import { ConnectionTransport, WebSocketTransport, PipeTransport } from '../transport';
|
||||||
import * as util from 'util';
|
import * as util from 'util';
|
||||||
import { launchProcess, waitForLine } from '../processLauncher';
|
import { launchProcess, waitForLine } from '../processLauncher';
|
||||||
|
|
@ -71,7 +70,7 @@ export class Launcher {
|
||||||
this._preferredRevision = preferredRevision;
|
this._preferredRevision = preferredRevision;
|
||||||
}
|
}
|
||||||
|
|
||||||
async launch(options: (LauncherLaunchOptions & LauncherChromeArgOptions & LauncherBrowserOptions) = {}): Promise<Browser> {
|
async launch(options: (LauncherLaunchOptions & LauncherChromeArgOptions & ConnectionOptions) = {}): Promise<Browser> {
|
||||||
const {
|
const {
|
||||||
ignoreDefaultArgs = false,
|
ignoreDefaultArgs = false,
|
||||||
args = [],
|
args = [],
|
||||||
|
|
@ -82,8 +81,6 @@ export class Launcher {
|
||||||
handleSIGINT = true,
|
handleSIGINT = true,
|
||||||
handleSIGTERM = true,
|
handleSIGTERM = true,
|
||||||
handleSIGHUP = true,
|
handleSIGHUP = true,
|
||||||
ignoreHTTPSErrors = false,
|
|
||||||
defaultViewport = {width: 800, height: 600},
|
|
||||||
slowMo = 0,
|
slowMo = 0,
|
||||||
timeout = 30000
|
timeout = 30000
|
||||||
} = options;
|
} = options;
|
||||||
|
|
@ -146,7 +143,7 @@ export class Launcher {
|
||||||
const transport = new PipeTransport(launched.process.stdio[3] as NodeJS.WritableStream, launched.process.stdio[4] as NodeJS.ReadableStream);
|
const transport = new PipeTransport(launched.process.stdio[3] as NodeJS.WritableStream, launched.process.stdio[4] as NodeJS.ReadableStream);
|
||||||
connection = new Connection('', transport, slowMo);
|
connection = new Connection('', transport, slowMo);
|
||||||
}
|
}
|
||||||
const browser = await Browser.create(connection, [], ignoreHTTPSErrors, defaultViewport, launched.process, launched.gracefullyClose);
|
const browser = await Browser.create(connection, [], launched.process, launched.gracefullyClose);
|
||||||
await browser._waitForTarget(t => t.type() === 'page');
|
await browser._waitForTarget(t => t.type() === 'page');
|
||||||
return browser;
|
return browser;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -184,15 +181,13 @@ export class Launcher {
|
||||||
return this._resolveExecutablePath().executablePath;
|
return this._resolveExecutablePath().executablePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
async connect(options: (LauncherBrowserOptions & {
|
async connect(options: (ConnectionOptions & {
|
||||||
browserWSEndpoint?: string;
|
browserWSEndpoint?: string;
|
||||||
browserURL?: string;
|
browserURL?: string;
|
||||||
transport?: ConnectionTransport; })): Promise<Browser> {
|
transport?: ConnectionTransport; })): Promise<Browser> {
|
||||||
const {
|
const {
|
||||||
browserWSEndpoint,
|
browserWSEndpoint,
|
||||||
browserURL,
|
browserURL,
|
||||||
ignoreHTTPSErrors = false,
|
|
||||||
defaultViewport = {width: 800, height: 600},
|
|
||||||
transport,
|
transport,
|
||||||
slowMo = 0,
|
slowMo = 0,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
@ -212,7 +207,7 @@ export class Launcher {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { browserContextIds } = await connection.rootSession.send('Target.getBrowserContexts');
|
const { browserContextIds } = await connection.rootSession.send('Target.getBrowserContexts');
|
||||||
return Browser.create(connection, browserContextIds, ignoreHTTPSErrors, defaultViewport, null, async () => {
|
return Browser.create(connection, browserContextIds, null, async () => {
|
||||||
connection.rootSession.send('Browser.close').catch(debugError);
|
connection.rootSession.send('Browser.close').catch(debugError);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -275,9 +270,7 @@ export type LauncherLaunchOptions = {
|
||||||
pipe?: boolean,
|
pipe?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type LauncherBrowserOptions = {
|
export type ConnectionOptions = {
|
||||||
ignoreHTTPSErrors?: boolean,
|
|
||||||
defaultViewport?: types.Viewport | null,
|
|
||||||
slowMo?: number,
|
slowMo?: number,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,6 @@ import * as frames from '../frames';
|
||||||
|
|
||||||
export class NetworkManager {
|
export class NetworkManager {
|
||||||
private _client: CDPSession;
|
private _client: CDPSession;
|
||||||
private _ignoreHTTPSErrors: boolean;
|
|
||||||
private _page: Page;
|
private _page: Page;
|
||||||
private _requestIdToRequest = new Map<string, InterceptableRequest>();
|
private _requestIdToRequest = new Map<string, InterceptableRequest>();
|
||||||
private _requestIdToRequestWillBeSentEvent = new Map<string, Protocol.Network.requestWillBeSentPayload>();
|
private _requestIdToRequestWillBeSentEvent = new Map<string, Protocol.Network.requestWillBeSentPayload>();
|
||||||
|
|
@ -37,9 +36,8 @@ export class NetworkManager {
|
||||||
private _requestIdToInterceptionId = new Map<string, string>();
|
private _requestIdToInterceptionId = new Map<string, string>();
|
||||||
private _eventListeners: RegisteredListener[];
|
private _eventListeners: RegisteredListener[];
|
||||||
|
|
||||||
constructor(client: CDPSession, ignoreHTTPSErrors: boolean, page: Page) {
|
constructor(client: CDPSession, page: Page) {
|
||||||
this._client = client;
|
this._client = client;
|
||||||
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
|
|
||||||
this._page = page;
|
this._page = page;
|
||||||
|
|
||||||
this._eventListeners = [
|
this._eventListeners = [
|
||||||
|
|
@ -54,8 +52,6 @@ export class NetworkManager {
|
||||||
|
|
||||||
async initialize() {
|
async initialize() {
|
||||||
await this._client.send('Network.enable');
|
await this._client.send('Network.enable');
|
||||||
if (this._ignoreHTTPSErrors)
|
|
||||||
await this._client.send('Security.setIgnoreCertificateErrors', {ignore: true});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import { BrowserFetcher, BrowserFetcherOptions, BrowserFetcherRevisionInfo, OnPr
|
||||||
import { ConnectionTransport } from '../transport';
|
import { ConnectionTransport } from '../transport';
|
||||||
import { DeviceDescriptors, DeviceDescriptor } from '../deviceDescriptors';
|
import { DeviceDescriptors, DeviceDescriptor } from '../deviceDescriptors';
|
||||||
import * as Errors from '../errors';
|
import * as Errors from '../errors';
|
||||||
import { Launcher, LauncherBrowserOptions, LauncherChromeArgOptions, LauncherLaunchOptions, createBrowserFetcher } from './Launcher';
|
import { Launcher, ConnectionOptions, LauncherChromeArgOptions, LauncherLaunchOptions, createBrowserFetcher } from './Launcher';
|
||||||
|
|
||||||
type Devices = { [name: string]: DeviceDescriptor } & DeviceDescriptor[];
|
type Devices = { [name: string]: DeviceDescriptor } & DeviceDescriptor[];
|
||||||
|
|
||||||
|
|
@ -42,11 +42,11 @@ export class Playwright {
|
||||||
return revisionInfo;
|
return revisionInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
launch(options?: (LauncherLaunchOptions & LauncherChromeArgOptions & LauncherBrowserOptions) | undefined): Promise<Browser> {
|
launch(options?: (LauncherLaunchOptions & LauncherChromeArgOptions & ConnectionOptions) | undefined): Promise<Browser> {
|
||||||
return this._launcher.launch(options);
|
return this._launcher.launch(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(options: (LauncherBrowserOptions & {
|
connect(options: (ConnectionOptions & {
|
||||||
browserWSEndpoint?: string;
|
browserWSEndpoint?: string;
|
||||||
browserURL?: string;
|
browserURL?: string;
|
||||||
transport?: ConnectionTransport; })): Promise<Browser> {
|
transport?: ConnectionTransport; })): Promise<Browser> {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as types from '../types';
|
|
||||||
import { Browser } from './Browser';
|
import { Browser } from './Browser';
|
||||||
import { BrowserContext } from '../browserContext';
|
import { BrowserContext } from '../browserContext';
|
||||||
import { CDPSession, CDPSessionEvents } from './Connection';
|
import { CDPSession, CDPSessionEvents } from './Connection';
|
||||||
|
|
@ -33,8 +32,6 @@ export class Target {
|
||||||
private _browserContext: BrowserContext;
|
private _browserContext: BrowserContext;
|
||||||
_targetId: string;
|
_targetId: string;
|
||||||
private _sessionFactory: () => Promise<CDPSession>;
|
private _sessionFactory: () => Promise<CDPSession>;
|
||||||
private _ignoreHTTPSErrors: boolean;
|
|
||||||
private _defaultViewport: types.Viewport;
|
|
||||||
private _pagePromise: Promise<Page> | null = null;
|
private _pagePromise: Promise<Page> | null = null;
|
||||||
private _frameManager: FrameManager | null = null;
|
private _frameManager: FrameManager | null = null;
|
||||||
private _workerPromise: Promise<Worker> | null = null;
|
private _workerPromise: Promise<Worker> | null = null;
|
||||||
|
|
@ -49,15 +46,11 @@ export class Target {
|
||||||
constructor(
|
constructor(
|
||||||
targetInfo: Protocol.Target.TargetInfo,
|
targetInfo: Protocol.Target.TargetInfo,
|
||||||
browserContext: BrowserContext,
|
browserContext: BrowserContext,
|
||||||
sessionFactory: () => Promise<CDPSession>,
|
sessionFactory: () => Promise<CDPSession>) {
|
||||||
ignoreHTTPSErrors: boolean,
|
|
||||||
defaultViewport: types.Viewport | null) {
|
|
||||||
this._targetInfo = targetInfo;
|
this._targetInfo = targetInfo;
|
||||||
this._browserContext = browserContext;
|
this._browserContext = browserContext;
|
||||||
this._targetId = targetInfo.targetId;
|
this._targetId = targetInfo.targetId;
|
||||||
this._sessionFactory = sessionFactory;
|
this._sessionFactory = sessionFactory;
|
||||||
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
|
|
||||||
this._defaultViewport = defaultViewport;
|
|
||||||
this._initializedPromise = new Promise(fulfill => this._initializedCallback = fulfill).then(async success => {
|
this._initializedPromise = new Promise(fulfill => this._initializedCallback = fulfill).then(async success => {
|
||||||
if (!success)
|
if (!success)
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -84,7 +77,7 @@ export class Target {
|
||||||
async page(): Promise<Page | null> {
|
async page(): Promise<Page | null> {
|
||||||
if ((this._targetInfo.type === 'page' || this._targetInfo.type === 'background_page') && !this._pagePromise) {
|
if ((this._targetInfo.type === 'page' || this._targetInfo.type === 'background_page') && !this._pagePromise) {
|
||||||
this._pagePromise = this._sessionFactory().then(async client => {
|
this._pagePromise = this._sessionFactory().then(async client => {
|
||||||
this._frameManager = new FrameManager(client, this._browserContext, this._ignoreHTTPSErrors);
|
this._frameManager = new FrameManager(client, this._browserContext);
|
||||||
const page = this._frameManager.page();
|
const page = this._frameManager.page();
|
||||||
(page as any)[targetSymbol] = this;
|
(page as any)[targetSymbol] = this;
|
||||||
client.once(CDPSessionEvents.Disconnected, () => page._didDisconnect());
|
client.once(CDPSessionEvents.Disconnected, () => page._didDisconnect());
|
||||||
|
|
@ -96,8 +89,6 @@ export class Target {
|
||||||
});
|
});
|
||||||
await this._frameManager.initialize();
|
await this._frameManager.initialize();
|
||||||
await client.send('Target.setAutoAttach', {autoAttach: true, waitForDebuggerOnStart: false, flatten: true});
|
await client.send('Target.setAutoAttach', {autoAttach: true, waitForDebuggerOnStart: false, flatten: true});
|
||||||
if (this._defaultViewport)
|
|
||||||
await page.setViewport(this._defaultViewport);
|
|
||||||
return page;
|
return page;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,16 +15,26 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CDPSession } from '../Connection';
|
import { BrowserContext } from '../../browserContext';
|
||||||
|
import { FrameManager } from '../FrameManager';
|
||||||
|
import { Page } from '../api';
|
||||||
|
|
||||||
export class Overrides {
|
export class Overrides {
|
||||||
private _client: CDPSession;
|
private _context: BrowserContext;
|
||||||
|
private _geolocation: { longitude?: number; latitude?: number; accuracy?: number; } | null = null;
|
||||||
|
|
||||||
constructor(client: CDPSession) {
|
constructor(context: BrowserContext) {
|
||||||
this._client = client;
|
this._context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
async setGeolocation(options: { longitude: number; latitude: number; accuracy: (number | undefined); }) {
|
async setGeolocation(options: { longitude?: number; latitude?: number; accuracy?: (number | undefined); } | null) {
|
||||||
|
if (!options) {
|
||||||
|
for (const page of await this._context.pages())
|
||||||
|
await (page._delegate as FrameManager)._client.send('Emulation.clearGeolocationOverride', {});
|
||||||
|
this._geolocation = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { longitude, latitude, accuracy = 0} = options;
|
const { longitude, latitude, accuracy = 0} = options;
|
||||||
if (longitude < -180 || longitude > 180)
|
if (longitude < -180 || longitude > 180)
|
||||||
throw new Error(`Invalid longitude "${longitude}": precondition -180 <= LONGITUDE <= 180 failed.`);
|
throw new Error(`Invalid longitude "${longitude}": precondition -180 <= LONGITUDE <= 180 failed.`);
|
||||||
|
|
@ -32,16 +42,13 @@ export class Overrides {
|
||||||
throw new Error(`Invalid latitude "${latitude}": precondition -90 <= LATITUDE <= 90 failed.`);
|
throw new Error(`Invalid latitude "${latitude}": precondition -90 <= LATITUDE <= 90 failed.`);
|
||||||
if (accuracy < 0)
|
if (accuracy < 0)
|
||||||
throw new Error(`Invalid accuracy "${accuracy}": precondition 0 <= ACCURACY failed.`);
|
throw new Error(`Invalid accuracy "${accuracy}": precondition 0 <= ACCURACY failed.`);
|
||||||
await this._client.send('Emulation.setGeolocationOverride', {longitude, latitude, accuracy});
|
this._geolocation = { longitude, latitude, accuracy };
|
||||||
|
for (const page of await this._context.pages())
|
||||||
|
await (page._delegate as FrameManager)._client.send('Emulation.setGeolocationOverride', this._geolocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setTimezone(timezoneId: string | null) {
|
async _applyOverrides(page: Page): Promise<void> {
|
||||||
try {
|
if (this._geolocation)
|
||||||
await this._client.send('Emulation.setTimezoneOverride', {timezoneId: timezoneId || ''});
|
await (page._delegate as FrameManager)._client.send('Emulation.setGeolocationOverride', this._geolocation);
|
||||||
} catch (exception) {
|
|
||||||
if (exception.message.includes('Invalid timezone'))
|
|
||||||
throw new Error(`Invalid timezone ID: ${timezoneId}`);
|
|
||||||
throw exception;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ export class ElementHandle<T extends Node = Node> extends js.JSHandle<T> {
|
||||||
element.scrollIntoView({block: 'center', inline: 'center', behavior: 'instant'});
|
element.scrollIntoView({block: 'center', inline: 'center', behavior: 'instant'});
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}, !!this._page._state.javascriptEnabled);
|
}, !!this._page.browserContext()._options.javaScriptEnabled);
|
||||||
if (error)
|
if (error)
|
||||||
throw new Error(error);
|
throw new Error(error);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,10 @@ import * as types from '../types';
|
||||||
import { FrameManager } from './FrameManager';
|
import { FrameManager } from './FrameManager';
|
||||||
import { Firefox } from './features/firefox';
|
import { Firefox } from './features/firefox';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
import { BrowserContext, BrowserInterface } from '../browserContext';
|
import { BrowserContext, BrowserInterface, BrowserContextOptions } from '../browserContext';
|
||||||
|
|
||||||
export class Browser extends EventEmitter implements BrowserInterface {
|
export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
_connection: Connection;
|
_connection: Connection;
|
||||||
_defaultViewport: types.Viewport;
|
|
||||||
private _process: import('child_process').ChildProcess;
|
private _process: import('child_process').ChildProcess;
|
||||||
private _closeCallback: () => Promise<void>;
|
private _closeCallback: () => Promise<void>;
|
||||||
_targets: Map<string, Target>;
|
_targets: Map<string, Target>;
|
||||||
|
|
@ -39,27 +38,26 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
private _eventListeners: RegisteredListener[];
|
private _eventListeners: RegisteredListener[];
|
||||||
readonly firefox: Firefox;
|
readonly firefox: Firefox;
|
||||||
|
|
||||||
static async create(connection: Connection, defaultViewport: types.Viewport | null, process: import('child_process').ChildProcess | null, closeCallback: () => Promise<void>) {
|
static async create(connection: Connection, process: import('child_process').ChildProcess | null, closeCallback: () => Promise<void>) {
|
||||||
const {browserContextIds} = await connection.send('Target.getBrowserContexts');
|
const {browserContextIds} = await connection.send('Target.getBrowserContexts');
|
||||||
const browser = new Browser(connection, browserContextIds, defaultViewport, process, closeCallback);
|
const browser = new Browser(connection, browserContextIds, process, closeCallback);
|
||||||
await connection.send('Target.enable');
|
await connection.send('Target.enable');
|
||||||
return browser;
|
return browser;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(connection: Connection, browserContextIds: Array<string>, defaultViewport: types.Viewport | null, process: import('child_process').ChildProcess | null, closeCallback: () => Promise<void>) {
|
constructor(connection: Connection, browserContextIds: Array<string>, process: import('child_process').ChildProcess | null, closeCallback: () => Promise<void>) {
|
||||||
super();
|
super();
|
||||||
this._connection = connection;
|
this._connection = connection;
|
||||||
this._defaultViewport = defaultViewport;
|
|
||||||
this._process = process;
|
this._process = process;
|
||||||
this._closeCallback = closeCallback;
|
this._closeCallback = closeCallback;
|
||||||
this.firefox = new Firefox(this);
|
this.firefox = new Firefox(this);
|
||||||
|
|
||||||
this._targets = new Map();
|
this._targets = new Map();
|
||||||
|
|
||||||
this._defaultContext = this._createBrowserContext(null);
|
this._defaultContext = this._createBrowserContext(null, {});
|
||||||
this._contexts = new Map();
|
this._contexts = new Map();
|
||||||
for (const browserContextId of browserContextIds)
|
for (const browserContextId of browserContextIds)
|
||||||
this._contexts.set(browserContextId, this._createBrowserContext(browserContextId));
|
this._contexts.set(browserContextId, this._createBrowserContext(browserContextId, {}));
|
||||||
|
|
||||||
this._connection.on(ConnectionEvents.Disconnected, () => this.emit(Events.Browser.Disconnected));
|
this._connection.on(ConnectionEvents.Disconnected, () => this.emit(Events.Browser.Disconnected));
|
||||||
|
|
||||||
|
|
@ -78,9 +76,12 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
return !this._connection._closed;
|
return !this._connection._closed;
|
||||||
}
|
}
|
||||||
|
|
||||||
async newContext(): Promise<BrowserContext> {
|
async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> {
|
||||||
const {browserContextId} = await this._connection.send('Target.createBrowserContext');
|
const {browserContextId} = await this._connection.send('Target.createBrowserContext');
|
||||||
const context = this._createBrowserContext(browserContextId);
|
// TODO: move ignoreHTTPSErrors to browser context level.
|
||||||
|
if (options.ignoreHTTPSErrors)
|
||||||
|
await this._connection.send('Browser.setIgnoreHTTPSErrors', { enabled: true });
|
||||||
|
const context = this._createBrowserContext(browserContextId, options);
|
||||||
this._contexts.set(browserContextId, context);
|
this._contexts.set(browserContextId, context);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
@ -89,7 +90,7 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultBrowserContext() {
|
defaultContext() {
|
||||||
return this._defaultContext;
|
return this._defaultContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,7 +115,7 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
const existingTarget = this._allTargets().find(predicate);
|
const existingTarget = this._allTargets().find(predicate);
|
||||||
if (existingTarget)
|
if (existingTarget)
|
||||||
return existingTarget;
|
return existingTarget;
|
||||||
let resolve;
|
let resolve: (t: Target) => void;
|
||||||
const targetPromise = new Promise<Target>(x => resolve = x);
|
const targetPromise = new Promise<Target>(x => resolve = x);
|
||||||
this.on('targetchanged', check);
|
this.on('targetchanged', check);
|
||||||
try {
|
try {
|
||||||
|
|
@ -131,8 +132,9 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
newPage(): Promise<Page> {
|
async newPage(options?: BrowserContextOptions): Promise<Page> {
|
||||||
return this._defaultContext.newPage();
|
const context = await this.newContext(options);
|
||||||
|
return context._createOwnerPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
async pages() {
|
async pages() {
|
||||||
|
|
@ -173,7 +175,7 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
await this._closeCallback();
|
await this._closeCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
_createBrowserContext(browserContextId: string | null): BrowserContext {
|
_createBrowserContext(browserContextId: string | null, options: BrowserContextOptions): BrowserContext {
|
||||||
const isIncognito = !!browserContextId;
|
const isIncognito = !!browserContextId;
|
||||||
const context = new BrowserContext({
|
const context = new BrowserContext({
|
||||||
contextPages: async (): Promise<Page[]> => {
|
contextPages: async (): Promise<Page[]> => {
|
||||||
|
|
@ -187,7 +189,21 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
browserContextId: browserContextId || undefined
|
browserContextId: browserContextId || undefined
|
||||||
});
|
});
|
||||||
const target = this._targets.get(targetId);
|
const target = this._targets.get(targetId);
|
||||||
return await target.page();
|
const page = await target.page();
|
||||||
|
const session = (page._delegate as FrameManager)._session;
|
||||||
|
const promises: Promise<any>[] = [];
|
||||||
|
if (options.viewport)
|
||||||
|
promises.push(page._delegate.setViewport(options.viewport));
|
||||||
|
if (options.bypassCSP)
|
||||||
|
promises.push(session.send('Page.setBypassCSP', { enabled: true }));
|
||||||
|
if (options.javaScriptEnabled === false)
|
||||||
|
promises.push(session.send('Page.setJavascriptEnabled', { enabled: false }));
|
||||||
|
if (options.userAgent)
|
||||||
|
promises.push(session.send('Page.setUserAgent', { userAgent: options.userAgent }));
|
||||||
|
if (options.mediaType || options.colorScheme)
|
||||||
|
promises.push(session.send('Page.setEmulatedMedia', { type: options.mediaType, colorScheme: options.colorScheme }));
|
||||||
|
Promise.all(promises);
|
||||||
|
return page;
|
||||||
},
|
},
|
||||||
|
|
||||||
closeContext: async (): Promise<void> => {
|
closeContext: async (): Promise<void> => {
|
||||||
|
|
@ -211,7 +227,7 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
setContextCookies: async (cookies: network.SetNetworkCookieParam[]): Promise<void> => {
|
setContextCookies: async (cookies: network.SetNetworkCookieParam[]): Promise<void> => {
|
||||||
await this._connection.send('Browser.setCookies', { browserContextId: browserContextId || undefined, cookies });
|
await this._connection.send('Browser.setCookies', { browserContextId: browserContextId || undefined, cookies });
|
||||||
},
|
},
|
||||||
}, this, isIncognito);
|
}, this, isIncognito, options);
|
||||||
(context as any).permissions = new Permissions(this._connection, browserContextId);
|
(context as any).permissions = new Permissions(this._connection, browserContextId);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
@ -267,8 +283,6 @@ export class Target {
|
||||||
const page = this._frameManager._page;
|
const page = this._frameManager._page;
|
||||||
session.once(JugglerSessionEvents.Disconnected, () => page._didDisconnect());
|
session.once(JugglerSessionEvents.Disconnected, () => page._didDisconnect());
|
||||||
await this._frameManager._initialize();
|
await this._frameManager._initialize();
|
||||||
if (this._browser._defaultViewport)
|
|
||||||
await page.setViewport(this._browser._defaultViewport);
|
|
||||||
f(page);
|
f(page);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -189,18 +189,6 @@ export class FrameManager implements PageDelegate {
|
||||||
await this._session.send('Network.setExtraHTTPHeaders', { headers: array });
|
await this._session.send('Network.setExtraHTTPHeaders', { headers: array });
|
||||||
}
|
}
|
||||||
|
|
||||||
async setUserAgent(userAgent: string): Promise<void> {
|
|
||||||
await this._session.send('Page.setUserAgent', { userAgent });
|
|
||||||
}
|
|
||||||
|
|
||||||
async setJavaScriptEnabled(enabled: boolean): Promise<void> {
|
|
||||||
await this._session.send('Page.setJavascriptEnabled', { enabled });
|
|
||||||
}
|
|
||||||
|
|
||||||
async setBypassCSP(enabled: boolean): Promise<void> {
|
|
||||||
await this._session.send('Page.setBypassCSP', { enabled });
|
|
||||||
}
|
|
||||||
|
|
||||||
async setViewport(viewport: types.Viewport): Promise<void> {
|
async setViewport(viewport: types.Viewport): Promise<void> {
|
||||||
const {
|
const {
|
||||||
width,
|
width,
|
||||||
|
|
@ -215,7 +203,7 @@ export class FrameManager implements PageDelegate {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setEmulateMedia(mediaType: input.MediaType | null, mediaColorScheme: input.MediaColorScheme | null): Promise<void> {
|
async setEmulateMedia(mediaType: input.MediaType | null, mediaColorScheme: input.ColorScheme | null): Promise<void> {
|
||||||
await this._session.send('Page.setEmulatedMedia', {
|
await this._session.send('Page.setEmulatedMedia', {
|
||||||
type: mediaType === null ? undefined : mediaType,
|
type: mediaType === null ? undefined : mediaType,
|
||||||
colorScheme: mediaColorScheme === null ? undefined : mediaColorScheme
|
colorScheme: mediaColorScheme === null ? undefined : mediaColorScheme
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,6 @@ export class Launcher {
|
||||||
handleSIGHUP = true,
|
handleSIGHUP = true,
|
||||||
handleSIGINT = true,
|
handleSIGINT = true,
|
||||||
handleSIGTERM = true,
|
handleSIGTERM = true,
|
||||||
ignoreHTTPSErrors = false,
|
|
||||||
defaultViewport = {width: 800, height: 600},
|
|
||||||
slowMo = 0,
|
slowMo = 0,
|
||||||
timeout = 30000,
|
timeout = 30000,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
@ -129,9 +127,7 @@ export class Launcher {
|
||||||
const url = match[1];
|
const url = match[1];
|
||||||
const transport = await WebSocketTransport.create(url);
|
const transport = await WebSocketTransport.create(url);
|
||||||
connection = new Connection(url, transport, slowMo);
|
connection = new Connection(url, transport, slowMo);
|
||||||
const browser = await Browser.create(connection, defaultViewport, launched.process, launched.gracefullyClose);
|
const browser = await Browser.create(connection, launched.process, launched.gracefullyClose);
|
||||||
if (ignoreHTTPSErrors)
|
|
||||||
await connection.send('Browser.setIgnoreHTTPSErrors', {enabled: true});
|
|
||||||
await browser._waitForTarget(t => t.type() === 'page');
|
await browser._waitForTarget(t => t.type() === 'page');
|
||||||
return browser;
|
return browser;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -144,16 +140,11 @@ export class Launcher {
|
||||||
const {
|
const {
|
||||||
browserWSEndpoint,
|
browserWSEndpoint,
|
||||||
slowMo = 0,
|
slowMo = 0,
|
||||||
defaultViewport = {width: 800, height: 600},
|
|
||||||
ignoreHTTPSErrors = false,
|
|
||||||
} = options;
|
} = options;
|
||||||
let connection = null;
|
let connection = null;
|
||||||
const transport = await WebSocketTransport.create(browserWSEndpoint);
|
const transport = await WebSocketTransport.create(browserWSEndpoint);
|
||||||
connection = new Connection(browserWSEndpoint, transport, slowMo);
|
connection = new Connection(browserWSEndpoint, transport, slowMo);
|
||||||
const browser = await Browser.create(connection, defaultViewport, null, () => connection.send('Browser.close').catch(debugError));
|
return await Browser.create(connection, null, () => connection.send('Browser.close').catch(debugError));
|
||||||
if (ignoreHTTPSErrors)
|
|
||||||
await connection.send('Browser.setIgnoreHTTPSErrors', {enabled: true});
|
|
||||||
return browser;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
executablePath(): string {
|
executablePath(): string {
|
||||||
|
|
|
||||||
|
|
@ -406,5 +406,5 @@ export type FilePayload = {
|
||||||
|
|
||||||
export type MediaType = 'screen' | 'print';
|
export type MediaType = 'screen' | 'print';
|
||||||
export const mediaTypes: Set<MediaType> = new Set(['screen', 'print']);
|
export const mediaTypes: Set<MediaType> = new Set(['screen', 'print']);
|
||||||
export type MediaColorScheme = 'dark' | 'light' | 'no-preference';
|
export type ColorScheme = 'dark' | 'light' | 'no-preference';
|
||||||
export const mediaColorSchemes: Set<MediaColorScheme> = new Set(['dark', 'light', 'no-preference']);
|
export const mediaColorSchemes: Set<ColorScheme> = new Set(['dark', 'light', 'no-preference']);
|
||||||
|
|
|
||||||
53
src/page.ts
53
src/page.ts
|
|
@ -45,11 +45,8 @@ export interface PageDelegate {
|
||||||
needsLifecycleResetOnSetContent(): boolean;
|
needsLifecycleResetOnSetContent(): boolean;
|
||||||
|
|
||||||
setExtraHTTPHeaders(extraHTTPHeaders: network.Headers): Promise<void>;
|
setExtraHTTPHeaders(extraHTTPHeaders: network.Headers): Promise<void>;
|
||||||
setUserAgent(userAgent: string): Promise<void>;
|
|
||||||
setJavaScriptEnabled(enabled: boolean): Promise<void>;
|
|
||||||
setBypassCSP(enabled: boolean): Promise<void>;
|
|
||||||
setViewport(viewport: types.Viewport): Promise<void>;
|
setViewport(viewport: types.Viewport): Promise<void>;
|
||||||
setEmulateMedia(mediaType: input.MediaType | null, mediaColorScheme: input.MediaColorScheme | null): Promise<void>;
|
setEmulateMedia(mediaType: input.MediaType | null, colorScheme: input.ColorScheme | null): Promise<void>;
|
||||||
setCacheEnabled(enabled: boolean): Promise<void>;
|
setCacheEnabled(enabled: boolean): Promise<void>;
|
||||||
|
|
||||||
getBoundingBoxForScreenshot(handle: dom.ElementHandle<Node>): Promise<types.Rect | null>;
|
getBoundingBoxForScreenshot(handle: dom.ElementHandle<Node>): Promise<types.Rect | null>;
|
||||||
|
|
@ -69,12 +66,9 @@ export interface PageDelegate {
|
||||||
|
|
||||||
type PageState = {
|
type PageState = {
|
||||||
viewport: types.Viewport | null;
|
viewport: types.Viewport | null;
|
||||||
userAgent: string | null;
|
|
||||||
mediaType: input.MediaType | null;
|
mediaType: input.MediaType | null;
|
||||||
mediaColorScheme: input.MediaColorScheme | null;
|
colorScheme: input.ColorScheme | null;
|
||||||
javascriptEnabled: boolean | null;
|
|
||||||
extraHTTPHeaders: network.Headers | null;
|
extraHTTPHeaders: network.Headers | null;
|
||||||
bypassCSP: boolean | null;
|
|
||||||
cacheEnabled: boolean | null;
|
cacheEnabled: boolean | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -99,6 +93,7 @@ export class Page extends EventEmitter {
|
||||||
private _pageBindings = new Map<string, Function>();
|
private _pageBindings = new Map<string, Function>();
|
||||||
readonly _screenshotter: Screenshotter;
|
readonly _screenshotter: Screenshotter;
|
||||||
readonly _frameManager: frames.FrameManager;
|
readonly _frameManager: frames.FrameManager;
|
||||||
|
_isContextOwner = false;
|
||||||
|
|
||||||
constructor(delegate: PageDelegate, browserContext: BrowserContext) {
|
constructor(delegate: PageDelegate, browserContext: BrowserContext) {
|
||||||
super();
|
super();
|
||||||
|
|
@ -107,13 +102,10 @@ export class Page extends EventEmitter {
|
||||||
this._disconnectedPromise = new Promise(f => this._disconnectedCallback = f);
|
this._disconnectedPromise = new Promise(f => this._disconnectedCallback = f);
|
||||||
this._browserContext = browserContext;
|
this._browserContext = browserContext;
|
||||||
this._state = {
|
this._state = {
|
||||||
viewport: null,
|
viewport: browserContext._options.viewport || null,
|
||||||
userAgent: null,
|
mediaType: browserContext._options.mediaType || null,
|
||||||
mediaType: null,
|
colorScheme: browserContext._options.colorScheme || null,
|
||||||
mediaColorScheme: null,
|
|
||||||
javascriptEnabled: null,
|
|
||||||
extraHTTPHeaders: null,
|
extraHTTPHeaders: null,
|
||||||
bypassCSP: null,
|
|
||||||
cacheEnabled: null,
|
cacheEnabled: null,
|
||||||
};
|
};
|
||||||
this.keyboard = new input.Keyboard(delegate.rawKeyboard);
|
this.keyboard = new input.Keyboard(delegate.rawKeyboard);
|
||||||
|
|
@ -244,11 +236,6 @@ export class Page extends EventEmitter {
|
||||||
return this._delegate.setExtraHTTPHeaders(headers);
|
return this._delegate.setExtraHTTPHeaders(headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
setUserAgent(userAgent: string) {
|
|
||||||
this._state.userAgent = userAgent;
|
|
||||||
return this._delegate.setUserAgent(userAgent);
|
|
||||||
}
|
|
||||||
|
|
||||||
async _onBindingCalled(payload: string, context: js.ExecutionContext) {
|
async _onBindingCalled(payload: string, context: js.ExecutionContext) {
|
||||||
const {name, seq, args} = JSON.parse(payload);
|
const {name, seq, args} = JSON.parse(payload);
|
||||||
let expression = null;
|
let expression = null;
|
||||||
|
|
@ -360,35 +347,15 @@ export class Page extends EventEmitter {
|
||||||
return waitPromise;
|
return waitPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
async emulate(options: { viewport: types.Viewport; userAgent: string; }) {
|
|
||||||
await Promise.all([
|
|
||||||
this.setViewport(options.viewport),
|
|
||||||
this.setUserAgent(options.userAgent)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async setJavaScriptEnabled(enabled: boolean) {
|
async emulateMedia(options: { type?: input.MediaType, colorScheme?: input.ColorScheme }) {
|
||||||
if (this._state.javascriptEnabled === enabled)
|
|
||||||
return;
|
|
||||||
this._state.javascriptEnabled = enabled;
|
|
||||||
await this._delegate.setJavaScriptEnabled(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
async setBypassCSP(enabled: boolean) {
|
|
||||||
if (this._state.bypassCSP === enabled)
|
|
||||||
return;
|
|
||||||
this._state.bypassCSP = enabled;
|
|
||||||
await this._delegate.setBypassCSP(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
async emulateMedia(options: { type?: input.MediaType, colorScheme?: input.MediaColorScheme }) {
|
|
||||||
assert(!options.type || input.mediaTypes.has(options.type), 'Unsupported media type: ' + options.type);
|
assert(!options.type || input.mediaTypes.has(options.type), 'Unsupported media type: ' + options.type);
|
||||||
assert(!options.colorScheme || input.mediaColorSchemes.has(options.colorScheme), 'Unsupported color scheme: ' + options.colorScheme);
|
assert(!options.colorScheme || input.mediaColorSchemes.has(options.colorScheme), 'Unsupported color scheme: ' + options.colorScheme);
|
||||||
if (options.type !== undefined)
|
if (options.type !== undefined)
|
||||||
this._state.mediaType = options.type;
|
this._state.mediaType = options.type;
|
||||||
if (options.colorScheme !== undefined)
|
if (options.colorScheme !== undefined)
|
||||||
this._state.mediaColorScheme = options.colorScheme;
|
this._state.colorScheme = options.colorScheme;
|
||||||
await this._delegate.setEmulateMedia(this._state.mediaType, this._state.mediaColorScheme);
|
await this._delegate.setEmulateMedia(this._state.mediaType, this._state.colorScheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setViewport(viewport: types.Viewport) {
|
async setViewport(viewport: types.Viewport) {
|
||||||
|
|
@ -436,6 +403,8 @@ export class Page extends EventEmitter {
|
||||||
await this._delegate.closePage(runBeforeUnload);
|
await this._delegate.closePage(runBeforeUnload);
|
||||||
if (!runBeforeUnload)
|
if (!runBeforeUnload)
|
||||||
await this._closedPromise;
|
await this._closedPromise;
|
||||||
|
if (this._isContextOwner)
|
||||||
|
await this._browserContext.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
isClosed(): boolean {
|
isClosed(): boolean {
|
||||||
|
|
|
||||||
|
|
@ -19,43 +19,37 @@ import * as childProcess from 'child_process';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { helper, RegisteredListener, debugError } from '../helper';
|
import { helper, RegisteredListener, debugError } from '../helper';
|
||||||
import * as network from '../network';
|
import * as network from '../network';
|
||||||
|
import * as types from '../types';
|
||||||
import { Connection, ConnectionEvents, TargetSession } from './Connection';
|
import { Connection, ConnectionEvents, TargetSession } from './Connection';
|
||||||
import { Page } from '../page';
|
import { Page } from '../page';
|
||||||
import { Target } from './Target';
|
import { Target } from './Target';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
import * as types from '../types';
|
|
||||||
import { Events } from '../events';
|
import { Events } from '../events';
|
||||||
import { BrowserContext, BrowserInterface } from '../browserContext';
|
import { BrowserContext, BrowserInterface, BrowserContextOptions } from '../browserContext';
|
||||||
|
|
||||||
export class Browser extends EventEmitter implements BrowserInterface {
|
export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
readonly _defaultViewport: types.Viewport;
|
|
||||||
private readonly _process: childProcess.ChildProcess;
|
private readonly _process: childProcess.ChildProcess;
|
||||||
readonly _connection: Connection;
|
readonly _connection: Connection;
|
||||||
private _closeCallback: () => Promise<void>;
|
private _closeCallback: () => Promise<void>;
|
||||||
private readonly _defaultContext: BrowserContext;
|
private _defaultContext: BrowserContext;
|
||||||
private _contexts = new Map<string, BrowserContext>();
|
private _contexts = new Map<string, BrowserContext>();
|
||||||
_targets = new Map<string, Target>();
|
_targets = new Map<string, Target>();
|
||||||
private _eventListeners: RegisteredListener[];
|
private _eventListeners: RegisteredListener[];
|
||||||
private _privateEvents = new EventEmitter();
|
private _privateEvents = new EventEmitter();
|
||||||
private readonly _ignoreHTTPSErrors: boolean;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
ignoreHTTPSErrors: boolean,
|
|
||||||
defaultViewport: types.Viewport | null,
|
|
||||||
process: childProcess.ChildProcess | null,
|
process: childProcess.ChildProcess | null,
|
||||||
closeCallback?: (() => Promise<void>)) {
|
closeCallback?: (() => Promise<void>)) {
|
||||||
super();
|
super();
|
||||||
this._connection = connection;
|
this._connection = connection;
|
||||||
this._ignoreHTTPSErrors = ignoreHTTPSErrors;
|
|
||||||
this._defaultViewport = defaultViewport;
|
|
||||||
this._process = process;
|
this._process = process;
|
||||||
this._closeCallback = closeCallback || (() => Promise.resolve());
|
this._closeCallback = closeCallback || (() => Promise.resolve());
|
||||||
|
|
||||||
/** @type {!Map<string, !Target>} */
|
/** @type {!Map<string, !Target>} */
|
||||||
this._targets = new Map();
|
this._targets = new Map();
|
||||||
|
|
||||||
this._defaultContext = this._createBrowserContext(undefined);
|
this._defaultContext = this._createBrowserContext(undefined, {});
|
||||||
/** @type {!Map<string, !BrowserContext>} */
|
/** @type {!Map<string, !BrowserContext>} */
|
||||||
this._contexts = new Map();
|
this._contexts = new Map();
|
||||||
|
|
||||||
|
|
@ -70,9 +64,6 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
debugError(e);
|
debugError(e);
|
||||||
throw e;
|
throw e;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this._ignoreHTTPSErrors)
|
|
||||||
this._setIgnoreTLSFailures(undefined);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async userAgent(): Promise<string> {
|
async userAgent(): Promise<string> {
|
||||||
|
|
@ -92,11 +83,11 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
return this._process;
|
return this._process;
|
||||||
}
|
}
|
||||||
|
|
||||||
async newContext(): Promise<BrowserContext> {
|
async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> {
|
||||||
const {browserContextId} = await this._connection.send('Browser.createContext');
|
const { browserContextId } = await this._connection.send('Browser.createContext');
|
||||||
if (this._ignoreHTTPSErrors)
|
const context = this._createBrowserContext(browserContextId, options);
|
||||||
await this._setIgnoreTLSFailures(browserContextId);
|
if (options.ignoreHTTPSErrors)
|
||||||
const context = this._createBrowserContext(browserContextId);
|
await this._connection.send('Browser.setIgnoreCertificateErrors', { browserContextId, ignore: true });
|
||||||
this._contexts.set(browserContextId, context);
|
this._contexts.set(browserContextId, context);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
@ -105,15 +96,13 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
return [this._defaultContext, ...Array.from(this._contexts.values())];
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultBrowserContext(): BrowserContext {
|
defaultContext(): BrowserContext {
|
||||||
return this._defaultContext;
|
return this._defaultContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _disposeContext(browserContextId: string | null) {
|
async newPage(options?: BrowserContextOptions): Promise<Page> {
|
||||||
}
|
const context = await this.newContext(options);
|
||||||
|
return context._createOwnerPage();
|
||||||
async newPage(): Promise<Page> {
|
|
||||||
return this._defaultContext.newPage();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
targets(): Target[] {
|
targets(): Target[] {
|
||||||
|
|
@ -220,7 +209,7 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
await this._closeCallback.call(null);
|
await this._closeCallback.call(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createBrowserContext(browserContextId: string | undefined): BrowserContext {
|
_createBrowserContext(browserContextId: string | undefined, options: BrowserContextOptions): BrowserContext {
|
||||||
const isIncognito = !!browserContextId;
|
const isIncognito = !!browserContextId;
|
||||||
const context = new BrowserContext({
|
const context = new BrowserContext({
|
||||||
contextPages: async (): Promise<Page[]> => {
|
contextPages: async (): Promise<Page[]> => {
|
||||||
|
|
@ -256,13 +245,9 @@ export class Browser extends EventEmitter implements BrowserInterface {
|
||||||
const cc = cookies.map(c => ({ ...c, session: c.expires === -1 || c.expires === undefined })) as Protocol.Browser.SetCookieParam[];
|
const cc = cookies.map(c => ({ ...c, session: c.expires === -1 || c.expires === undefined })) as Protocol.Browser.SetCookieParam[];
|
||||||
await this._connection.send('Browser.setCookies', { cookies: cc, browserContextId });
|
await this._connection.send('Browser.setCookies', { cookies: cc, browserContextId });
|
||||||
},
|
},
|
||||||
}, this, isIncognito);
|
}, this, isIncognito, options);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
async _setIgnoreTLSFailures(browserContextId: string | undefined) {
|
|
||||||
await this._connection.send('Browser.setIgnoreCertificateErrors', { browserContextId, ignore: true });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const BrowserEvents = {
|
const BrowserEvents = {
|
||||||
|
|
|
||||||
|
|
@ -86,16 +86,19 @@ export class FrameManager implements PageDelegate {
|
||||||
// Dialog agent resides in the UI process and should not be re-enabled on navigation.
|
// Dialog agent resides in the UI process and should not be re-enabled on navigation.
|
||||||
promises.push(session.send('Dialog.enable'));
|
promises.push(session.send('Dialog.enable'));
|
||||||
}
|
}
|
||||||
if (this._page._state.userAgent !== null)
|
const contextOptions = this._page.browserContext()._options;
|
||||||
promises.push(this._setUserAgent(session, this._page._state.userAgent));
|
if (contextOptions.userAgent)
|
||||||
if (this._page._state.mediaType !== null || this._page._state.mediaColorScheme !== null)
|
promises.push(session.send('Page.overrideUserAgent', { value: contextOptions.userAgent }));
|
||||||
promises.push(this._setEmulateMedia(session, this._page._state.mediaType, this._page._state.mediaColorScheme));
|
if (this._page._state.mediaType || this._page._state.colorScheme)
|
||||||
if (this._page._state.javascriptEnabled !== null)
|
promises.push(this._setEmulateMedia(session, this._page._state.mediaType, this._page._state.colorScheme));
|
||||||
promises.push(this._setJavaScriptEnabled(session, this._page._state.javascriptEnabled));
|
if (contextOptions.javaScriptEnabled === false)
|
||||||
if (this._page._state.bypassCSP !== null)
|
promises.push(session.send('Emulation.setJavaScriptEnabled', { enabled: false }));
|
||||||
promises.push(this._setBypassCSP(session, this._page._state.bypassCSP));
|
if (contextOptions.bypassCSP)
|
||||||
|
promises.push(session.send('Page.setBypassCSP', { enabled: true }));
|
||||||
if (this._page._state.extraHTTPHeaders !== null)
|
if (this._page._state.extraHTTPHeaders !== null)
|
||||||
promises.push(this._setExtraHTTPHeaders(session, this._page._state.extraHTTPHeaders));
|
promises.push(this._setExtraHTTPHeaders(session, this._page._state.extraHTTPHeaders));
|
||||||
|
if (this._page._state.viewport)
|
||||||
|
promises.push(this.setViewport(this._page._state.viewport));
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -256,19 +259,7 @@ export class FrameManager implements PageDelegate {
|
||||||
await session.send('Network.setExtraHTTPHeaders', { headers });
|
await session.send('Network.setExtraHTTPHeaders', { headers });
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _setUserAgent(session: TargetSession, userAgent: string): Promise<void> {
|
private async _setEmulateMedia(session: TargetSession, mediaType: input.MediaType | null, mediaColorScheme: input.ColorScheme | null): Promise<void> {
|
||||||
await session.send('Page.overrideUserAgent', { value: userAgent });
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _setJavaScriptEnabled(session: TargetSession, enabled: boolean): Promise<void> {
|
|
||||||
await session.send('Emulation.setJavaScriptEnabled', { enabled });
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _setBypassCSP(session: TargetSession, enabled: boolean): Promise<void> {
|
|
||||||
await session.send('Page.setBypassCSP', { enabled });
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _setEmulateMedia(session: TargetSession, mediaType: input.MediaType | null, mediaColorScheme: input.MediaColorScheme | null): Promise<void> {
|
|
||||||
const promises = [];
|
const promises = [];
|
||||||
promises.push(session.send('Page.setEmulatedMedia', { media: mediaType || '' }));
|
promises.push(session.send('Page.setEmulatedMedia', { media: mediaType || '' }));
|
||||||
if (mediaColorScheme !== null) {
|
if (mediaColorScheme !== null) {
|
||||||
|
|
@ -286,23 +277,12 @@ export class FrameManager implements PageDelegate {
|
||||||
await this._setExtraHTTPHeaders(this._session, headers);
|
await this._setExtraHTTPHeaders(this._session, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setUserAgent(userAgent: string): Promise<void> {
|
async setEmulateMedia(mediaType: input.MediaType | null, mediaColorScheme: input.ColorScheme | null): Promise<void> {
|
||||||
await this._setUserAgent(this._session, userAgent);
|
|
||||||
await this._page.reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
async setJavaScriptEnabled(enabled: boolean): Promise<void> {
|
|
||||||
await this._setJavaScriptEnabled(this._session, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
async setBypassCSP(enabled: boolean): Promise<void> {
|
|
||||||
await this._setBypassCSP(this._session, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
async setEmulateMedia(mediaType: input.MediaType | null, mediaColorScheme: input.MediaColorScheme | null): Promise<void> {
|
|
||||||
await this._setEmulateMedia(this._session, mediaType, mediaColorScheme);
|
await this._setEmulateMedia(this._session, mediaType, mediaColorScheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
async setViewport(viewport: types.Viewport): Promise<void> {
|
async setViewport(viewport: types.Viewport): Promise<void> {
|
||||||
if (viewport.isMobile || viewport.isLandscape || viewport.hasTouch)
|
if (viewport.isMobile || viewport.isLandscape || viewport.hasTouch)
|
||||||
throw new Error('Not implemented');
|
throw new Error('Not implemented');
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,6 @@ import { debugError, assert } from '../helper';
|
||||||
import { Browser } from './Browser';
|
import { Browser } from './Browser';
|
||||||
import { BrowserFetcher, BrowserFetcherOptions } from '../browserFetcher';
|
import { BrowserFetcher, BrowserFetcherOptions } from '../browserFetcher';
|
||||||
import { Connection } from './Connection';
|
import { Connection } from './Connection';
|
||||||
import * as types from '../types';
|
|
||||||
import { PipeTransport } from '../transport';
|
import { PipeTransport } from '../transport';
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
@ -58,9 +57,7 @@ export class Launcher {
|
||||||
handleSIGINT = true,
|
handleSIGINT = true,
|
||||||
handleSIGTERM = true,
|
handleSIGTERM = true,
|
||||||
handleSIGHUP = true,
|
handleSIGHUP = true,
|
||||||
defaultViewport = {width: 800, height: 600},
|
|
||||||
slowMo = 0,
|
slowMo = 0,
|
||||||
ignoreHTTPSErrors = false
|
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
const webkitArguments = [];
|
const webkitArguments = [];
|
||||||
|
|
@ -104,7 +101,7 @@ export class Launcher {
|
||||||
try {
|
try {
|
||||||
const transport = new PipeTransport(launched.process.stdio[3] as NodeJS.WritableStream, launched.process.stdio[4] as NodeJS.ReadableStream);
|
const transport = new PipeTransport(launched.process.stdio[3] as NodeJS.WritableStream, launched.process.stdio[4] as NodeJS.ReadableStream);
|
||||||
connection = new Connection(transport, slowMo);
|
connection = new Connection(transport, slowMo);
|
||||||
const browser = new Browser(connection, ignoreHTTPSErrors, defaultViewport, launched.process, launched.gracefullyClose);
|
const browser = new Browser(connection, launched.process, launched.gracefullyClose);
|
||||||
await browser._waitForTarget(t => t._type === 'page');
|
await browser._waitForTarget(t => t._type === 'page');
|
||||||
return browser;
|
return browser;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -136,9 +133,7 @@ export type LauncherLaunchOptions = {
|
||||||
headless?: boolean,
|
headless?: boolean,
|
||||||
dumpio?: boolean,
|
dumpio?: boolean,
|
||||||
env?: {[key: string]: string} | undefined,
|
env?: {[key: string]: string} | undefined,
|
||||||
defaultViewport?: types.Viewport | null,
|
|
||||||
slowMo?: number,
|
slowMo?: number,
|
||||||
ignoreHTTPSErrors?: boolean,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let cachedMacVersion = undefined;
|
let cachedMacVersion = undefined;
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Browser } from './Browser';
|
|
||||||
import { BrowserContext } from '../browserContext';
|
import { BrowserContext } from '../browserContext';
|
||||||
import { Page } from '../page';
|
import { Page } from '../page';
|
||||||
import { Protocol } from './protocol';
|
import { Protocol } from './protocol';
|
||||||
|
|
@ -86,7 +85,6 @@ export class Target {
|
||||||
|
|
||||||
async page(): Promise<Page> {
|
async page(): Promise<Page> {
|
||||||
if (this._type === 'page' && !this._pagePromise) {
|
if (this._type === 'page' && !this._pagePromise) {
|
||||||
const browser = this._browserContext.browser() as Browser;
|
|
||||||
this._frameManager = new FrameManager(this._browserContext);
|
this._frameManager = new FrameManager(this._browserContext);
|
||||||
// Reference local page variable as |this._frameManager| may be
|
// Reference local page variable as |this._frameManager| may be
|
||||||
// cleared on swap.
|
// cleared on swap.
|
||||||
|
|
@ -94,8 +92,6 @@ export class Target {
|
||||||
this._pagePromise = new Promise(async f => {
|
this._pagePromise = new Promise(async f => {
|
||||||
this._adoptPage();
|
this._adoptPage();
|
||||||
await this._initializeSession(this._session);
|
await this._initializeSession(this._session);
|
||||||
if (browser._defaultViewport)
|
|
||||||
await page.setViewport(browser._defaultViewport);
|
|
||||||
f(page);
|
f(page);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
const utils = require('./utils');
|
const utils = require('./utils');
|
||||||
|
|
||||||
module.exports.addTests = function({testRunner, expect}) {
|
module.exports.addTests = function({testRunner, expect, playwright, WEBKIT}) {
|
||||||
const {describe, xdescribe, fdescribe} = testRunner;
|
const {describe, xdescribe, fdescribe} = testRunner;
|
||||||
const {it, fit, xit} = testRunner;
|
const {it, fit, xit} = testRunner;
|
||||||
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
||||||
|
|
@ -29,7 +29,7 @@ module.exports.addTests = function({testRunner, expect}) {
|
||||||
expect(defaultContext.isIncognito()).toBe(false);
|
expect(defaultContext.isIncognito()).toBe(false);
|
||||||
let error = null;
|
let error = null;
|
||||||
await defaultContext.close().catch(e => error = e);
|
await defaultContext.close().catch(e => error = e);
|
||||||
expect(browser.defaultBrowserContext()).toBe(defaultContext);
|
expect(browser.defaultContext()).toBe(defaultContext);
|
||||||
expect(error.message).toContain('cannot be closed');
|
expect(error.message).toContain('cannot be closed');
|
||||||
});
|
});
|
||||||
it('should create new incognito context', async function({browser, server}) {
|
it('should create new incognito context', async function({browser, server}) {
|
||||||
|
|
@ -107,5 +107,161 @@ module.exports.addTests = function({testRunner, expect}) {
|
||||||
]);
|
]);
|
||||||
expect(browser.browserContexts().length).toBe(1);
|
expect(browser.browserContexts().length).toBe(1);
|
||||||
});
|
});
|
||||||
|
it('should set the default viewport', async({ newPage }) => {
|
||||||
|
const page = await newPage({ viewport: { width: 456, height: 789 } });
|
||||||
|
expect(await page.evaluate('window.innerWidth')).toBe(456);
|
||||||
|
expect(await page.evaluate('window.innerHeight')).toBe(789);
|
||||||
|
});
|
||||||
|
it('should take fullPage screenshots when default viewport is null', async({server, newPage}) => {
|
||||||
|
const page = await newPage({ viewport: null });
|
||||||
|
await page.goto(server.PREFIX + '/grid.html');
|
||||||
|
const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
||||||
|
const screenshot = await page.screenshot({
|
||||||
|
fullPage: true
|
||||||
|
});
|
||||||
|
expect(screenshot).toBeInstanceOf(Buffer);
|
||||||
|
|
||||||
|
const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
||||||
|
expect(sizeBefore.width).toBe(sizeAfter.width);
|
||||||
|
expect(sizeBefore.height).toBe(sizeAfter.height);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('BrowserContext({setUserAgent})', function() {
|
||||||
|
it('should work', async({newPage, server}) => {
|
||||||
|
{
|
||||||
|
const page = await newPage();
|
||||||
|
expect(await page.evaluate(() => navigator.userAgent)).toContain('Mozilla');
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const page = await newPage({ userAgent: 'foobar' });
|
||||||
|
const [request] = await Promise.all([
|
||||||
|
server.waitForRequest('/empty.html'),
|
||||||
|
page.goto(server.EMPTY_PAGE),
|
||||||
|
]);
|
||||||
|
expect(request.headers['user-agent']).toBe('foobar');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('should work for subframes', async({newPage, server}) => {
|
||||||
|
{
|
||||||
|
const page = await newPage();
|
||||||
|
expect(await page.evaluate(() => navigator.userAgent)).toContain('Mozilla');
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const page = await newPage({ userAgent: 'foobar' });
|
||||||
|
const [request] = await Promise.all([
|
||||||
|
server.waitForRequest('/empty.html'),
|
||||||
|
utils.attachFrame(page, 'frame1', server.EMPTY_PAGE),
|
||||||
|
]);
|
||||||
|
expect(request.headers['user-agent']).toBe('foobar');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('should emulate device user-agent', async({newPage, server}) => {
|
||||||
|
{
|
||||||
|
const page = await newPage();
|
||||||
|
await page.goto(server.PREFIX + '/mobile.html');
|
||||||
|
expect(await page.evaluate(() => navigator.userAgent)).not.toContain('iPhone');
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const page = await newPage({ userAgent: playwright.devices['iPhone 6'].userAgent });
|
||||||
|
await page.goto(server.PREFIX + '/mobile.html');
|
||||||
|
expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('BrowserContext({bypassCSP})', function() {
|
||||||
|
it('should bypass CSP meta tag', async({newPage, server}) => {
|
||||||
|
// Make sure CSP prohibits addScriptTag.
|
||||||
|
{
|
||||||
|
const page = await newPage();
|
||||||
|
await page.goto(server.PREFIX + '/csp.html');
|
||||||
|
await page.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
||||||
|
expect(await page.evaluate(() => window.__injected)).toBe(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
// By-pass CSP and try one more time.
|
||||||
|
{
|
||||||
|
const page = await newPage({ bypassCSP: true });
|
||||||
|
await page.goto(server.PREFIX + '/csp.html');
|
||||||
|
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||||
|
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should bypass CSP header', async({newPage, server}) => {
|
||||||
|
// Make sure CSP prohibits addScriptTag.
|
||||||
|
server.setCSP('/empty.html', 'default-src "self"');
|
||||||
|
|
||||||
|
{
|
||||||
|
const page = await newPage();
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
await page.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
||||||
|
expect(await page.evaluate(() => window.__injected)).toBe(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
// By-pass CSP and try one more time.
|
||||||
|
{
|
||||||
|
const page = await newPage({ bypassCSP: true });
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||||
|
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should bypass after cross-process navigation', async({newPage, server}) => {
|
||||||
|
const page = await newPage({ bypassCSP: true });
|
||||||
|
await page.goto(server.PREFIX + '/csp.html');
|
||||||
|
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||||
|
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||||
|
|
||||||
|
await page.goto(server.CROSS_PROCESS_PREFIX + '/csp.html');
|
||||||
|
await page.addScriptTag({content: 'window.__injected = 42;'});
|
||||||
|
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
||||||
|
});
|
||||||
|
it('should bypass CSP in iframes as well', async({newPage, server}) => {
|
||||||
|
// Make sure CSP prohibits addScriptTag in an iframe.
|
||||||
|
{
|
||||||
|
const page = await newPage();
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
const frame = await utils.attachFrame(page, 'frame1', server.PREFIX + '/csp.html');
|
||||||
|
await frame.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
||||||
|
expect(await frame.evaluate(() => window.__injected)).toBe(undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
// By-pass CSP and try one more time.
|
||||||
|
{
|
||||||
|
const page = await newPage({ bypassCSP: true });
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
const frame = await utils.attachFrame(page, 'frame1', server.PREFIX + '/csp.html');
|
||||||
|
await frame.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
||||||
|
expect(await frame.evaluate(() => window.__injected)).toBe(42);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('BrowserContext({javaScriptEnabled})', function() {
|
||||||
|
it('should work', async({newPage}) => {
|
||||||
|
{
|
||||||
|
const page = await newPage({ javaScriptEnabled: false });
|
||||||
|
await page.goto('data:text/html, <script>var something = "forbidden"</script>');
|
||||||
|
let error = null;
|
||||||
|
await page.evaluate('something').catch(e => error = e);
|
||||||
|
if (WEBKIT)
|
||||||
|
expect(error.message).toContain('Can\'t find variable: something');
|
||||||
|
else
|
||||||
|
expect(error.message).toContain('something is not defined');
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
const page = await newPage();
|
||||||
|
await page.goto('data:text/html, <script>var something = "forbidden"</script>');
|
||||||
|
expect(await page.evaluate('something')).toBe('forbidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
it('should be able to navigate after disabling javascript', async({newPage, server}) => {
|
||||||
|
const page = await newPage({ javaScriptEnabled: false });
|
||||||
|
await page.goto(server.EMPTY_PAGE);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -55,16 +55,17 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
const originalBrowser = await playwright.launch(defaultBrowserOptions);
|
const originalBrowser = await playwright.launch(defaultBrowserOptions);
|
||||||
const browserWSEndpoint = originalBrowser.chromium.wsEndpoint();
|
const browserWSEndpoint = originalBrowser.chromium.wsEndpoint();
|
||||||
|
|
||||||
const browser = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint, ignoreHTTPSErrors: true});
|
const browser = await playwright.connect({...defaultBrowserOptions, browserWSEndpoint});
|
||||||
const page = await browser.newPage();
|
const context = await browser.newContext({ ignoreHTTPSErrors: true });
|
||||||
|
const page = await context.newPage();
|
||||||
let error = null;
|
let error = null;
|
||||||
const [serverRequest, response] = await Promise.all([
|
const [, response] = await Promise.all([
|
||||||
httpsServer.waitForRequest('/empty.html'),
|
httpsServer.waitForRequest('/empty.html'),
|
||||||
page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e)
|
page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e)
|
||||||
]);
|
]);
|
||||||
expect(error).toBe(null);
|
expect(error).toBe(null);
|
||||||
expect(response.ok()).toBe(true);
|
expect(response.ok()).toBe(true);
|
||||||
await page.close();
|
await context.close();
|
||||||
await browser.close();
|
await browser.close();
|
||||||
});
|
});
|
||||||
it('should be able to reconnect to a disconnected browser', async({server}) => {
|
it('should be able to reconnect to a disconnected browser', async({server}) => {
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ module.exports.addTests = function ({ testRunner, expect }) {
|
||||||
it('should work', async({page, server, context}) => {
|
it('should work', async({page, server, context}) => {
|
||||||
await context.permissions.override(server.PREFIX, ['geolocation']);
|
await context.permissions.override(server.PREFIX, ['geolocation']);
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await page.overrides.setGeolocation({longitude: 10, latitude: 10});
|
await context.overrides.setGeolocation({longitude: 10, latitude: 10});
|
||||||
const geolocation = await page.evaluate(() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {
|
const geolocation = await page.evaluate(() => new Promise(resolve => navigator.geolocation.getCurrentPosition(position => {
|
||||||
resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});
|
resolve({latitude: position.coords.latitude, longitude: position.coords.longitude});
|
||||||
})));
|
})));
|
||||||
|
|
@ -35,10 +35,10 @@ module.exports.addTests = function ({ testRunner, expect }) {
|
||||||
longitude: 10
|
longitude: 10
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it('should throw when invalid longitude', async({page, server, context}) => {
|
it('should throw when invalid longitude', async({context}) => {
|
||||||
let error = null;
|
let error = null;
|
||||||
try {
|
try {
|
||||||
await page.overrides.setGeolocation({longitude: 200, latitude: 10});
|
await context.overrides.setGeolocation({longitude: 200, latitude: 10});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,13 +71,13 @@ module.exports.addTests = function({testRunner, expect, playwright, defaultBrows
|
||||||
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 headfulBrowser = await playwright.launch(Object.assign({userDataDir}, headfulOptions));
|
||||||
const headfulPage = await headfulBrowser.newPage();
|
const headfulPage = await headfulBrowser.defaultContext().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 headfulBrowser.close();
|
||||||
// Read the cookie from headless chrome
|
// Read the cookie from headless chrome
|
||||||
const headlessBrowser = await playwright.launch(Object.assign({userDataDir}, headlessOptions));
|
const headlessBrowser = await playwright.launch(Object.assign({userDataDir}, headlessOptions));
|
||||||
const headlessPage = await headlessBrowser.newPage();
|
const headlessPage = await headlessBrowser.defaultContext().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 headlessBrowser.close();
|
||||||
|
|
|
||||||
|
|
@ -124,13 +124,13 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
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 browser = await playwright.launch(options);
|
||||||
const page = await browser.newPage();
|
const page = await browser.defaultContext().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 browser.close();
|
||||||
|
|
||||||
const browser2 = await playwright.launch(options);
|
const browser2 = await playwright.launch(options);
|
||||||
const page2 = await browser2.newPage();
|
const page2 = await browser2.defaultContext().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 browser2.close();
|
||||||
|
|
@ -142,13 +142,13 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
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 browser = await playwright.launch(options);
|
||||||
const page = await browser.newPage();
|
const page = await browser.defaultContext().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 browser.close();
|
||||||
|
|
||||||
const browser2 = await playwright.launch(options);
|
const browser2 = await playwright.launch(options);
|
||||||
const page2 = await browser2.newPage();
|
const page2 = await browser2.defaultContext().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 browser2.close();
|
||||||
|
|
@ -161,7 +161,7 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
it('should support the pipe option', async() => {
|
it('should support the pipe option', async() => {
|
||||||
const options = Object.assign({pipe: true}, defaultBrowserOptions);
|
const options = Object.assign({pipe: true}, defaultBrowserOptions);
|
||||||
const browser = await playwright.launch(options);
|
const browser = await playwright.launch(options);
|
||||||
expect((await browser.pages()).length).toBe(1);
|
expect((await browser.defaultContext().pages()).length).toBe(1);
|
||||||
expect(browser.chromium.wsEndpoint()).toBe('');
|
expect(browser.chromium.wsEndpoint()).toBe('');
|
||||||
const page = await browser.newPage();
|
const page = await browser.newPage();
|
||||||
expect(await page.evaluate('11 * 11')).toBe(121);
|
expect(await page.evaluate('11 * 11')).toBe(121);
|
||||||
|
|
|
||||||
|
|
@ -70,8 +70,8 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
|
||||||
await page.click('button');
|
await page.click('button');
|
||||||
expect(await page.evaluate(() => result)).toBe('Clicked');
|
expect(await page.evaluate(() => result)).toBe('Clicked');
|
||||||
});
|
});
|
||||||
it.skip(FFOX)('should click with disabled javascript', async({page, server}) => {
|
it.skip(FFOX)('should click with disabled javascript', async({newPage, server}) => {
|
||||||
await page.setJavaScriptEnabled(false);
|
const page = await newPage({ javaScriptEnabled: false });
|
||||||
await page.goto(server.PREFIX + '/wrappedlink.html');
|
await page.goto(server.PREFIX + '/wrappedlink.html');
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
page.click('a'),
|
page.click('a'),
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ module.exports.addTests = function ({ testRunner, expect, defaultBrowserOptions,
|
||||||
const {it, fit, xit} = testRunner;
|
const {it, fit, xit} = testRunner;
|
||||||
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
||||||
|
|
||||||
describe('DefaultBrowserContext', function() {
|
describe('defaultContext()', function() {
|
||||||
beforeEach(async state => {
|
beforeEach(async state => {
|
||||||
state.browser = await playwright.launch(defaultBrowserOptions);
|
state.browser = await playwright.launch(defaultBrowserOptions);
|
||||||
state.page = await state.browser.newPage();
|
state.page = await state.browser.newPage();
|
||||||
|
|
|
||||||
|
|
@ -81,14 +81,14 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Page.emulate', function() {
|
describe('Page.emulate', function() {
|
||||||
it.skip(WEBKIT)('should work', async({page, server}) => {
|
it.skip(WEBKIT)('should work', async({newPage, server}) => {
|
||||||
|
const page = await newPage({ viewport: iPhone.viewport, userAgent: iPhone.userAgent });
|
||||||
await page.goto(server.PREFIX + '/mobile.html');
|
await page.goto(server.PREFIX + '/mobile.html');
|
||||||
await page.emulate(iPhone);
|
|
||||||
expect(await page.evaluate(() => window.innerWidth)).toBe(375);
|
expect(await page.evaluate(() => window.innerWidth)).toBe(375);
|
||||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone');
|
expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone');
|
||||||
});
|
});
|
||||||
it.skip(WEBKIT)('should support clicking', async({page, server}) => {
|
it.skip(WEBKIT)('should support clicking', async({newPage, server}) => {
|
||||||
await page.emulate(iPhone);
|
const page = await newPage({ viewport: iPhone.viewport, userAgent: iPhone.userAgent });
|
||||||
await page.goto(server.PREFIX + '/input/button.html');
|
await page.goto(server.PREFIX + '/input/button.html');
|
||||||
const button = await page.$('button');
|
const button = await page.$('button');
|
||||||
await page.evaluate(button => button.style.marginTop = '200px', button);
|
await page.evaluate(button => button.style.marginTop = '200px', button);
|
||||||
|
|
@ -143,29 +143,32 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.skip(FFOX || WEBKIT)('Overrides.setTimezone', function() {
|
describe.skip(FFOX || WEBKIT)('BrowserContext({timezoneId})', function() {
|
||||||
it('should work', async({page, server}) => {
|
it('should work', async ({ newPage }) => {
|
||||||
page.evaluate(() => {
|
const func = () => new Date(1479579154987).toString();
|
||||||
globalThis.date = new Date(1479579154987);
|
{
|
||||||
});
|
const page = await newPage({ timezoneId: 'America/Jamaica' });
|
||||||
await page.overrides.setTimezone('America/Jamaica');
|
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)');
|
||||||
expect(await page.evaluate(() => date.toString())).toBe('Sat Nov 19 2016 13:12:34 GMT-0500 (Eastern Standard Time)');
|
}
|
||||||
|
{
|
||||||
await page.overrides.setTimezone('Pacific/Honolulu');
|
const page = await newPage({ timezoneId: 'Pacific/Honolulu' });
|
||||||
expect(await page.evaluate(() => date.toString())).toBe('Sat Nov 19 2016 08:12:34 GMT-1000 (Hawaii-Aleutian Standard Time)');
|
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 08:12:34 GMT-1000 (Hawaii-Aleutian Standard Time)');
|
||||||
|
}
|
||||||
await page.overrides.setTimezone('America/Buenos_Aires');
|
{
|
||||||
expect(await page.evaluate(() => date.toString())).toBe('Sat Nov 19 2016 15:12:34 GMT-0300 (Argentina Standard Time)');
|
const page = await newPage({ timezoneId: 'America/Buenos_Aires' });
|
||||||
|
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 15:12:34 GMT-0300 (Argentina Standard Time)');
|
||||||
await page.overrides.setTimezone('Europe/Berlin');
|
}
|
||||||
expect(await page.evaluate(() => date.toString())).toBe('Sat Nov 19 2016 19:12:34 GMT+0100 (Central European Standard Time)');
|
{
|
||||||
|
const page = await newPage({ timezoneId: 'Europe/Berlin' });
|
||||||
|
expect(await page.evaluate(func)).toBe('Sat Nov 19 2016 19:12:34 GMT+0100 (Central European Standard Time)');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw for invalid timezone IDs', async({page, server}) => {
|
it('should throw for invalid timezone IDs', async({newPage}) => {
|
||||||
let error = null;
|
let error = null;
|
||||||
await page.overrides.setTimezone('Foo/Bar').catch(e => error = e);
|
await newPage({ timezoneId: 'Foo/Bar' }).catch(e => error = e);
|
||||||
expect(error.message).toBe('Invalid timezone ID: Foo/Bar');
|
expect(error.message).toBe('Invalid timezone ID: Foo/Bar');
|
||||||
await page.overrides.setTimezone('Baz/Qux').catch(e => error = e);
|
await newPage({ timezoneId: 'Baz/Qux' }).catch(e => error = e);
|
||||||
expect(error.message).toBe('Invalid timezone ID: Baz/Qux');
|
expect(error.message).toBe('Invalid timezone ID: Baz/Qux');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -663,17 +663,13 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ignoreHTTPSErrors', function() {
|
describe('ignoreHTTPSErrors', function() {
|
||||||
it('should work with request interception', async({httpsServer}) => {
|
it('should work with request interception', async({newPage, httpsServer}) => {
|
||||||
const browser = await playwright.launch({...defaultBrowserOptions, ignoreHTTPSErrors: true});
|
const page = await newPage({ ignoreHTTPSErrors: true });
|
||||||
const context = await browser.newContext();
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
await page.interception.enable();
|
await page.interception.enable();
|
||||||
page.on('request', request => page.interception.continue(request));
|
page.on('request', request => page.interception.continue(request));
|
||||||
const response = await page.goto(httpsServer.EMPTY_PAGE);
|
const response = await page.goto(httpsServer.EMPTY_PAGE);
|
||||||
expect(response.status()).toBe(200);
|
expect(response.status()).toBe(200);
|
||||||
|
|
||||||
await browser.close();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
2
test/fixtures/dumpio.js
vendored
2
test/fixtures/dumpio.js
vendored
|
|
@ -1,7 +1,7 @@
|
||||||
(async() => {
|
(async() => {
|
||||||
const [, , playwrightRoot, options] = process.argv;
|
const [, , playwrightRoot, options] = process.argv;
|
||||||
const browser = await require(playwrightRoot).launch(JSON.parse(options));
|
const browser = await require(playwrightRoot).launch(JSON.parse(options));
|
||||||
const page = await browser.newPage();
|
const page = await browser.defaultContext().newPage();
|
||||||
await page.evaluate(() => console.error('message from dumpio'));
|
await page.evaluate(() => console.error('message from dumpio'));
|
||||||
await page.close();
|
await page.close();
|
||||||
await browser.close();
|
await browser.close();
|
||||||
|
|
|
||||||
|
|
@ -20,33 +20,18 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
const {it, fit, xit} = testRunner;
|
const {it, fit, xit} = testRunner;
|
||||||
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
const {beforeAll, beforeEach, afterAll, afterEach} = testRunner;
|
||||||
describe('ignoreHTTPSErrors', function() {
|
describe('ignoreHTTPSErrors', function() {
|
||||||
beforeAll(async state => {
|
it('should work', async({newPage, httpsServer}) => {
|
||||||
state.browser = await playwright.launch({...defaultBrowserOptions, ignoreHTTPSErrors: true});
|
|
||||||
});
|
|
||||||
afterAll(async state => {
|
|
||||||
await state.browser.close();
|
|
||||||
delete state.browser;
|
|
||||||
});
|
|
||||||
beforeEach(async state => {
|
|
||||||
state.context = await state.browser.newContext();
|
|
||||||
state.page = await state.context.newPage();
|
|
||||||
});
|
|
||||||
afterEach(async state => {
|
|
||||||
await state.context.close();
|
|
||||||
delete state.context;
|
|
||||||
delete state.page;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should work', async({page, httpsServer}) => {
|
|
||||||
let error = null;
|
let error = null;
|
||||||
|
const page = await newPage({ ignoreHTTPSErrors: true });
|
||||||
const response = await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e);
|
const response = await page.goto(httpsServer.EMPTY_PAGE).catch(e => error = e);
|
||||||
expect(error).toBe(null);
|
expect(error).toBe(null);
|
||||||
expect(response.ok()).toBe(true);
|
expect(response.ok()).toBe(true);
|
||||||
});
|
});
|
||||||
it('should work with mixed content', async({page, server, httpsServer}) => {
|
it('should work with mixed content', async({newPage, server, httpsServer}) => {
|
||||||
httpsServer.setRoute('/mixedcontent.html', (req, res) => {
|
httpsServer.setRoute('/mixedcontent.html', (req, res) => {
|
||||||
res.end(`<iframe src=${server.EMPTY_PAGE}></iframe>`);
|
res.end(`<iframe src=${server.EMPTY_PAGE}></iframe>`);
|
||||||
});
|
});
|
||||||
|
const page = await newPage({ ignoreHTTPSErrors: true });
|
||||||
await page.goto(httpsServer.PREFIX + '/mixedcontent.html', {waitUntil: 'load'});
|
await page.goto(httpsServer.PREFIX + '/mixedcontent.html', {waitUntil: 'load'});
|
||||||
expect(page.frames().length).toBe(2);
|
expect(page.frames().length).toBe(2);
|
||||||
// Make sure blocked iframe has functional execution context
|
// Make sure blocked iframe has functional execution context
|
||||||
|
|
|
||||||
|
|
@ -45,46 +45,6 @@ module.exports.addTests = function({testRunner, expect, defaultBrowserOptions, p
|
||||||
await playwright.launch(options).catch(e => waitError = e);
|
await playwright.launch(options).catch(e => waitError = e);
|
||||||
expect(waitError.message).toContain('Failed to launch');
|
expect(waitError.message).toContain('Failed to launch');
|
||||||
});
|
});
|
||||||
it('should set the default viewport', async() => {
|
|
||||||
const options = Object.assign({}, defaultBrowserOptions, {
|
|
||||||
defaultViewport: {
|
|
||||||
width: 456,
|
|
||||||
height: 789
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const browser = await playwright.launch(options);
|
|
||||||
const page = await browser.newPage();
|
|
||||||
expect(await page.evaluate('window.innerWidth')).toBe(456);
|
|
||||||
expect(await page.evaluate('window.innerHeight')).toBe(789);
|
|
||||||
await browser.close();
|
|
||||||
});
|
|
||||||
it('should disable the default viewport', async() => {
|
|
||||||
const options = Object.assign({}, defaultBrowserOptions, {
|
|
||||||
defaultViewport: null
|
|
||||||
});
|
|
||||||
const browser = await playwright.launch(options);
|
|
||||||
const page = await browser.newPage();
|
|
||||||
expect(page.viewport()).toBe(null);
|
|
||||||
await browser.close();
|
|
||||||
});
|
|
||||||
it('should take fullPage screenshots when defaultViewport is null', async({server}) => {
|
|
||||||
const options = Object.assign({}, defaultBrowserOptions, {
|
|
||||||
defaultViewport: null
|
|
||||||
});
|
|
||||||
const browser = await playwright.launch(options);
|
|
||||||
const page = await browser.newPage();
|
|
||||||
await page.goto(server.PREFIX + '/grid.html');
|
|
||||||
const sizeBefore = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
|
||||||
const screenshot = await page.screenshot({
|
|
||||||
fullPage: true
|
|
||||||
});
|
|
||||||
expect(screenshot).toBeInstanceOf(Buffer);
|
|
||||||
|
|
||||||
const sizeAfter = await page.evaluate(() => ({ width: document.body.offsetWidth, height: document.body.offsetHeight }));
|
|
||||||
expect(sizeBefore.width).toBe(sizeAfter.width);
|
|
||||||
expect(sizeBefore.height).toBe(sizeAfter.height);
|
|
||||||
await browser.close();
|
|
||||||
});
|
|
||||||
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 browser = await playwright.launch(defaultBrowserOptions);
|
||||||
const pages = (await browser.pages()).map(page => page.url());
|
const pages = (await browser.pages()).map(page => page.url());
|
||||||
|
|
|
||||||
|
|
@ -125,7 +125,7 @@ module.exports.addTests = function({testRunner, expect, playwright, FFOX, CHROME
|
||||||
const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'domcontentloaded'});
|
const response = await page.goto(server.EMPTY_PAGE, {waitUntil: 'domcontentloaded'});
|
||||||
expect(response.status()).toBe(200);
|
expect(response.status()).toBe(200);
|
||||||
});
|
});
|
||||||
it('should work when page calls history API in beforeunload', async({page, server}) => {
|
it.skip(WEBKIT)('should work when page calls history API in beforeunload', async({page, server}) => {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await page.evaluate(() => {
|
await page.evaluate(() => {
|
||||||
window.addEventListener('beforeunload', () => history.replaceState(null, 'initial', window.location.href), false);
|
window.addEventListener('beforeunload', () => history.replaceState(null, 'initial', window.location.href), false);
|
||||||
|
|
|
||||||
|
|
@ -166,7 +166,7 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF
|
||||||
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
expect(await page.evaluate(() => !!window.opener)).toBe(false);
|
||||||
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
|
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
|
||||||
});
|
});
|
||||||
it('should not treat navigations as new popups', async({page, server}) => {
|
it.skip(WEBKIT)('should not treat navigations as new popups', async({page, server}) => {
|
||||||
await page.goto(server.EMPTY_PAGE);
|
await page.goto(server.EMPTY_PAGE);
|
||||||
await page.setContent('<a target=_blank rel=noopener href="/one-style.html">yo</a>');
|
await page.setContent('<a target=_blank rel=noopener href="/one-style.html">yo</a>');
|
||||||
const [popup] = await Promise.all([
|
const [popup] = await Promise.all([
|
||||||
|
|
@ -585,33 +585,6 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Page.setUserAgent', function() {
|
|
||||||
it('should work', async({page, server}) => {
|
|
||||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('Mozilla');
|
|
||||||
await page.setUserAgent('foobar');
|
|
||||||
const [request] = await Promise.all([
|
|
||||||
server.waitForRequest('/empty.html'),
|
|
||||||
page.goto(server.EMPTY_PAGE),
|
|
||||||
]);
|
|
||||||
expect(request.headers['user-agent']).toBe('foobar');
|
|
||||||
});
|
|
||||||
it('should work for subframes', async({page, server}) => {
|
|
||||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('Mozilla');
|
|
||||||
await page.setUserAgent('foobar');
|
|
||||||
const [request] = await Promise.all([
|
|
||||||
server.waitForRequest('/empty.html'),
|
|
||||||
utils.attachFrame(page, 'frame1', server.EMPTY_PAGE),
|
|
||||||
]);
|
|
||||||
expect(request.headers['user-agent']).toBe('foobar');
|
|
||||||
});
|
|
||||||
it('should emulate device user-agent', async({page, server}) => {
|
|
||||||
await page.goto(server.PREFIX + '/mobile.html');
|
|
||||||
expect(await page.evaluate(() => navigator.userAgent)).not.toContain('iPhone');
|
|
||||||
await page.setUserAgent(playwright.devices['iPhone 6'].userAgent);
|
|
||||||
expect(await page.evaluate(() => navigator.userAgent)).toContain('iPhone');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Page.setContent', function() {
|
describe('Page.setContent', function() {
|
||||||
const expectedOutput = '<html><head></head><body><div>hello</div></body></html>';
|
const expectedOutput = '<html><head></head><body><div>hello</div></body></html>';
|
||||||
it('should work', async({page, server}) => {
|
it('should work', async({page, server}) => {
|
||||||
|
|
@ -705,64 +678,6 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Page.setBypassCSP', function() {
|
|
||||||
it('should bypass CSP meta tag', async({page, server}) => {
|
|
||||||
// Make sure CSP prohibits addScriptTag.
|
|
||||||
await page.goto(server.PREFIX + '/csp.html');
|
|
||||||
await page.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
|
||||||
expect(await page.evaluate(() => window.__injected)).toBe(undefined);
|
|
||||||
|
|
||||||
// By-pass CSP and try one more time.
|
|
||||||
await page.setBypassCSP(true);
|
|
||||||
await page.reload();
|
|
||||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
|
||||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should bypass CSP header', async({page, server}) => {
|
|
||||||
// Make sure CSP prohibits addScriptTag.
|
|
||||||
server.setCSP('/empty.html', 'default-src "self"');
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
|
||||||
await page.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
|
||||||
expect(await page.evaluate(() => window.__injected)).toBe(undefined);
|
|
||||||
|
|
||||||
// By-pass CSP and try one more time.
|
|
||||||
await page.setBypassCSP(true);
|
|
||||||
await page.reload();
|
|
||||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
|
||||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should bypass after cross-process navigation', async({page, server}) => {
|
|
||||||
await page.setBypassCSP(true);
|
|
||||||
await page.goto(server.PREFIX + '/csp.html');
|
|
||||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
|
||||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
|
||||||
|
|
||||||
await page.goto(server.CROSS_PROCESS_PREFIX + '/csp.html');
|
|
||||||
await page.addScriptTag({content: 'window.__injected = 42;'});
|
|
||||||
expect(await page.evaluate(() => window.__injected)).toBe(42);
|
|
||||||
});
|
|
||||||
it('should bypass CSP in iframes as well', async({page, server}) => {
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
|
||||||
{
|
|
||||||
// Make sure CSP prohibits addScriptTag in an iframe.
|
|
||||||
const frame = await utils.attachFrame(page, 'frame1', server.PREFIX + '/csp.html');
|
|
||||||
await frame.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
|
||||||
expect(await frame.evaluate(() => window.__injected)).toBe(undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
// By-pass CSP and try one more time.
|
|
||||||
await page.setBypassCSP(true);
|
|
||||||
await page.reload();
|
|
||||||
|
|
||||||
{
|
|
||||||
const frame = await utils.attachFrame(page, 'frame1', server.PREFIX + '/csp.html');
|
|
||||||
await frame.addScriptTag({content: 'window.__injected = 42;'}).catch(e => void e);
|
|
||||||
expect(await frame.evaluate(() => window.__injected)).toBe(42);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Page.addScriptTag', function() {
|
describe('Page.addScriptTag', function() {
|
||||||
it('should throw an error if no options are provided', async({page, server}) => {
|
it('should throw an error if no options are provided', async({page, server}) => {
|
||||||
|
|
@ -924,27 +839,6 @@ module.exports.addTests = function({testRunner, expect, headless, playwright, FF
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Page.setJavaScriptEnabled', function() {
|
|
||||||
it('should work', async({page, server}) => {
|
|
||||||
await page.setJavaScriptEnabled(false);
|
|
||||||
await page.goto('data:text/html, <script>var something = "forbidden"</script>');
|
|
||||||
let error = null;
|
|
||||||
await page.evaluate('something').catch(e => error = e);
|
|
||||||
if (WEBKIT)
|
|
||||||
expect(error.message).toContain('Can\'t find variable: something');
|
|
||||||
else
|
|
||||||
expect(error.message).toContain('something is not defined');
|
|
||||||
|
|
||||||
await page.setJavaScriptEnabled(true);
|
|
||||||
await page.goto('data:text/html, <script>var something = "forbidden"</script>');
|
|
||||||
expect(await page.evaluate('something')).toBe('forbidden');
|
|
||||||
});
|
|
||||||
it('should be able to navigate after disabling javascript', async({page, server}) => {
|
|
||||||
await page.setJavaScriptEnabled(false);
|
|
||||||
await page.goto(server.EMPTY_PAGE);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Page.setCacheEnabled', function() {
|
describe('Page.setCacheEnabled', function() {
|
||||||
// FIXME: 'if-modified-since' is not set for some reason even if cache is on.
|
// FIXME: 'if-modified-since' is not set for some reason even if cache is on.
|
||||||
it.skip(WEBKIT)('should enable or disable the cache based on the state passed', async({page, server}) => {
|
it.skip(WEBKIT)('should enable or disable the cache based on the state passed', async({page, server}) => {
|
||||||
|
|
|
||||||
|
|
@ -98,36 +98,51 @@ module.exports.addTests = ({testRunner, product, playwrightPath}) => {
|
||||||
state.browser = null;
|
state.browser = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!WEBKIT) {
|
beforeEach(async(state, test) => {
|
||||||
beforeEach(async(state, test) => {
|
const pages = [];
|
||||||
const rl = require('readline').createInterface({input: state.browser.process().stderr});
|
const contexts = [];
|
||||||
|
const onLine = (line) => test.output += line + '\n';
|
||||||
|
|
||||||
|
let rl;
|
||||||
|
if (!WEBKIT) {
|
||||||
|
rl = require('readline').createInterface({ input: state.browser.process().stderr });
|
||||||
test.output = '';
|
test.output = '';
|
||||||
rl.on('line', onLine);
|
rl.on('line', onLine);
|
||||||
state.tearDown = () => {
|
}
|
||||||
|
|
||||||
|
state.tearDown = async () => {
|
||||||
|
await Promise.all(pages.map(p => p.close()));
|
||||||
|
await Promise.all(contexts.map(c => c.close()));
|
||||||
|
if (!WEBKIT) {
|
||||||
rl.removeListener('line', onLine);
|
rl.removeListener('line', onLine);
|
||||||
rl.close();
|
rl.close();
|
||||||
};
|
|
||||||
function onLine(line) {
|
|
||||||
test.output += line + '\n';
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}
|
|
||||||
|
|
||||||
if (!WEBKIT) {
|
state.newContext = async (options) => {
|
||||||
afterEach(async state => {
|
const context = await state.browser.newContext(options);
|
||||||
state.tearDown();
|
contexts.push(context);
|
||||||
});
|
return context;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
state.newPage = async (options) => {
|
||||||
|
const page = await state.browser.newPage(options);
|
||||||
|
pages.push(page);
|
||||||
|
return page;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async state => {
|
||||||
|
await state.tearDown();
|
||||||
|
});
|
||||||
|
|
||||||
describe('Page', function() {
|
describe('Page', function() {
|
||||||
beforeEach(async state => {
|
beforeEach(async state => {
|
||||||
state.context = await state.browser.newContext();
|
state.context = await state.newContext();
|
||||||
state.page = await state.context.newPage();
|
state.page = await state.context.newPage();
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(async state => {
|
afterEach(async state => {
|
||||||
// This closes all pages.
|
|
||||||
await state.context.close();
|
|
||||||
state.context = null;
|
state.context = null;
|
||||||
state.page = null;
|
state.page = null;
|
||||||
});
|
});
|
||||||
|
|
@ -172,12 +187,12 @@ module.exports.addTests = ({testRunner, product, playwrightPath}) => {
|
||||||
|
|
||||||
// Browser-level tests that are given a browser.
|
// Browser-level tests that are given a browser.
|
||||||
require('./browsercontext.spec.js').addTests(testOptions);
|
require('./browsercontext.spec.js').addTests(testOptions);
|
||||||
|
require('./ignorehttpserrors.spec.js').addTests(testOptions);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Top-level tests that launch Browser themselves.
|
// Top-level tests that launch Browser themselves.
|
||||||
require('./defaultbrowsercontext.spec.js').addTests(testOptions);
|
require('./defaultbrowsercontext.spec.js').addTests(testOptions);
|
||||||
require('./fixtures.spec.js').addTests(testOptions);
|
require('./fixtures.spec.js').addTests(testOptions);
|
||||||
require('./ignorehttpserrors.spec.js').addTests(testOptions);
|
|
||||||
require('./launcher.spec.js').addTests(testOptions);
|
require('./launcher.spec.js').addTests(testOptions);
|
||||||
|
|
||||||
if (CHROME) {
|
if (CHROME) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue