diff --git a/.eslintrc.js b/.eslintrc.js
index a1de9853eeffd..a119974e1fed2 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -166,6 +166,8 @@ module.exports = {
default: 'array-simple',
},
],
+ // By default this is a warning but we want it to error.
+ '@typescript-eslint/explicit-module-boundary-types': 2,
},
},
{
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 65edf9aa5b24f..51e5d7e8dd8c2 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -14,9 +14,9 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- # Include all major maintenance + active LTS Node.js versions.
+ # Include all major maintenance + active LTS + current Node.js versions.
# https://github.com/nodejs/Release#release-schedule
- node: [10, 12, 14]
+ node: [12, 14, 16]
steps:
- name: Checkout
uses: actions/checkout@v2
@@ -42,16 +42,20 @@ jobs:
- name: Run code checks
run: |
+ npm run ensure-pinned-deps
npm run lint
npm run generate-docs
npm run ensure-correct-devtools-protocol-revision
npm run test-types-file
- name: Run unit tests
+ uses: nick-invision/retry@v2
env:
CHROMIUM: true
- run: |
- xvfb-run --auto-servernum npm run unit
+ with:
+ max_attempts: 3
+ command: xvfb-run --auto-servernum npm run unit
+ timeout_minutes: 10
- name: Run unit tests with coverage
env:
@@ -61,11 +65,15 @@ jobs:
xvfb-run --auto-servernum npm run assert-unit-coverage
- name: Run unit tests on Firefox
+ uses: nick-invision/retry@v2
env:
FIREFOX: true
MOZ_WEBRENDER: 0
- run: |
- xvfb-run --auto-servernum npm run funit
+ with:
+ max_attempts: 3
+ timeout_minutes: 10
+ command: xvfb-run --auto-servernum npm run funit
+
- name: Run browser tests
run: |
npm run test-browser
@@ -93,7 +101,7 @@ jobs:
with:
# Test only the oldest maintenance LTS Node.js version.
# https://github.com/nodejs/Release#release-schedule
- node-version: 10
+ node-version: 12
- name: Install dependencies
run: |
@@ -114,11 +122,11 @@ jobs:
npm run unit
- name: Run unit tests on Firefox
- env:
- FIREFOX: true
- MOZ_WEBRENDER: 0
- run: |
- npm run funit
+ uses: nick-invision/retry@v2
+ with:
+ max_attempts: 3
+ timeout_minutes: 10
+ command: npm run funit
windows:
# https://github.com/actions/virtual-environments#available-environments
@@ -134,7 +142,7 @@ jobs:
with:
# Test only the oldest maintenance LTS Node.js version.
# https://github.com/nodejs/Release#release-schedule
- node-version: 10
+ node-version: 12
- name: Install dependencies
run: |
@@ -157,8 +165,11 @@ jobs:
npm run unit
- name: Run unit tests on Firefox
+ uses: nick-invision/retry@v2
env:
FIREFOX: true
MOZ_WEBRENDER: 0
- run: |
- npm run funit
+ with:
+ max_attempts: 3
+ timeout_minutes: 10
+ command: npm run funit
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 51ae72fa6b0e3..6c675eae370d5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,30 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
+## [10.0.0](https://github.com/puppeteer/puppeteer/compare/v9.1.1...v10.0.0) (2021-05-31)
+
+
+### ⚠ BREAKING CHANGES
+
+* Node.js 10 is no longer supported.
+
+### Features
+
+* **chromium:** roll to Chromium 92.0.4512.0 (r884014) ([#7288](https://github.com/puppeteer/puppeteer/issues/7288)) ([f863f4b](https://github.com/puppeteer/puppeteer/commit/f863f4bfe015e57ea1f9fbb322f1cedee468b857))
+* **requestinterception:** remove cacheSafe flag ([#7217](https://github.com/puppeteer/puppeteer/issues/7217)) ([d01aa6c](https://github.com/puppeteer/puppeteer/commit/d01aa6c84a1e41f15ffed3a8d36ad26a404a7187))
+* expose other sessions from connection ([#6863](https://github.com/puppeteer/puppeteer/issues/6863)) ([cb285a2](https://github.com/puppeteer/puppeteer/commit/cb285a237921259eac99ade1d8b5550e068a55eb))
+* **launcher:** add new launcher option `waitForInitialPage` ([#7105](https://github.com/puppeteer/puppeteer/issues/7105)) ([2605309](https://github.com/puppeteer/puppeteer/commit/2605309f74b43da160cda4d214016e4422bf7676)), closes [#3630](https://github.com/puppeteer/puppeteer/issues/3630)
+
+
+### Bug Fixes
+
+* added comments for browsercontext, startCSSCoverage, and startJSCoverage. ([#7264](https://github.com/puppeteer/puppeteer/issues/7264)) ([b750397](https://github.com/puppeteer/puppeteer/commit/b75039746ac6bddf1411538242b5e70b0f2e6e8a))
+* modified comment for method product, platform and newPage ([#7262](https://github.com/puppeteer/puppeteer/issues/7262)) ([159d283](https://github.com/puppeteer/puppeteer/commit/159d2835450697dabea6f9adf6e67d158b5b8ae3))
+* **requestinterception:** fix font loading issue ([#7060](https://github.com/puppeteer/puppeteer/issues/7060)) ([c9978d2](https://github.com/puppeteer/puppeteer/commit/c9978d20d5584c9fd2dc902e4b4ac86ed8ea5d6e)), closes [/github.com/puppeteer/puppeteer/pull/6996#issuecomment-811546501](https://github.com/puppeteer//github.com/puppeteer/puppeteer/pull/6996/issues/issuecomment-811546501) [/github.com/puppeteer/puppeteer/pull/6996#issuecomment-813797393](https://github.com/puppeteer//github.com/puppeteer/puppeteer/pull/6996/issues/issuecomment-813797393) [#7038](https://github.com/puppeteer/puppeteer/issues/7038)
+
+
+* drop support for Node.js 10 ([#7200](https://github.com/puppeteer/puppeteer/issues/7200)) ([97c9fe2](https://github.com/puppeteer/puppeteer/commit/97c9fe2520723d45a5a86da06b888ae888d400be)), closes [#6753](https://github.com/puppeteer/puppeteer/issues/6753)
+
### [9.1.1](https://github.com/puppeteer/puppeteer/compare/v9.1.0...v9.1.1) (2021-05-05)
diff --git a/README.md b/README.md
index 34e20813405a0..d40f06c038669 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
-###### [API](https://github.com/puppeteer/puppeteer/blob/v9.1.1/docs/api.md) | [FAQ](#faq) | [Contributing](https://github.com/puppeteer/puppeteer/blob/main/CONTRIBUTING.md) | [Troubleshooting](https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md)
+###### [API](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md) | [FAQ](#faq) | [Contributing](https://github.com/puppeteer/puppeteer/blob/main/CONTRIBUTING.md) | [Troubleshooting](https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md)
> Puppeteer is a Node library which provides a high-level API to control Chrome or Chromium over the [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/). Puppeteer runs [headless](https://developers.google.com/web/updates/2017/04/headless-chrome) by default, but can be configured to run full (non-headless) Chrome or Chromium.
@@ -41,7 +41,7 @@ npm i puppeteer
# or "yarn add puppeteer"
```
-Note: When you install Puppeteer, it downloads a recent version of Chromium (~170MB Mac, ~282MB Linux, ~280MB Win) that is guaranteed to work with the API. To skip the download, or to download a different browser, see [Environment variables](https://github.com/puppeteer/puppeteer/blob/v9.1.1/docs/api.md#environment-variables).
+Note: When you install Puppeteer, it downloads a recent version of Chromium (~170MB Mac, ~282MB Linux, ~280MB Win) that is guaranteed to work with the API. To skip the download, or to download a different browser, see [Environment variables](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#environment-variables).
### puppeteer-core
@@ -66,7 +66,7 @@ Note: Prior to v1.18.1, Puppeteer required at least Node v6.4.0. Versions from v
Node 8.9.0+. Starting from v3.0.0 Puppeteer starts to rely on Node 10.18.1+. All examples below use async/await which is only supported in Node v7.6.0 or greater.
Puppeteer will be familiar to people using other browser testing frameworks. You create an instance
-of `Browser`, open pages, and then manipulate them with [Puppeteer's API](https://github.com/puppeteer/puppeteer/blob/v9.1.1/docs/api.md#).
+of `Browser`, open pages, and then manipulate them with [Puppeteer's API](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#).
**Example** - navigating to https://example.com and saving a screenshot as _example.png_:
@@ -91,7 +91,7 @@ Execute script on the command line
node example.js
```
-Puppeteer sets an initial page size to 800×600px, which defines the screenshot size. The page size can be customized with [`Page.setViewport()`](https://github.com/puppeteer/puppeteer/blob/v9.1.1/docs/api.md#pagesetviewportviewport).
+Puppeteer sets an initial page size to 800×600px, which defines the screenshot size. The page size can be customized with [`Page.setViewport()`](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#pagesetviewportviewport).
**Example** - create a PDF.
@@ -118,7 +118,7 @@ Execute script on the command line
node hn.js
```
-See [`Page.pdf()`](https://github.com/puppeteer/puppeteer/blob/v9.1.1/docs/api.md#pagepdfoptions) for more information about creating pdfs.
+See [`Page.pdf()`](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#pagepdfoptions) for more information about creating pdfs.
**Example** - evaluate script in the context of the page
@@ -153,7 +153,7 @@ Execute script on the command line
node get-dimensions.js
```
-See [`Page.evaluate()`](https://github.com/puppeteer/puppeteer/blob/v9.1.1/docs/api.md#pageevaluatepagefunction-args) for more information on `evaluate` and related methods like `evaluateOnNewDocument` and `exposeFunction`.
+See [`Page.evaluate()`](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#pageevaluatepagefunction-args) for more information on `evaluate` and related methods like `evaluateOnNewDocument` and `exposeFunction`.
@@ -163,7 +163,7 @@ See [`Page.evaluate()`](https://github.com/puppeteer/puppeteer/blob/v9.1.1/docs/
**1. Uses Headless mode**
-Puppeteer launches Chromium in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). To launch a full version of Chromium, set the [`headless` option](https://github.com/puppeteer/puppeteer/blob/v9.1.1/docs/api.md#puppeteerlaunchoptions) when launching a browser:
+Puppeteer launches Chromium in [headless mode](https://developers.google.com/web/updates/2017/04/headless-chrome). To launch a full version of Chromium, set the [`headless` option](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#puppeteerlaunchoptions) when launching a browser:
```js
const browser = await puppeteer.launch({ headless: false }); // default is true
@@ -179,7 +179,7 @@ pass in the executable's path when creating a `Browser` instance:
const browser = await puppeteer.launch({ executablePath: '/path/to/Chrome' });
```
-You can also use Puppeteer with Firefox Nightly (experimental support). See [`Puppeteer.launch()`](https://github.com/puppeteer/puppeteer/blob/v9.1.1/docs/api.md#puppeteerlaunchoptions) for more information.
+You can also use Puppeteer with Firefox Nightly (experimental support). See [`Puppeteer.launch()`](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#puppeteerlaunchoptions) for more information.
See [`this article`](https://www.howtogeek.com/202825/what%E2%80%99s-the-difference-between-chromium-and-chrome/) for a description of the differences between Chromium and Chrome. [`This article`](https://chromium.googlesource.com/chromium/src/+/master/docs/chromium_browser_vs_google_chrome.md) describes some differences for Linux users.
@@ -191,7 +191,7 @@ Puppeteer creates its own browser user profile which it **cleans up on every run
## Resources
-- [API Documentation](https://github.com/puppeteer/puppeteer/blob/v9.1.1/docs/api.md)
+- [API Documentation](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md)
- [Examples](https://github.com/puppeteer/puppeteer/tree/main/examples/)
- [Community list of Puppeteer resources](https://github.com/transitive-bullshit/awesome-puppeteer)
@@ -333,7 +333,7 @@ See [Contributing](https://github.com/puppeteer/puppeteer/blob/main/CONTRIBUTING
Official Firefox support is currently experimental. The ongoing collaboration with Mozilla aims to support common end-to-end testing use cases, for which developers expect cross-browser coverage. The Puppeteer team needs input from users to stabilize Firefox support and to bring missing APIs to our attention.
-From Puppeteer v2.1.0 onwards you can specify [`puppeteer.launch({product: 'firefox'})`](https://github.com/puppeteer/puppeteer/blob/v9.1.1/docs/api.md#puppeteerlaunchoptions) to run your Puppeteer scripts in Firefox Nightly, without any additional custom patches. While [an older experiment](https://www.npmjs.com/package/puppeteer-firefox) required a patched version of Firefox, [the current approach](https://wiki.mozilla.org/Remote) works with “stock” Firefox.
+From Puppeteer v2.1.0 onwards you can specify [`puppeteer.launch({product: 'firefox'})`](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#puppeteerlaunchoptions) to run your Puppeteer scripts in Firefox Nightly, without any additional custom patches. While [an older experiment](https://www.npmjs.com/package/puppeteer-firefox) required a patched version of Firefox, [the current approach](https://wiki.mozilla.org/Remote) works with “stock” Firefox.
We will continue to collaborate with other browser vendors to bring Puppeteer support to browsers such as Safari.
This effort includes exploration of a standard for executing cross-browser commands (instead of relying on the non-standard DevTools Protocol used by Chrome).
@@ -433,7 +433,7 @@ await page.evaluate(() => {
You may find that Puppeteer does not behave as expected when controlling pages that incorporate audio and video. (For example, [video playback/screenshots is likely to fail](https://github.com/puppeteer/puppeteer/issues/291).) There are two reasons for this:
-- Puppeteer is bundled with Chromium — not Chrome — and so by default, it inherits all of [Chromium's media-related limitations](https://www.chromium.org/audio-video). This means that Puppeteer does not support licensed formats such as AAC or H.264. (However, it is possible to force Puppeteer to use a separately-installed version Chrome instead of Chromium via the [`executablePath` option to `puppeteer.launch`](https://github.com/puppeteer/puppeteer/blob/v9.1.1/docs/api.md#puppeteerlaunchoptions). You should only use this configuration if you need an official release of Chrome that supports these media formats.)
+- Puppeteer is bundled with Chromium — not Chrome — and so by default, it inherits all of [Chromium's media-related limitations](https://www.chromium.org/audio-video). This means that Puppeteer does not support licensed formats such as AAC or H.264. (However, it is possible to force Puppeteer to use a separately-installed version Chrome instead of Chromium via the [`executablePath` option to `puppeteer.launch`](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md#puppeteerlaunchoptions). You should only use this configuration if you need an official release of Chrome that supports these media formats.)
- Since Puppeteer (in all configurations) controls a desktop version of Chromium/Chrome, features that are only supported by the mobile version of Chrome are not supported. This means that Puppeteer [does not support HTTP Live Streaming (HLS)](https://caniuse.com/#feat=http-live-streaming).
#### Q: I am having trouble installing / running Puppeteer in my test environment. Where should I look for help?
diff --git a/docs/api.md b/docs/api.md
index c807a23fb86d4..e5c4c74446e2e 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -1,4 +1,4 @@
-# Puppeteer API v9.1.1
+# Puppeteer API v10.0.0
@@ -10,6 +10,7 @@
- Releases per Chromium version:
+ * Chromium 92.0.4512.0 - [Puppeteer v10.0.0](https://github.com/puppeteer/puppeteer/blob/v10.0.0/docs/api.md)
* Chromium 91.0.4469.0 - [Puppeteer v9.0.0](https://github.com/puppeteer/puppeteer/blob/v9.0.0/docs/api.md)
* Chromium 90.0.4427.0 - [Puppeteer v8.0.0](https://github.com/puppeteer/puppeteer/blob/v8.0.0/docs/api.md)
* Chromium 90.0.4403.0 - [Puppeteer v7.0.0](https://github.com/puppeteer/puppeteer/blob/v7.0.0/docs/api.md)
@@ -174,7 +175,7 @@
* [page.setGeolocation(options)](#pagesetgeolocationoptions)
* [page.setJavaScriptEnabled(enabled)](#pagesetjavascriptenabledenabled)
* [page.setOfflineMode(enabled)](#pagesetofflinemodeenabled)
- * [page.setRequestInterception(value[, cacheSafe])](#pagesetrequestinterceptionvalue-cachesafe)
+ * [page.setRequestInterception(value)](#pagesetrequestinterceptionvalue)
* [page.setUserAgent(userAgent)](#pagesetuseragentuseragent)
* [page.setViewport(viewport)](#pagesetviewportviewport)
* [page.tap(selector)](#pagetapselector)
@@ -357,6 +358,7 @@
* [target.url()](#targeturl)
* [target.worker()](#targetworker)
- [class: CDPSession](#class-cdpsession)
+ * [cdpSession.connection()](#cdpsessionconnection)
* [cdpSession.detach()](#cdpsessiondetach)
* [cdpSession.send(method[, ...paramArgs])](#cdpsessionsendmethod-paramargs)
- [class: Coverage](#class-coverage)
@@ -625,6 +627,7 @@ try {
- `pipe` <[boolean]> Connects to the browser over a pipe instead of a WebSocket. Defaults to `false`.
- `extraPrefsFirefox` <[Object]> Additional [preferences](https://developer.mozilla.org/en-US/docs/Mozilla/Preferences/Preference_reference) that can be passed to Firefox (see `PUPPETEER_PRODUCT`)
- `targetFilter` [function]\([Protocol.Target.TargetInfo]\):[boolean]> Use this function to decide if Puppeteer should connect to the given target. If a `targetFilter` is provided, Puppeteer only connects to targets for which `targetFilter` returns `true`. By default, Puppeteer connects to all available targets.
+ - `waitForInitialPage` <[boolean]> Whether to wait for the initial page to be ready. Defaults to `true`.
- returns: <[Promise]<[Browser]>> Promise which resolves to browser instance.
You can use `ignoreDefaultArgs` to filter out `--mute-audio` from default arguments:
@@ -2187,6 +2190,8 @@ This setting will change the default maximum time for the following methods and
The extra HTTP headers will be sent with every request the page initiates.
+> **NOTE** All HTTP header names are lowercased. (HTTP headers are case-insensitive, so this shouldn’t impact your server code.)
+
> **NOTE** page.setExtraHTTPHeaders does not guarantee the order of headers in the outgoing requests.
#### page.setGeolocation(options)
@@ -2214,10 +2219,9 @@ await page.setGeolocation({ latitude: 59.95, longitude: 30.31667 });
- `enabled` <[boolean]> When `true`, enables offline mode for the page.
- returns: <[Promise]>
-#### page.setRequestInterception(value[, cacheSafe])
+#### page.setRequestInterception(value)
- `value` <[boolean]> Whether to enable request interception.
-- `cacheSafe` <[boolean]> Whether to trust browser caching. If set to false, enabling request interception disables page caching. Defaults to false.
- returns: <[Promise]>
Activating request interception enables `request.abort`, `request.continue` and
@@ -4557,6 +4561,12 @@ await client.send('Animation.setPlaybackRate', {
});
```
+#### cdpSession.connection()
+
+- returns: <[Connection]>
+
+Returns the underlying connection associated with the session. Can be used to obtain other related sessions.
+
#### cdpSession.detach()
- returns: <[Promise]>
diff --git a/package.json b/package.json
index 4c276e0056467..7222bbc64e947 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "puppeteer",
- "version": "9.1.1",
+ "version": "10.0.0",
"description": "A high-level API to control headless Chrome over the DevTools Protocol",
"main": "./cjs-entry.js",
"types": "lib/types.d.ts",
@@ -24,8 +24,8 @@
"eslint": "([ \"$CI\" = true ] && eslint --ext js --ext ts --quiet -f codeframe . || eslint --ext js --ext ts .)",
"eslint-fix": "eslint --ext js --ext ts --fix .",
"commitlint": "commitlint --from=HEAD~1",
- "markdownlint": "prettier --check **/README.md docs/api.md docs/troubleshooting.md",
- "markdownlint-fix": "prettier --write **/README.md docs/api.md docs/troubleshooting.md",
+ "markdownlint": "prettier --check **/README.md docs/troubleshooting.md",
+ "markdownlint-fix": "prettier --write **/README.md docs/troubleshooting.md",
"lint": "npm run eslint && npm run build && npm run doc && npm run commitlint && npm run markdownlint",
"doc": "node utils/doclint/cli.js",
"clean-lib": "rimraf lib",
@@ -38,6 +38,7 @@
"generate-d-ts": "api-extractor run --local --verbose",
"generate-docs": "npm run generate-d-ts && api-documenter markdown -i temp -o new-docs",
"ensure-correct-devtools-protocol-revision": "ts-node -s scripts/ensure-correct-devtools-protocol-package",
+ "ensure-pinned-deps": "ts-node -s scripts/ensure-pinned-deps",
"test-types-file": "ts-node -s scripts/test-ts-definition-files.ts",
"release": "node utils/remove_version_suffix.js && standard-version --commit-all"
},
@@ -55,65 +56,66 @@
"author": "The Chromium Authors",
"license": "Apache-2.0",
"dependencies": {
- "debug": "^4.1.0",
- "devtools-protocol": "0.0.869402",
- "extract-zip": "^2.0.0",
- "https-proxy-agent": "^5.0.0",
- "node-fetch": "^2.6.1",
- "pkg-dir": "^4.2.0",
- "progress": "^2.0.1",
- "proxy-from-env": "^1.1.0",
- "rimraf": "^3.0.2",
- "tar-fs": "^2.0.0",
- "unbzip2-stream": "^1.3.3",
- "ws": "^7.2.3"
+ "debug": "4.3.1",
+ "devtools-protocol": "0.0.883894",
+ "extract-zip": "2.0.1",
+ "https-proxy-agent": "5.0.0",
+ "node-fetch": "2.6.1",
+ "pkg-dir": "4.2.0",
+ "progress": "2.0.1",
+ "proxy-from-env": "1.1.0",
+ "rimraf": "3.0.2",
+ "tar-fs": "2.0.0",
+ "unbzip2-stream": "1.3.3",
+ "ws": "7.4.6"
},
"devDependencies": {
- "@commitlint/cli": "^11.0.0",
- "@commitlint/config-conventional": "^11.0.0",
- "@microsoft/api-documenter": "^7.12.7",
- "@microsoft/api-extractor": "^7.13.1",
+ "@commitlint/cli": "11.0.0",
+ "@commitlint/config-conventional": "11.0.0",
+ "@microsoft/api-documenter": "7.13.8",
+ "@microsoft/api-extractor": "7.15.1",
"@types/debug": "0.0.31",
- "@types/mime": "^2.0.0",
- "@types/mocha": "^7.0.2",
- "@types/node": "^14.0.13",
- "@types/proxy-from-env": "^1.0.1",
- "@types/rimraf": "^2.0.2",
- "@types/sinon": "^9.0.4",
- "@types/tar-fs": "^1.16.2",
- "@types/ws": "^7.2.4",
- "@typescript-eslint/eslint-plugin": "^4.4.0",
- "@typescript-eslint/parser": "^4.4.0",
- "@web/test-runner": "^0.12.15",
- "commonmark": "^0.28.1",
- "cross-env": "^7.0.2",
- "eslint": "^7.10.0",
- "eslint-config-prettier": "^6.12.0",
- "eslint-plugin-import": "^2.22.0",
- "eslint-plugin-mocha": "^8.0.0",
- "eslint-plugin-prettier": "^3.1.4",
- "eslint-plugin-unicorn": "^22.0.0",
- "esprima": "^4.0.0",
- "expect": "^25.2.7",
- "husky": "^4.3.0",
- "jpeg-js": "^0.3.7",
- "mime": "^2.0.3",
- "minimist": "^1.2.0",
- "mocha": "^8.2.0",
- "ncp": "^2.0.0",
- "pixelmatch": "^4.0.2",
- "pngjs": "^5.0.0",
- "prettier": "^2.1.2",
- "sinon": "^9.0.2",
- "source-map-support": "^0.5.19",
- "standard-version": "^9.0.0",
- "text-diff": "^1.0.1",
- "ts-node": "^9.0.0",
- "typescript": "^4.1.5"
+ "@types/mime": "2.0.3",
+ "@types/mocha": "7.0.2",
+ "@types/node": "14.14.45",
+ "@types/proxy-from-env": "1.0.1",
+ "@types/rimraf": "2.0.2",
+ "@types/sinon": "9.0.11",
+ "@types/tar-fs": "1.16.2",
+ "@types/ws": "7.4.4",
+ "@typescript-eslint/eslint-plugin": "4.23.0",
+ "@typescript-eslint/parser": "4.23.0",
+ "@web/test-runner": "0.12.20",
+ "commonmark": "0.29.3",
+ "cross-env": "7.0.3",
+ "eslint": "7.26.0",
+ "eslint-config-prettier": "8.3.0",
+ "eslint-plugin-import": "2.22.1",
+ "eslint-plugin-mocha": "8.1.0",
+ "eslint-plugin-prettier": "3.4.0",
+ "eslint-plugin-unicorn": "22.0.0",
+ "esprima": "4.0.0",
+ "expect": "25.2.7",
+ "husky": "4.3.8",
+ "jpeg-js": "0.3.7",
+ "mime": "2.5.2",
+ "minimist": "1.2.0",
+ "mocha": "8.4.0",
+ "ncp": "2.0.0",
+ "pixelmatch": "4.0.2",
+ "pngjs": "5.0.0",
+ "prettier": "2.3.0",
+ "sinon": "9.2.4",
+ "source-map-support": "0.5.19",
+ "standard-version": "9.3.0",
+ "text-diff": "1.0.1",
+ "ts-node": "9.1.1",
+ "typescript": "4.2.4"
},
"husky": {
"hooks": {
- "commit-msg": "commitlint --env HUSKY_GIT_PARAMS"
+ "commit-msg": "commitlint --env HUSKY_GIT_PARAMS",
+ "pre-push": "npm run ensure-pinned-deps"
}
}
}
diff --git a/scripts/ensure-pinned-deps.ts b/scripts/ensure-pinned-deps.ts
new file mode 100644
index 0000000000000..e51c8c60f047c
--- /dev/null
+++ b/scripts/ensure-pinned-deps.ts
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2021 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import packageJson from '../package.json';
+
+const allDeps = { ...packageJson.dependencies, ...packageJson.devDependencies };
+
+const invalidDeps = new Map();
+
+for (const [depKey, depValue] of Object.entries(allDeps)) {
+ if (/[0-9]/.test(depValue[0])) {
+ continue;
+ }
+
+ invalidDeps.set(depKey, depValue);
+}
+
+if (invalidDeps.size > 0) {
+ console.error('Found non-pinned dependencies in package.json:');
+ console.log([...invalidDeps.keys()].map((k) => ` ${k}`).join('\n'));
+ process.exit(1);
+}
+
+process.exit(0);
diff --git a/scripts/test-ts-definition-files.ts b/scripts/test-ts-definition-files.ts
index e65fbfa079c36..7e4e83737218a 100644
--- a/scripts/test-ts-definition-files.ts
+++ b/scripts/test-ts-definition-files.ts
@@ -10,6 +10,10 @@ const EXPECTED_ERRORS = new Map([
"bad.ts(6,35): error TS2551: Property 'launh' does not exist on type",
"bad.ts(8,29): error TS2551: Property 'devics' does not exist on type",
'bad.ts(12,39): error TS2554: Expected 0 arguments, but got 1.',
+ "bad.ts(20,5): error TS2345: Argument of type '(divElem: number) => any' is not assignable to parameter of type 'EvaluateFn",
+ "bad.ts(20,34): error TS2339: Property 'innerText' does not exist on type 'number'.",
+ "bad.ts(24,45): error TS2344: Type '(x: number) => string' does not satisfy the constraint 'EvaluateFn'.",
+ "bad.ts(27,34): error TS2339: Property 'innerText' does not exist on type 'number'.",
],
],
[
@@ -35,6 +39,8 @@ const EXPECTED_ERRORS = new Map([
"bad.js(7,29): error TS2551: Property 'devics' does not exist on type",
'bad.js(11,39): error TS2554: Expected 0 arguments, but got 1.',
"bad.js(15,9): error TS2322: Type 'ElementHandle | null' is not assignable to type 'ElementHandle'",
+ "bad.js(22,5): error TS2345: Argument of type '(divElem: number) => any' is not assignable to parameter of type 'EvaluateFn'.",
+ "bad.js(22,26): error TS2339: Property 'innerText' does not exist on type 'number'.",
],
],
[
@@ -67,6 +73,13 @@ const EXPECTED_ERRORS = new Map([
]);
const PROJECT_FOLDERS = [...EXPECTED_ERRORS.keys()];
+if (!process.env.CI) {
+ console.log(`IMPORTANT: this script assumes you have compiled Puppeteer
+and its types file before running. Make sure you have run:
+=> npm run tsc && npm run generate-d-ts
+before executing this script locally.`);
+}
+
function packPuppeteer() {
console.log('Packing Puppeteer');
const result = spawnSync('npm', ['pack'], {
diff --git a/src/common/Accessibility.ts b/src/common/Accessibility.ts
index 69684a47705e9..cf9a37fc5c284 100644
--- a/src/common/Accessibility.ts
+++ b/src/common/Accessibility.ts
@@ -437,10 +437,11 @@ class AXNode {
properties.get(key) as boolean;
for (const booleanProperty of booleanProperties) {
- // WebArea's treat focus differently than other nodes. They report whether
+ // RootWebArea's treat focus differently than other nodes. They report whether
// their frame has focus, not whether focus is specifically on the root
// node.
- if (booleanProperty === 'focused' && this._role === 'WebArea') continue;
+ if (booleanProperty === 'focused' && this._role === 'RootWebArea')
+ continue;
const value = getBooleanPropertyValue(booleanProperty);
if (!value) continue;
node[booleanProperty] = getBooleanPropertyValue(booleanProperty);
diff --git a/src/common/AriaQueryHandler.ts b/src/common/AriaQueryHandler.ts
index 9d8cf964a7c3e..8b2e4c921c274 100644
--- a/src/common/AriaQueryHandler.ts
+++ b/src/common/AriaQueryHandler.ts
@@ -32,7 +32,7 @@ async function queryAXTree(
role,
});
const filteredNodes: Protocol.Accessibility.AXNode[] = nodes.filter(
- (node: Protocol.Accessibility.AXNode) => node.role.value !== 'text'
+ (node: Protocol.Accessibility.AXNode) => node.role.value !== 'StaticText'
);
return filteredNodes;
}
@@ -52,7 +52,8 @@ function parseAriaSelector(selector: string): ariaQueryOption {
const normalize = (value: string): string => value.replace(/ +/g, ' ').trim();
const knownAttributes = new Set(['name', 'role']);
const queryOptions: ariaQueryOption = {};
- const attributeRegexp = /\[\s*(?\w+)\s*=\s*"(?\\.|[^"\\]*)"\s*\]/g;
+ const attributeRegexp =
+ /\[\s*(?\w+)\s*=\s*"(?\\.|[^"\\]*)"\s*\]/g;
const defaultName = selector.replace(
attributeRegexp,
(_, attribute: string, value: string) => {
diff --git a/src/common/Browser.ts b/src/common/Browser.ts
index ea40dd317a262..d9c9d29737a1f 100644
--- a/src/common/Browser.ts
+++ b/src/common/Browser.ts
@@ -417,7 +417,8 @@ export class Browser extends EventEmitter {
}
/**
- * Creates a {@link Page} in the default browser context.
+ * Promise which resolves to a new {@link Page} object. The Page is created in
+ * a default browser context.
*/
async newPage(): Promise {
return this._defaultContext.newPage();
@@ -722,9 +723,8 @@ export class BrowserContext extends EventEmitter {
permissions: Permission[]
): Promise {
const protocolPermissions = permissions.map((permission) => {
- const protocolPermission = WEB_PERMISSION_TO_PROTOCOL_PERMISSION.get(
- permission
- );
+ const protocolPermission =
+ WEB_PERMISSION_TO_PROTOCOL_PERMISSION.get(permission);
if (!protocolPermission)
throw new Error('Unknown permission: ' + permission);
return protocolPermission;
diff --git a/src/common/BrowserConnector.ts b/src/common/BrowserConnector.ts
index 58cdf4ce6ace0..3a707b5f300da 100644
--- a/src/common/BrowserConnector.ts
+++ b/src/common/BrowserConnector.ts
@@ -89,16 +89,14 @@ export const connectToBrowser = async (
connection = new Connection('', transport, slowMo);
} else if (browserWSEndpoint) {
const WebSocketClass = await getWebSocketTransportClass();
- const connectionTransport: ConnectionTransport = await WebSocketClass.create(
- browserWSEndpoint
- );
+ const connectionTransport: ConnectionTransport =
+ await WebSocketClass.create(browserWSEndpoint);
connection = new Connection(browserWSEndpoint, connectionTransport, slowMo);
} else if (browserURL) {
const connectionURL = await getWSEndpoint(browserURL);
const WebSocketClass = await getWebSocketTransportClass();
- const connectionTransport: ConnectionTransport = await WebSocketClass.create(
- connectionURL
- );
+ const connectionTransport: ConnectionTransport =
+ await WebSocketClass.create(connectionURL);
connection = new Connection(connectionURL, connectionTransport, slowMo);
}
diff --git a/src/common/Connection.ts b/src/common/Connection.ts
index 4f11556a28a05..34aab7df5c093 100644
--- a/src/common/Connection.ts
+++ b/src/common/Connection.ts
@@ -125,11 +125,21 @@ export class Connection extends EventEmitter {
sessionId
);
this._sessions.set(sessionId, session);
+ this.emit('sessionattached', session);
+ const parentSession = this._sessions.get(object.sessionId);
+ if (parentSession) {
+ parentSession.emit('sessionattached', session);
+ }
} else if (object.method === 'Target.detachedFromTarget') {
const session = this._sessions.get(object.params.sessionId);
if (session) {
session._onClosed();
this._sessions.delete(object.params.sessionId);
+ this.emit('sessiondetached', session);
+ const parentSession = this._sessions.get(object.sessionId);
+ if (parentSession) {
+ parentSession.emit('sessiondetached', session);
+ }
}
}
if (object.sessionId) {
@@ -253,6 +263,10 @@ export class CDPSession extends EventEmitter {
this._sessionId = sessionId;
}
+ connection(): Connection {
+ return this._connection;
+ }
+
send(
method: T,
...paramArgs: ProtocolMapping.Commands[T]['paramsType']
@@ -270,11 +284,7 @@ export class CDPSession extends EventEmitter {
const id = this._connection._rawSend({
sessionId: this._sessionId,
method,
- /* TODO(jacktfranklin@): once this Firefox bug is solved
- * we no longer need the `|| {}` check
- * https://bugzilla.mozilla.org/show_bug.cgi?id=1631570
- */
- params: params || {},
+ params,
});
return new Promise((resolve, reject) => {
diff --git a/src/common/Coverage.ts b/src/common/Coverage.ts
index c4910d45803df..06354a3da0a81 100644
--- a/src/common/Coverage.ts
+++ b/src/common/Coverage.ts
@@ -123,8 +123,8 @@ export class Coverage {
}
/**
- * @param options - defaults to
- * `{ resetOnNavigation : true, reportAnonymousScripts : false }`
+ * @param options - Set of configurable options for coverage defaults to `{
+ * resetOnNavigation : true, reportAnonymousScripts : false }`
* @returns Promise that resolves when coverage is started.
*
* @remarks
@@ -150,7 +150,8 @@ export class Coverage {
}
/**
- * @param options - defaults to `{ resetOnNavigation : true }`
+ * @param options - Set of configurable options for coverage, defaults to `{
+ * resetOnNavigation : true }`
* @returns Promise that resolves when coverage is started.
*/
async startCSSCoverage(options: CSSCoverageOptions = {}): Promise {
@@ -192,10 +193,8 @@ export class JSCoverage {
} = {}
): Promise {
assert(!this._enabled, 'JSCoverage is already enabled');
- const {
- resetOnNavigation = true,
- reportAnonymousScripts = false,
- } = options;
+ const { resetOnNavigation = true, reportAnonymousScripts = false } =
+ options;
this._resetOnNavigation = resetOnNavigation;
this._reportAnonymousScripts = reportAnonymousScripts;
this._enabled = true;
diff --git a/src/common/DOMWorld.ts b/src/common/DOMWorld.ts
index 7906d131b4178..69f927f345ed2 100644
--- a/src/common/DOMWorld.ts
+++ b/src/common/DOMWorld.ts
@@ -484,9 +484,8 @@ export class DOMWorld {
selector: string,
options: WaitForSelectorOptions
): Promise {
- const { updatedSelector, queryHandler } = getQueryHandlerAndSelector(
- selector
- );
+ const { updatedSelector, queryHandler } =
+ getQueryHandlerAndSelector(selector);
return queryHandler.waitFor(this, updatedSelector, options);
}
@@ -687,10 +686,8 @@ export class DOMWorld {
options: { polling?: string | number; timeout?: number } = {},
...args: SerializableOrJSHandle[]
): Promise {
- const {
- polling = 'raf',
- timeout = this._timeoutSettings.timeout(),
- } = options;
+ const { polling = 'raf', timeout = this._timeoutSettings.timeout() } =
+ options;
const waitTaskOptions: WaitTaskOptions = {
domWorld: this,
predicateBody: pageFunction,
diff --git a/src/common/EmulationManager.ts b/src/common/EmulationManager.ts
index f3130ef3c5a10..baae0b5c7105d 100644
--- a/src/common/EmulationManager.ts
+++ b/src/common/EmulationManager.ts
@@ -31,9 +31,10 @@ export class EmulationManager {
const width = viewport.width;
const height = viewport.height;
const deviceScaleFactor = viewport.deviceScaleFactor || 1;
- const screenOrientation: Protocol.Emulation.ScreenOrientation = viewport.isLandscape
- ? { angle: 90, type: 'landscapePrimary' }
- : { angle: 0, type: 'portraitPrimary' };
+ const screenOrientation: Protocol.Emulation.ScreenOrientation =
+ viewport.isLandscape
+ ? { angle: 90, type: 'landscapePrimary' }
+ : { angle: 0, type: 'portraitPrimary' };
const hasTouch = viewport.hasTouch || false;
await Promise.all([
diff --git a/src/common/EventEmitter.ts b/src/common/EventEmitter.ts
index e75377130f77b..91cf7e0904be9 100644
--- a/src/common/EventEmitter.ts
+++ b/src/common/EventEmitter.ts
@@ -21,7 +21,7 @@ export interface CommonEventEmitter {
*/
addListener(event: EventType, handler: Handler): CommonEventEmitter;
removeListener(event: EventType, handler: Handler): CommonEventEmitter;
- emit(event: EventType, eventData?: any): boolean;
+ emit(event: EventType, eventData?: unknown): boolean;
once(event: EventType, handler: Handler): CommonEventEmitter;
listenerCount(event: string): number;
@@ -55,7 +55,7 @@ export class EventEmitter implements CommonEventEmitter {
* Bind an event listener to fire when an event occurs.
* @param event - the event type you'd like to listen to. Can be a string or symbol.
* @param handler - the function to be called when the event occurs.
- * @returns `this` to enable you to chain calls.
+ * @returns `this` to enable you to chain method calls.
*/
on(event: EventType, handler: Handler): EventEmitter {
this.emitter.on(event, handler);
@@ -66,7 +66,7 @@ export class EventEmitter implements CommonEventEmitter {
* Remove an event listener from firing.
* @param event - the event type you'd like to stop listening to.
* @param handler - the function that should be removed.
- * @returns `this` to enable you to chain calls.
+ * @returns `this` to enable you to chain method calls.
*/
off(event: EventType, handler: Handler): EventEmitter {
this.emitter.off(event, handler);
@@ -75,7 +75,7 @@ export class EventEmitter implements CommonEventEmitter {
/**
* Remove an event listener.
- * @deprecated please use `off` instead.
+ * @deprecated please use {@link EventEmitter.off} instead.
*/
removeListener(event: EventType, handler: Handler): EventEmitter {
this.off(event, handler);
@@ -84,7 +84,7 @@ export class EventEmitter implements CommonEventEmitter {
/**
* Add an event listener.
- * @deprecated please use `on` instead.
+ * @deprecated please use {@link EventEmitter.on} instead.
*/
addListener(event: EventType, handler: Handler): EventEmitter {
this.on(event, handler);
@@ -98,7 +98,7 @@ export class EventEmitter implements CommonEventEmitter {
* @param eventData - any data you'd like to emit with the event
* @returns `true` if there are any listeners, `false` if there are not.
*/
- emit(event: EventType, eventData?: any): boolean {
+ emit(event: EventType, eventData?: unknown): boolean {
this.emitter.emit(event, eventData);
return this.eventListenersCount(event) > 0;
}
@@ -107,7 +107,7 @@ export class EventEmitter implements CommonEventEmitter {
* Like `on` but the listener will only be fired once and then it will be removed.
* @param event - the event you'd like to listen to
* @param handler - the handler function to run when the event occurs
- * @returns `this` to enable you to chain calls.
+ * @returns `this` to enable you to chain method calls.
*/
once(event: EventType, handler: Handler): EventEmitter {
const onceHandler: Handler = (eventData) => {
@@ -132,7 +132,7 @@ export class EventEmitter implements CommonEventEmitter {
* Removes all listeners. If given an event argument, it will remove only
* listeners for that event.
* @param event - the event to remove listeners for.
- * @returns `this` to enable you to chain calls.
+ * @returns `this` to enable you to chain method calls.
*/
removeAllListeners(event?: EventType): EventEmitter {
if (event) {
diff --git a/src/common/ExecutionContext.ts b/src/common/ExecutionContext.ts
index e8e2fbe48661b..f51b8a42ed5b3 100644
--- a/src/common/ExecutionContext.ts
+++ b/src/common/ExecutionContext.ts
@@ -267,10 +267,8 @@ export class ExecutionContext {
error.message += ' Are you passing a nested JSHandle?';
throw error;
}
- const {
- exceptionDetails,
- result: remoteObject,
- } = await callFunctionOnPromise.catch(rewriteError);
+ const { exceptionDetails, result: remoteObject } =
+ await callFunctionOnPromise.catch(rewriteError);
if (exceptionDetails)
throw new Error(
'Evaluation failed: ' + helper.getExceptionMessage(exceptionDetails)
diff --git a/src/common/FileChooser.ts b/src/common/FileChooser.ts
index eb3f4d91c811c..d1cbea73a38ad 100644
--- a/src/common/FileChooser.ts
+++ b/src/common/FileChooser.ts
@@ -76,7 +76,7 @@ export class FileChooser {
/**
* Closes the file chooser without selecting any files.
*/
- cancel() {
+ cancel(): void {
assert(
!this._handled,
'Cannot cancel FileChooser which is already handled!'
diff --git a/src/common/HTTPRequest.ts b/src/common/HTTPRequest.ts
index 7b581ee936fcb..7c4dcd7ed9cb1 100644
--- a/src/common/HTTPRequest.ts
+++ b/src/common/HTTPRequest.ts
@@ -191,14 +191,16 @@ export class HTTPRequest {
}
/**
- * @returns the response for this request, if a response has been received.
+ * @returns A matching `HTTPResponse` object, or null if the response has not
+ * been received yet.
*/
response(): HTTPResponse | null {
return this._response;
}
/**
- * @returns the frame that initiated the request.
+ * @returns the frame that initiated the request, or null if navigating to
+ * error pages.
*/
frame(): Frame | null {
return this._frame;
@@ -212,6 +214,7 @@ export class HTTPRequest {
}
/**
+ * A `redirectChain` is a chain of requests initiated to fetch a resource.
* @remarks
*
* `redirectChain` is shared between all the requests of the same chain.
diff --git a/src/common/JSHandle.ts b/src/common/JSHandle.ts
index ece483f265808..db83587be5aa9 100644
--- a/src/common/JSHandle.ts
+++ b/src/common/JSHandle.ts
@@ -105,7 +105,7 @@ export function createJSHandle(
*
* @public
*/
-export class JSHandle {
+export class JSHandle {
/**
* @internal
*/
@@ -154,7 +154,7 @@ export class JSHandle {
* ```
*/
- async evaluate(
+ async evaluate>(
pageFunction: T | string,
...args: SerializableOrJSHandle[]
): Promise>> {
@@ -193,7 +193,7 @@ export class JSHandle {
*/
async getProperty(propertyName: string): Promise {
const objectHandle = await this.evaluateHandle(
- (object: HTMLElement, propertyName: string) => {
+ (object: Element, propertyName: string) => {
const result = { __proto__: null };
result[propertyName] = object[propertyName];
return result;
@@ -237,8 +237,8 @@ export class JSHandle {
}
/**
- * Returns a JSON representation of the object.
- *
+ * @returns Returns a JSON representation of the object.If the object has a
+ * `toJSON` function, it will not be called.
* @remarks
*
* The JSON is generated by running {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify | JSON.stringify}
@@ -259,12 +259,13 @@ export class JSHandle {
}
/**
- * Returns either `null` or the object handle itself, if the object handle is
- * an instance of {@link ElementHandle}.
+ * @returns Either `null` or the object handle itself, if the object
+ * handle is an instance of {@link ElementHandle}.
*/
asElement(): ElementHandle | null {
- // This always returns null, but subclasses can override this and return an
- // ElementHandle.
+ /* This always returns null, but subclasses can override this and return an
+ ElementHandle.
+ */
return null;
}
@@ -328,7 +329,7 @@ export class JSHandle {
*/
export class ElementHandle<
ElementType extends Element = Element
-> extends JSHandle {
+> extends JSHandle {
private _page: Page;
private _frameManager: FrameManager;
@@ -521,24 +522,25 @@ export class ElementHandle<
'"'
);
- return this.evaluate<
- (element: HTMLSelectElement, values: string[]) => string[]
- >((element, values) => {
- if (element.nodeName.toLowerCase() !== 'select')
- throw new Error('Element is not a