Bug 1694506 - [puppeteer] Sync vendored puppeteer to v8.0.0 r=remote-protocol-reviewers,whimboo

Generated by running ./mach remote vendor-puppeteer and replacing itFailsFirefox to it etc...

Differential Revision: https://phabricator.services.mozilla.com/D108099
This commit is contained in:
Julian Descottes 2021-03-18 15:02:12 +00:00
parent bf279d8795
commit 3eac4f3ce3
91 changed files with 3350 additions and 4117 deletions

View File

@ -1,17 +0,0 @@
FROM node:10
RUN apt-get update && \
apt-get -y install xvfb gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 \
libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 \
libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 \
libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 \
libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget && \
rm -rf /var/lib/apt/lists/*
# Add user so we don't need --no-sandbox.
RUN groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser \
&& mkdir -p /home/pptruser/Downloads \
&& chown -R pptruser:pptruser /home/pptruser
# Run everything after as non-privileged user.
USER pptruser

View File

@ -1,17 +0,0 @@
FROM node:12
RUN apt-get update && \
apt-get -y install xvfb gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 \
libdbus-1-3 libexpat1 libfontconfig1 libgbm1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 \
libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 \
libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 \
libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget && \
rm -rf /var/lib/apt/lists/*
# Add user so we don't need --no-sandbox.
RUN groupadd -r pptruser && useradd -r -g pptruser -G audio,video pptruser \
&& mkdir -p /home/pptruser/Downloads \
&& chown -R pptruser:pptruser /home/pptruser
# Run everything after as non-privileged user.
USER pptruser

View File

@ -12,3 +12,4 @@ lib/
utils/doclint/generate_types/test/test.ts
vendor/
web-test-runner.config.mjs
test-ts-types/

View File

@ -0,0 +1,157 @@
name: run-checks
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
linux-main-checks:
# https://github.com/actions/virtual-environments#available-environments
runs-on: ubuntu-latest
strategy:
matrix:
# Include all major maintenance + active LTS Node.js versions.
# https://github.com/nodejs/Release#release-schedule
node: [10, 12, 14]
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 2
- name: Set up Node.js
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- name: Install dependencies
run: |
sudo apt-get install xvfb
# Ensure both a Chromium and a Firefox binary are available.
PUPPETEER_PRODUCT=firefox npm install
npm install
ls .local-chromium .local-firefox
- name: Build
run: |
npm run build
- name: Run code checks
run: |
npm run lint
npm run generate-docs
npm run ensure-correct-devtools-protocol-revision
npm run test-types-file
- name: Run unit tests
env:
CHROMIUM: true
run: |
xvfb-run --auto-servernum npm run unit
- name: Run unit tests with coverage
env:
CHROMIUM: true
run: |
xvfb-run --auto-servernum npm run unit-with-coverage
xvfb-run --auto-servernum npm run assert-unit-coverage
- name: Run unit tests on Firefox
env:
FIREFOX: true
MOZ_WEBRENDER: 0
run: |
xvfb-run --auto-servernum npm run funit
- name: Run browser tests
run: |
npm run test-browser
- name: Test bundling and installation
env:
CHROMIUM: true
run: |
# Note: this modifies package.json to test puppeteer-core.
npm run test-install
# Undo those changes.
git checkout --force
macos:
# https://github.com/actions/virtual-environments#available-environments
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 2
- name: Set up Node.js
uses: actions/setup-node@v1
with:
# Test only the oldest maintenance LTS Node.js version.
# https://github.com/nodejs/Release#release-schedule
node-version: 10
- name: Install dependencies
run: |
# Test platform-specific browser binary fetching for both
# Chromium and Firefox.
PUPPETEER_PRODUCT=firefox npm install
npm install
ls .local-chromium .local-firefox
- name: Build
run: |
npm run build
- name: Run unit tests
env:
CHROMIUM: true
run: |
npm run unit
- name: Run unit tests on Firefox
env:
FIREFOX: true
MOZ_WEBRENDER: 0
run: |
npm run funit
windows:
# https://github.com/actions/virtual-environments#available-environments
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 2
- name: Set up Node.js
uses: actions/setup-node@v1
with:
# Test only the oldest maintenance LTS Node.js version.
# https://github.com/nodejs/Release#release-schedule
node-version: 10
- name: Install dependencies
run: |
# Test platform-specific browser binary fetching for both
# Chromium and Firefox.
$env:PUPPETEER_PRODUCT='firefox'
npm install
Remove-Item Env:\PUPPETEER_PRODUCT
npm install
Get-ChildItem -Path .local-chromium,.local-firefox
- name: Build
run: |
npm run build
- name: Run unit tests
env:
CHROMIUM: true
run: |
npm run unit

View File

@ -3,7 +3,7 @@ name: publish-on-tag
on:
push:
tags:
- '*'
- '*'
jobs:
publish:
@ -19,6 +19,7 @@ jobs:
env:
NPM_TOKEN: ${{secrets.NPM_TOKEN_PUPPETEER}}
run: |
npm config set registry 'https://wombat-dressing-room.appspot.com/'
npm config set '//wombat-dressing-room.appspot.com/:_authToken' '${NPM_TOKEN}'
npm publish
- name: Publish puppeteer-core
@ -26,5 +27,6 @@ jobs:
NPM_TOKEN: ${{secrets.NPM_TOKEN_PUPPETEER_CORE}}
run: |
utils/prepare_puppeteer_core.js
npm config set registry 'https://wombat-dressing-room.appspot.com/'
npm config set '//wombat-dressing-room.appspot.com/:_authToken' '${NPM_TOKEN}'
npm publish

View File

@ -1,2 +1 @@
registry=https://wombat-dressing-room.appspot.com/
access=public

View File

@ -1,118 +0,0 @@
language: node_js
services: xvfb
# Throughout this file, the following `node_js` versions are being used:
#
# - node_js: '10' # The maintenance LTS version.
# - node_js: '12' # The oldest major active LTS version.
# - node_js: '14' # The newest major active LTS version.
jobs:
include:
- os: 'osx'
name: 'Unit tests: macOS/Chromium'
node_js: '10' # The maintenance LTS version.
osx_image: xcode11.4
env:
- CHROMIUM=true
before_install:
- PUPPETEER_PRODUCT=firefox npm install
script:
- ls .local-chromium .local-firefox
- npm run tsc
- npm run unit
- os: 'windows'
name: 'Unit tests: Windows/Chromium'
node_js: '10' # The maintenance LTS version.
env:
- CHROMIUM=true
before_install:
- PUPPETEER_PRODUCT=firefox npm install
script:
- ls .local-chromium .local-firefox
- npm run tsc
- travis_retry npm run unit
# Node <10.17's fs.promises module was experimental and doesn't behave as
# expected. This problem was fixed in Node 10.19, but we run the unit tests
# through on 10.15 to make sure we don't cause any regressions when using
# fs.promises. See https://github.com/puppeteer/puppeteer/issues/6548 for an
# example.
- node_js: '10.15.0'
name: 'Node 10.15 Unit tests: Linux/Chromium'
env:
- CHROMIUM=true
before_install:
- PUPPETEER_PRODUCT=firefox npm install
script:
- npm run unit
- node_js: '10' # The maintenance LTS version.
name: 'Unit tests [with coverage]: Linux/Chromium'
env:
- CHROMIUM=true
before_install:
- PUPPETEER_PRODUCT=firefox npm install
script:
- travis_retry npm run unit-with-coverage
- npm run assert-unit-coverage
- node_js: '12' # The oldest major active LTS version.
name: 'Unit tests [Node 12]: Linux/Chromium'
env:
- CHROMIUM=true
before_install:
- PUPPETEER_PRODUCT=firefox npm install
script:
- npm run unit
- node_js: '14' # The newest major active LTS version.
name: 'Unit tests [Node 14]: Linux/Chromium'
env:
- CHROMIUM=true
before_install:
- PUPPETEER_PRODUCT=firefox npm install
script:
- npm run unit
- node_js: '12' # The oldest major active LTS version.
name: 'Browser tests: Linux/Chromium'
addons:
chrome: stable
env:
- CHROMIUM=true
script:
- npm run test-browser
# This bot runs all the extra checks that aren't the main Puppeteer unit tests.
- node_js: '10' # The maintenance LTS version.
name: 'Extra tests: Linux/Chromium'
env:
- CHROMIUM=true
script:
- npm run lint
# Ensure that we can generate the new docs without erroring
- npm run generate-docs
- npm run ensure-correct-devtools-protocol-revision
# This bot runs separately as it changes package.json to test puppeteer-core
# and we don't want that leaking into other bots and causing issues.
- node_js: '10' # The maintenance LTS version.
name: 'Test bundling and install of packages'
env:
- CHROMIUM=true
script:
- npm run test-install
- node_js: '10' # The maintenance LTS version.
name: 'Unit tests: Linux/Firefox'
env:
- FIREFOX=true
before_install:
- PUPPETEER_PRODUCT=firefox npm install
script:
- npm run funit
notifications:
email: false

View File

@ -1,3 +0,0 @@
{
"releaseCommitMessageFormat": "chore(release): mark v{{currentTag}}"
}

View File

@ -0,0 +1,26 @@
/**
* Copyright 2020 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
*
* https://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.
*/
module.exports = {
releaseCommitMessageFormat: 'chore(release): mark v{{currentTag}}',
skip: {
tag: true,
},
scripts: {
prerelease: 'node utils/remove_version_suffix.js',
postbump: 'IS_RELEASE=true npm run doc && git add --update',
},
};

View File

@ -2,6 +2,109 @@
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.
## [8.0.0](https://github.com/puppeteer/puppeteer/compare/v7.1.0...v8.0.0) (2021-02-26)
### ⚠ BREAKING CHANGES
* renamed type `ChromeArgOptions` to `BrowserLaunchArgumentOptions`
* renamed type `BrowserOptions` to `BrowserConnectOptions`
### Features
* **chromium:** roll Chromium to r856583 ([#6927](https://github.com/puppeteer/puppeteer/issues/6927)) ([0c688bd](https://github.com/puppeteer/puppeteer/commit/0c688bd75ef1d1fc3afd14cbe8966757ecda68fb))
### Bug Fixes
* explicit HTTPRequest.resourceType type defs ([#6882](https://github.com/puppeteer/puppeteer/issues/6882)) ([ff26c62](https://github.com/puppeteer/puppeteer/commit/ff26c62647b60cd0d8d7ea66ee998adaadc3fcc2)), closes [#6854](https://github.com/puppeteer/puppeteer/issues/6854)
* expose `Viewport` type ([#6881](https://github.com/puppeteer/puppeteer/issues/6881)) ([be7c229](https://github.com/puppeteer/puppeteer/commit/be7c22933c1dcf5eee797d61463171bd0ef44582))
* improve TS types for launching browsers ([#6888](https://github.com/puppeteer/puppeteer/issues/6888)) ([98c8145](https://github.com/puppeteer/puppeteer/commit/98c81458c27f378eb66c38e1620e79e2ffde418e))
* move CI npm config out of .npmrc ([#6901](https://github.com/puppeteer/puppeteer/issues/6901)) ([f7de60b](https://github.com/puppeteer/puppeteer/commit/f7de60be22d9bc6433ada7bfefeaa7f6f6f62047))
## [7.1.0](https://github.com/puppeteer/puppeteer/compare/v7.0.4...v7.1.0) (2021-02-12)
### Features
* **page:** add color-gamut support to Page.emulateMediaFeatures ([#6857](https://github.com/puppeteer/puppeteer/issues/6857)) ([ad59357](https://github.com/puppeteer/puppeteer/commit/ad5935738d869cfce386a0d28b4bc6131457f962)), closes [#6761](https://github.com/puppeteer/puppeteer/issues/6761)
### Bug Fixes
* add favicon test asset ([#6868](https://github.com/puppeteer/puppeteer/issues/6868)) ([a63f53c](https://github.com/puppeteer/puppeteer/commit/a63f53c9380545550503f5539494c72c607e19ac))
* expose `ScreenshotOptions` type in type defs ([#6869](https://github.com/puppeteer/puppeteer/issues/6869)) ([63d48b2](https://github.com/puppeteer/puppeteer/commit/63d48b2ecba317b6c0a3acad87a7a3671c769dbc)), closes [#6866](https://github.com/puppeteer/puppeteer/issues/6866)
* expose puppeteer.Permission type ([#6856](https://github.com/puppeteer/puppeteer/issues/6856)) ([a5e174f](https://github.com/puppeteer/puppeteer/commit/a5e174f696eb192c541db64a603ea5cdf385a643))
* jsonValue() type is generic ([#6865](https://github.com/puppeteer/puppeteer/issues/6865)) ([bdaba78](https://github.com/puppeteer/puppeteer/commit/bdaba7829da366aabbc81885d84bb2401ab3eaff))
* wider compat TS types and CI checks to ensure correct type defs ([#6855](https://github.com/puppeteer/puppeteer/issues/6855)) ([6a0eb78](https://github.com/puppeteer/puppeteer/commit/6a0eb7841fd82493903b0b9fa153d2de181350eb))
### [7.0.4](https://github.com/puppeteer/puppeteer/compare/v7.0.3...v7.0.4) (2021-02-09)
### Bug Fixes
* make publish bot run full build, not just tsc ([#6848](https://github.com/puppeteer/puppeteer/issues/6848)) ([f718b14](https://github.com/puppeteer/puppeteer/commit/f718b14b64df8be492d344ddd35e40961ff750c5))
### [7.0.3](https://github.com/puppeteer/puppeteer/compare/v7.0.2...v7.0.3) (2021-02-09)
### Bug Fixes
* include lib/types.d.ts in files list ([#6844](https://github.com/puppeteer/puppeteer/issues/6844)) ([e34f317](https://github.com/puppeteer/puppeteer/commit/e34f317b37533256a063c1238609b488d263b998))
### [7.0.2](https://github.com/puppeteer/puppeteer/compare/v7.0.1...v7.0.2) (2021-02-09)
### Bug Fixes
* much better TypeScript definitions ([#6837](https://github.com/puppeteer/puppeteer/issues/6837)) ([f1b46ab](https://github.com/puppeteer/puppeteer/commit/f1b46ab5faa262f893c17923579d0cf52268a764))
* **domworld:** reset bindings when context changes ([#6766](https://github.com/puppeteer/puppeteer/issues/6766)) ([#6836](https://github.com/puppeteer/puppeteer/issues/6836)) ([4e8d074](https://github.com/puppeteer/puppeteer/commit/4e8d074c2f8384a2f283f5edf9ef69c40bd8464f))
* **launcher:** output correct error message for browser ([#6815](https://github.com/puppeteer/puppeteer/issues/6815)) ([6c61874](https://github.com/puppeteer/puppeteer/commit/6c618747979c3a08f2727e9e22fe45cade8c926a))
### [7.0.1](https://github.com/puppeteer/puppeteer/compare/v7.0.0...v7.0.1) (2021-02-04)
### Bug Fixes
* **typescript:** ship .d.ts file in npm package ([#6811](https://github.com/puppeteer/puppeteer/issues/6811)) ([a7e3c2e](https://github.com/puppeteer/puppeteer/commit/a7e3c2e09e9163eee2f15221aafa4400e6a75f91))
## [7.0.0](https://github.com/puppeteer/puppeteer/compare/v6.0.0...v7.0.0) (2021-02-03)
### ⚠ BREAKING CHANGES
* - `page.screenshot` makes a screenshot with the clip dimensions, not cutting it by the ViewPort size.
* **chromium:** - `page.screenshot` cuts screenshot content by the ViewPort size, not ViewPort position.
### Features
* use `captureBeyondViewport` in `Page.captureScreenshot` ([#6805](https://github.com/puppeteer/puppeteer/issues/6805)) ([401d84e](https://github.com/puppeteer/puppeteer/commit/401d84e4a3508f9ca5c24dbfcad2a71571b1b8eb))
* **chromium:** roll Chromium to r848005 ([#6801](https://github.com/puppeteer/puppeteer/issues/6801)) ([890d5c2](https://github.com/puppeteer/puppeteer/commit/890d5c2e57cdee7d73915a878bda86b72e26b608))
## [6.0.0](https://github.com/puppeteer/puppeteer/compare/v5.5.0...v6.0.0) (2021-02-02)
### ⚠ BREAKING CHANGES
* **chromium:** The built-in `aria/` selector query handler doesnt return ignored elements anymore.
### Features
* **chromium:** roll Chromium to r843427 ([#6797](https://github.com/puppeteer/puppeteer/issues/6797)) ([8f9fbdb](https://github.com/puppeteer/puppeteer/commit/8f9fbdbae68254600a9c73ab05f36146c975dba6)), closes [#6758](https://github.com/puppeteer/puppeteer/issues/6758)
* add page.emulateNetworkConditions ([#6759](https://github.com/puppeteer/puppeteer/issues/6759)) ([5ea76e9](https://github.com/puppeteer/puppeteer/commit/5ea76e9333c42ab5a751ca01aa5676a662f6c063))
* **types:** expose typedefs to consumers ([#6745](https://github.com/puppeteer/puppeteer/issues/6745)) ([ebd087a](https://github.com/puppeteer/puppeteer/commit/ebd087a31661a1b701650d0be3e123cc5a813bd8))
* add iPhone 11 models to DeviceDescriptors ([#6467](https://github.com/puppeteer/puppeteer/issues/6467)) ([50b810d](https://github.com/puppeteer/puppeteer/commit/50b810dab7fae5950ba086295462788f91ff1e6f))
* support fetching and launching on Apple M1 ([9a8479a](https://github.com/puppeteer/puppeteer/commit/9a8479a52a7d8b51690b0732b2a10816cd1b8aef)), closes [#6495](https://github.com/puppeteer/puppeteer/issues/6495) [#6634](https://github.com/puppeteer/puppeteer/issues/6634) [#6641](https://github.com/puppeteer/puppeteer/issues/6641) [#6614](https://github.com/puppeteer/puppeteer/issues/6614)
* support promise as return value for page.waitForResponse predicate ([#6624](https://github.com/puppeteer/puppeteer/issues/6624)) ([b57f3fc](https://github.com/puppeteer/puppeteer/commit/b57f3fcd5393c68f51d82e670b004f5b116dcbc3))
### Bug Fixes
* **domworld:** fix waitfor bindings ([#6766](https://github.com/puppeteer/puppeteer/issues/6766)) ([#6775](https://github.com/puppeteer/puppeteer/issues/6775)) ([cac540b](https://github.com/puppeteer/puppeteer/commit/cac540be3ab8799a1d77b0951b16bc22ea1c2adb))
* **launcher:** rename TranslateUI to Translate to match Chrome ([#6692](https://github.com/puppeteer/puppeteer/issues/6692)) ([d901696](https://github.com/puppeteer/puppeteer/commit/d901696e0d8901bcb23cf676a5e5ac562f821a0d))
* do not use old utility world ([#6528](https://github.com/puppeteer/puppeteer/issues/6528)) ([fb85911](https://github.com/puppeteer/puppeteer/commit/fb859115c0e2829bae1d1b32edbf642988e2ef76)), closes [#6527](https://github.com/puppeteer/puppeteer/issues/6527)
* update to https-proxy-agent@^5.0.0 to fix `ERR_INVALID_PROTOCOL` ([#6555](https://github.com/puppeteer/puppeteer/issues/6555)) ([3bf5a55](https://github.com/puppeteer/puppeteer/commit/3bf5a552890ee80cc4326b1e430424b0fdad4363))
## [5.5.0](https://github.com/puppeteer/puppeteer/compare/v5.4.1...v5.5.0) (2020-11-16)

View File

@ -183,7 +183,7 @@ There are additional considerations for dependencies that are environment agonis
- Tests should not depend on external services.
- Tests should work on all three platforms: Mac, Linux and Win. This is especially important for screenshot tests.
Puppeteer tests are located in the test directory ([`test`](https://github.com/puppeteer/puppeteer/blob/main/test/) and are written using Mocha. See [`test/README.md`](https://github.com/puppeteer/puppeteer/blob/main/test/) for more details.
Puppeteer tests are located in [the `test` directory](https://github.com/puppeteer/puppeteer/blob/main/test/) and are written using Mocha. See [`test/README.md`](https://github.com/puppeteer/puppeteer/blob/main/test/README.md) for more details.
Despite being named 'unit', these are integration tests, making sure public API methods and events work as expected.
@ -276,9 +276,8 @@ node utils/bisect.js --good 686378 --bad 706915 script.js
Releasing to npm consists of the following phases:
1. Source Code: mark a release.
1. Run `npm run release` to bump the version number in `package.json` and populate the changelog.
1. Run `npm run doc` to update the docs accordingly.
1. Send a PR titled `'chore(release): mark vXXX.YYY.ZZZ'` ([example](https://github.com/puppeteer/puppeteer/pull/5078)).
1. Run `npm run release`. (This automatically bumps the version number in `package.json`, populates the changelog, updates the docs, and creates a Git commit for the next step.)
1. Send a PR for the commit created in the previous step.
1. Make sure the PR passes **all checks**.
- **WHY**: there are linters in place that help to avoid unnecessary errors, e.g. [like this](https://github.com/puppeteer/puppeteer/pull/2446)
1. Merge the PR.
@ -286,5 +285,5 @@ Releasing to npm consists of the following phases:
- **NOTE**: tag names are prefixed with `'v'`, e.g. for version `1.4.0` the tag is `v1.4.0`.
1. As soon as the Git tag is created by completing the previous step, our CI automatically `npm publish`es the new releases for both the `puppeteer` and `puppeteer-core` packages.
1. Source Code: mark post-release.
1. Bump `package.json` version to the `-post` version, run `npm run doc` to update the “released APIs” section at the top of `docs/api.md` accordingly, and send a PR titled `'chore: bump version to vXXX.YYY.ZZZ-post'` ([example](https://github.com/puppeteer/puppeteer/commit/d02440d1eac98028e29f4e1cf55413062a259156))
1. Bump `package.json` version to the `-post` version, run `npm run doc` to update the “released APIs” section at the top of `docs/api.md` accordingly, and send a PR titled `'chore: bump version to vXXX.YYY.ZZZ-post'` ([example](https://github.com/puppeteer/puppeteer/pull/6808))
- **NOTE**: no other commits should be landed in-between release commit and bump commit.

View File

@ -1,31 +1,35 @@
# Puppeteer
<!-- [START badges] -->
[![Build status](https://img.shields.io/travis/com/puppeteer/puppeteer/main.svg)](https://travis-ci.com/puppeteer/puppeteer) [![npm puppeteer package](https://img.shields.io/npm/v/puppeteer.svg)](https://npmjs.org/package/puppeteer) [![Issue resolution status](https://isitmaintained.com/badge/resolution/puppeteer/puppeteer.svg)](https://github.com/puppeteer/puppeteer/issues)
[![Build status](https://github.com/puppeteer/puppeteer/workflows/run-checks/badge.svg)](https://github.com/puppeteer/puppeteer/actions?query=workflow%3Arun-checks) [![npm puppeteer package](https://img.shields.io/npm/v/puppeteer.svg)](https://npmjs.org/package/puppeteer)
<!-- [END badges] -->
<img src="https://user-images.githubusercontent.com/10379601/29446482-04f7036a-841f-11e7-9872-91d1fc2ea683.png" height="200" align="right">
###### [API](https://github.com/puppeteer/puppeteer/blob/v5.5.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)
###### [API](https://github.com/puppeteer/puppeteer/blob/v8.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.
<!-- [START usecases] -->
###### What can I do?
Most things that you can do manually in the browser can be done using Puppeteer! Here are a few examples to get you started:
* Generate screenshots and PDFs of pages.
* Crawl a SPA (Single-Page Application) and generate pre-rendered content (i.e. "SSR" (Server-Side Rendering)).
* Automate form submission, UI testing, keyboard input, etc.
* Create an up-to-date, automated testing environment. Run your tests directly in the latest version of Chrome using the latest JavaScript and browser features.
* Capture a [timeline trace](https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/reference) of your site to help diagnose performance issues.
* Test Chrome Extensions.
- Generate screenshots and PDFs of pages.
- Crawl a SPA (Single-Page Application) and generate pre-rendered content (i.e. "SSR" (Server-Side Rendering)).
- Automate form submission, UI testing, keyboard input, etc.
- Create an up-to-date, automated testing environment. Run your tests directly in the latest version of Chrome using the latest JavaScript and browser features.
- Capture a [timeline trace](https://developers.google.com/web/tools/chrome-devtools/evaluate-performance/reference) of your site to help diagnose performance issues.
- Test Chrome Extensions.
<!-- [END usecases] -->
Give it a spin: https://try-puppeteer.appspot.com/
<!-- [START getstarted] -->
## Getting Started
### Installation
@ -37,8 +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/v5.5.0/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/v8.0.0/docs/api.md#environment-variables).
### puppeteer-core
@ -63,9 +66,9 @@ 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/v5.5.0/docs/api.md#).
of `Browser`, open pages, and then manipulate them with [Puppeteer's API](https://github.com/puppeteer/puppeteer/blob/v8.0.0/docs/api.md#).
**Example** - navigating to https://example.com and saving a screenshot as *example.png*:
**Example** - navigating to https://example.com and saving a screenshot as _example.png_:
Save file as **example.js**
@ -76,7 +79,7 @@ const puppeteer = require('puppeteer');
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({path: 'example.png'});
await page.screenshot({ path: 'example.png' });
await browser.close();
})();
@ -88,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/v5.5.0/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/v8.0.0/docs/api.md#pagesetviewportviewport).
**Example** - create a PDF.
@ -100,8 +103,10 @@ const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com', {waitUntil: 'networkidle2'});
await page.pdf({path: 'hn.pdf', format: 'A4'});
await page.goto('https://news.ycombinator.com', {
waitUntil: 'networkidle2',
});
await page.pdf({ path: 'hn.pdf', format: 'a4' });
await browser.close();
})();
@ -113,7 +118,7 @@ Execute script on the command line
node hn.js
```
See [`Page.pdf()`](https://github.com/puppeteer/puppeteer/blob/v5.5.0/docs/api.md#pagepdfoptions) for more information about creating pdfs.
See [`Page.pdf()`](https://github.com/puppeteer/puppeteer/blob/v8.0.0/docs/api.md#pagepdfoptions) for more information about creating pdfs.
**Example** - evaluate script in the context of the page
@ -132,7 +137,7 @@ const puppeteer = require('puppeteer');
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
deviceScaleFactor: window.devicePixelRatio,
};
});
@ -148,19 +153,20 @@ Execute script on the command line
node get-dimensions.js
```
See [`Page.evaluate()`](https://github.com/puppeteer/puppeteer/blob/v5.5.0/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/v8.0.0/docs/api.md#pageevaluatepagefunction-args) for more information on `evaluate` and related methods like `evaluateOnNewDocument` and `exposeFunction`.
<!-- [END getstarted] -->
<!-- [START runtimesettings] -->
## Default runtime settings
**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/v5.5.0/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/v8.0.0/docs/api.md#puppeteerlaunchoptions) when launching a browser:
```js
const browser = await puppeteer.launch({headless: false}); // default is true
const browser = await puppeteer.launch({ headless: false }); // default is true
```
**2. Runs a bundled version of Chromium**
@ -170,10 +176,10 @@ is guaranteed to work out of the box. To use Puppeteer with a different version
pass in the executable's path when creating a `Browser` instance:
```js
const browser = await puppeteer.launch({executablePath: '/path/to/Chrome'});
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/v5.5.0/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/v8.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.
@ -185,43 +191,42 @@ Puppeteer creates its own browser user profile which it **cleans up on every run
## Resources
- [API Documentation](https://github.com/puppeteer/puppeteer/blob/v5.5.0/docs/api.md)
- [API Documentation](https://github.com/puppeteer/puppeteer/blob/v8.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)
<!-- [START debugging] -->
## Debugging tips
1. Turn off headless mode - sometimes it's useful to see what the browser is
displaying. Instead of launching in headless mode, launch a full version of
the browser using `headless: false`:
1. Turn off headless mode - sometimes it's useful to see what the browser is
displaying. Instead of launching in headless mode, launch a full version of
the browser using `headless: false`:
```js
const browser = await puppeteer.launch({headless: false});
const browser = await puppeteer.launch({ headless: false });
```
2. Slow it down - the `slowMo` option slows down Puppeteer operations by the
specified amount of milliseconds. It's another way to help see what's going on.
2. Slow it down - the `slowMo` option slows down Puppeteer operations by the
specified amount of milliseconds. It's another way to help see what's going on.
```js
const browser = await puppeteer.launch({
headless: false,
slowMo: 250 // slow down by 250ms
slowMo: 250, // slow down by 250ms
});
```
3. Capture console output - You can listen for the `console` event.
This is also handy when debugging code in `page.evaluate()`:
3. Capture console output - You can listen for the `console` event.
This is also handy when debugging code in `page.evaluate()`:
```js
page.on('console', msg => console.log('PAGE LOG:', msg.text()));
page.on('console', (msg) => console.log('PAGE LOG:', msg.text()));
await page.evaluate(() => console.log(`url is ${location.href}`));
```
4. Use debugger in application code browser
4. Use debugger in application code browser
There are two execution context: node.js that is running test code, and the browser
running application code being tested. This lets you debug code in the
@ -230,26 +235,28 @@ Puppeteer creates its own browser user profile which it **cleans up on every run
- Use `{devtools: true}` when launching Puppeteer:
```js
const browser = await puppeteer.launch({devtools: true});
const browser = await puppeteer.launch({ devtools: true });
```
- Change default test timeout:
jest: `jest.setTimeout(100000);`
jest: `jest.setTimeout(100000);`
jasmine: `jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;`
jasmine: `jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;`
mocha: `this.timeout(100000);` (don't forget to change test to use [function and not '=>'](https://stackoverflow.com/a/23492442))
mocha: `this.timeout(100000);` (don't forget to change test to use [function and not '=>'](https://stackoverflow.com/a/23492442))
- Add an evaluate statement with `debugger` inside / add `debugger` to an existing evaluate statement:
- Add an evaluate statement with `debugger` inside / add `debugger` to an existing evaluate statement:
```js
await page.evaluate(() => {debugger;});
await page.evaluate(() => {
debugger;
});
```
The test will now stop executing in the above evaluate statement, and chromium will stop in debug mode.
The test will now stop executing in the above evaluate statement, and chromium will stop in debug mode.
5. Use debugger in node.js
5. Use debugger in node.js
This will let you debug test code. For example, you can step over `await page.click()` in the node.js script and see the click happen in the application code browser.
@ -270,37 +277,36 @@ Puppeteer creates its own browser user profile which it **cleans up on every run
- In the newly opened test browser, type `F8` to resume test execution
- Now your `debugger` will be hit and you can debug in the test browser
6. Enable verbose logging - internal DevTools protocol traffic
will be logged via the [`debug`](https://github.com/visionmedia/debug) module under the `puppeteer` namespace.
6. Enable verbose logging - internal DevTools protocol traffic
will be logged via the [`debug`](https://github.com/visionmedia/debug) module under the `puppeteer` namespace.
# Basic verbose logging
env DEBUG="puppeteer:*" node script.js
# Basic verbose logging
env DEBUG="puppeteer:*" node script.js
# Protocol traffic can be rather noisy. This example filters out all Network domain messages
env DEBUG="puppeteer:*" env DEBUG_COLORS=true node script.js 2>&1 | grep -v '"Network'
# Protocol traffic can be rather noisy. This example filters out all Network domain messages
env DEBUG="puppeteer:*" env DEBUG_COLORS=true node script.js 2>&1 | grep -v '"Network'
7. Debug your Puppeteer (node) code easily, using [ndb](https://github.com/GoogleChromeLabs/ndb)
7. Debug your Puppeteer (node) code easily, using [ndb](https://github.com/GoogleChromeLabs/ndb)
- `npm install -g ndb` (or even better, use [npx](https://github.com/zkat/npx)!)
- `npm install -g ndb` (or even better, use [npx](https://github.com/zkat/npx)!)
- add a `debugger` to your Puppeteer (node) code
- add a `debugger` to your Puppeteer (node) code
- add `ndb` (or `npx ndb`) before your test command. For example:
- add `ndb` (or `npx ndb`) before your test command. For example:
`ndb jest` or `ndb mocha` (or `npx ndb jest` / `npx ndb mocha`)
`ndb jest` or `ndb mocha` (or `npx ndb jest` / `npx ndb mocha`)
- debug your test inside chromium like a boss!
- debug your test inside chromium like a boss!
<!-- [END debugging] -->
<!-- [START typescript] -->
## Usage with TypeScript
We have recently completed a migration to move the Puppeteer source code from JavaScript to TypeScript and we're currently working on shipping type definitions for TypeScript with Puppeteer's npm package.
We have recently completed a migration to move the Puppeteer source code from JavaScript to TypeScript and as of version 7.0.1 we ship our own built-in type definitions.
Until this work is complete we recommend installing the Puppeteer type definitions from the [DefinitelyTyped](https://definitelytyped.org/) repository:
If you are on a version older than 7, we recommend installing the Puppeteer type definitions from the [DefinitelyTyped](https://definitelytyped.org/) repository:
```bash
npm install --save-dev @types/puppeteer
@ -310,7 +316,6 @@ The types that you'll see appearing in the Puppeteer source code are based off t
<!-- [END typescript] -->
## Contributing to Puppeteer
Check out [contributing guide](https://github.com/puppeteer/puppeteer/blob/main/CONTRIBUTING.md) to get an overview of Puppeteer development.
@ -328,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/v5.5.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.
From Puppeteer v2.1.0 onwards you can specify [`puppeteer.launch({product: 'firefox'})`](https://github.com/puppeteer/puppeteer/blob/v8.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).
@ -344,6 +349,7 @@ The goals of the project are:
- Learn more about the pain points of automated browser testing and help fill those gaps.
We adapt [Chromium principles](https://www.chromium.org/developers/core-principles) to help us drive product decisions:
- **Speed**: Puppeteer has almost zero performance overhead over an automated page.
- **Security**: Puppeteer operates off-process with respect to Chromium, making it safe to automate potentially malicious pages.
- **Stability**: Puppeteer should not be flaky and should not leak memory.
@ -352,6 +358,7 @@ We adapt [Chromium principles](https://www.chromium.org/developers/core-principl
#### Q: Is Puppeteer replacing Selenium/WebDriver?
**No**. Both projects are valuable for very different reasons:
- Selenium/WebDriver focuses on cross-browser automation; its value proposition is a single standard API that works across all major browsers.
- Puppeteer focuses on Chromium; its value proposition is richer functionality and higher reliability.
@ -367,6 +374,7 @@ That said, you **can** use Puppeteer to run tests against Chromium, e.g. using t
We see Puppeteer as an **indivisible entity** with Chromium. Each version of Puppeteer bundles a specific version of Chromium **the only** version it is guaranteed to work with.
This is not an artificial constraint: A lot of work on Puppeteer is actually taking place in the Chromium repository. Heres a typical story:
- A Puppeteer bug is reported: https://github.com/puppeteer/puppeteer/issues/2709
- It turned out this is an issue with the DevTools protocol, so were fixing it in Chromium: https://chromium-review.googlesource.com/c/chromium/src/+/1102154
- Once the upstream fix is landed, we roll updated Chromium into Puppeteer: https://github.com/puppeteer/puppeteer/pull/2769
@ -374,6 +382,7 @@ This is not an artificial constraint: A lot of work on Puppeteer is actually tak
However, oftentimes it is desirable to use Puppeteer with the official Google Chrome rather than Chromium. For this to work, you should install a `puppeteer-core` version that corresponds to the Chrome version.
For example, in order to drive Chrome 71 with puppeteer-core, use `chrome-71` npm tag:
```bash
npm install puppeteer-core@chrome-71
```
@ -382,7 +391,6 @@ npm install puppeteer-core@chrome-71
Look for the `chromium` entry in [revisions.ts](https://github.com/puppeteer/puppeteer/blob/main/src/revisions.ts). To find the corresponding Chromium commit and version number, search for the revision prefixed by an `r` in [OmahaProxy](https://omahaproxy.appspot.com/)'s "Find Releases" section.
#### Q: Which Firefox version does Puppeteer use?
Since Firefox support is experimental, Puppeteer downloads the latest [Firefox Nightly](https://wiki.mozilla.org/Nightly) when the `PUPPETEER_PRODUCT` environment variable is set to `firefox`. That's also why the value of `firefox` in [revisions.ts](https://github.com/puppeteer/puppeteer/blob/main/src/revisions.ts) is `latest` -- Puppeteer isn't tied to a particular Firefox version.
@ -409,6 +417,7 @@ In browsers, input events could be divided into two big groups: trusted vs. untr
- **Untrusted event**: events generated by Web APIs, e.g. `document.createEvent` or `element.click()` methods.
Websites can distinguish between these two groups:
- using an [`Event.isTrusted`](https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted) event flag
- sniffing for accompanying events. For example, every trusted `'click'` event is preceded by `'mousedown'` and `'mouseup'` events.
@ -424,10 +433,11 @@ 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/v5.5.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).
- 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/v8.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?
We have a [troubleshooting](https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md) guide for various operating systems that lists the required dependencies.
#### Q: How do I try/test a prerelease version of Puppeteer?
@ -443,11 +453,11 @@ Please note that prerelease may be unstable and contain bugs.
#### Q: I have more questions! Where do I ask?
There are many ways to get help on Puppeteer:
- [bugtracker](https://github.com/puppeteer/puppeteer/issues)
- [Stack Overflow](https://stackoverflow.com/questions/tagged/puppeteer)
- [slack channel](https://join.slack.com/t/puppeteer/shared_invite/enQtMzU4MjIyMDA5NTM4LWI0YTE0MjM0NWQzYmE2MTRmNjM1ZTBkN2MxNmJmNTIwNTJjMmFhOWFjMGExMDViYjk2YjU2ZmYzMmE1NmExYzc)
Make sure to search these channels before posting your question.
<!-- [END faq] -->

View File

@ -1,7 +1,7 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
"mainEntryPointFilePath": "<projectFolder>/lib/cjs/puppeteer/api-docs-entry.d.ts",
"bundledPackages": [ "devtools-protocol" ],
"bundledPackages": ["devtools-protocol"],
"apiReport": {
"enabled": false
@ -12,7 +12,8 @@
},
"dtsRollup": {
"enabled": false
"enabled": true,
"untrimmedFilePath": "lib/types.d.ts"
},
"tsdocMetadata": {
@ -41,5 +42,4 @@
}
}
}
}

View File

@ -18,7 +18,11 @@ const base = require('./base');
module.exports = {
...base,
require: ['./test/mocha-ts-require', './test/mocha-utils.ts'],
require: [
'./test/mocha-ts-require',
'./test/mocha-utils.ts',
'source-map-support/register',
],
spec: 'test/*.spec.ts',
extension: ['js', 'ts'],
retries: process.env.CI ? 2 : 0,

View File

@ -5,6 +5,6 @@ origin:
description: Headless Chrome Node API
license: Apache-2.0
name: puppeteer
release: a5dedb7
url: https://github.com/mjzffr/puppeteer.git
release: v8.0.0
url: /Users/jdescottes/Development/git/puppeteer
schema: 1

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,9 @@
{
"name": "puppeteer",
"version": "5.5.0",
"version": "8.0.0",
"description": "A high-level API to control headless Chrome over the DevTools Protocol",
"main": "./cjs-entry.js",
"types": "lib/types.d.ts",
"repository": "github:puppeteer/puppeteer",
"engines": {
"node": ">=10.18.1"
@ -17,27 +18,33 @@
"funit": "PUPPETEER_PRODUCT=firefox npm run unit",
"test": "npm run tsc && npm run lint --silent && npm run unit-with-coverage && npm run test-browser",
"prepare": "node typescript-if-required.js",
"prepublishOnly": "npm run tsc",
"prepublishOnly": "npm run build",
"dev-install": "npm run tsc && node install.js",
"install": "node install.js",
"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",
"lint": "npm run eslint && npm run tsc && npm run doc && npm run commitlint",
"lint": "npm run eslint && npm run build && npm run doc && npm run commitlint",
"doc": "node utils/doclint/cli.js",
"clean-lib": "rm -rf lib",
"build": "npm run tsc",
"build": "npm run tsc && npm run generate-d-ts",
"tsc": "npm run clean-lib && tsc --version && npm run tsc-cjs && npm run tsc-esm",
"tsc-cjs": "tsc -b src/tsconfig.cjs.json",
"tsc-esm": "tsc -b src/tsconfig.esm.json",
"apply-next-version": "node utils/apply_next_version.js",
"test-install": "scripts/test-install.sh",
"generate-docs": "npm run tsc && api-extractor run --local --verbose && api-documenter markdown -i temp -o new-docs",
"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",
"release": "standard-version"
"test-types-file": "ts-node -s scripts/test-ts-definition-files.ts",
"release": "node utils/remove_version_suffix.js && standard-version --commit-all"
},
"files": [
"lib/",
"lib/types.d.ts",
"lib/**/*.d.ts",
"lib/**/*.d.ts.map",
"lib/**/*.js",
"lib/**/*.js.map",
"install.js",
"typescript-if-required.js",
"cjs-entry.js",
@ -47,13 +54,13 @@
"license": "Apache-2.0",
"dependencies": {
"debug": "^4.1.0",
"devtools-protocol": "0.0.818844",
"devtools-protocol": "0.0.854822",
"extract-zip": "^2.0.0",
"https-proxy-agent": "^4.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.0.0",
"proxy-from-env": "^1.1.0",
"rimraf": "^3.0.2",
"tar-fs": "^2.0.0",
"unbzip2-stream": "^1.3.3",
@ -62,8 +69,8 @@
"devDependencies": {
"@commitlint/cli": "^11.0.0",
"@commitlint/config-conventional": "^11.0.0",
"@microsoft/api-documenter": "7.9.7",
"@microsoft/api-extractor": "7.10.4",
"@microsoft/api-documenter": "^7.12.7",
"@microsoft/api-extractor": "^7.13.1",
"@types/debug": "0.0.31",
"@types/mime": "^2.0.0",
"@types/mocha": "^7.0.2",
@ -75,7 +82,7 @@
"@types/ws": "^7.2.4",
"@typescript-eslint/eslint-plugin": "^4.4.0",
"@typescript-eslint/parser": "^4.4.0",
"@web/test-runner": "^0.9.2",
"@web/test-runner": "^0.12.15",
"commonmark": "^0.28.1",
"cross-env": "^7.0.2",
"eslint": "^7.10.0",
@ -96,20 +103,15 @@
"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": "3.9.5"
"typescript": "^4.1.5"
},
"husky": {
"hooks": {
"commit-msg": "commitlint --env HUSKY_GIT_PARAMS"
}
},
"standard-version": {
"skip": {
"commit": true,
"tag": true
}
}
}

View File

@ -0,0 +1,208 @@
import { spawnSync } from 'child_process';
import { version } from '../package.json';
import path from 'path';
import fs from 'fs';
const PROJECT_FOLDERS_ROOT = 'test-ts-types';
const EXPECTED_ERRORS = new Map<string, string[]>([
[
'ts-esm-import-esm-output',
[
"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.',
],
],
[
'ts-esm-import-cjs-output',
[
"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.',
],
],
[
'ts-cjs-import-cjs-output',
[
"bad.ts(5,35): error TS2551: Property 'launh' does not exist on type",
"bad.ts(7,29): error TS2551: Property 'devics' does not exist on type",
'bad.ts(11,39): error TS2554: Expected 0 arguments, but got 1.',
],
],
[
'js-esm-import-cjs-output',
[
"bad.js(5,35): error TS2551: Property 'launh' does not exist on type",
"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<Element> | null' is not assignable to type 'ElementHandle<HTMLElement>'",
],
],
[
'js-cjs-import-esm-output',
[
"bad.js(5,35): error TS2551: Property 'launh' does not exist on type",
"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<Element> | null' is not assignable to type 'ElementHandle<HTMLElement>'",
],
],
[
'js-esm-import-esm-output',
[
"bad.js(5,35): error TS2551: Property 'launh' does not exist on type",
"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<Element> | null' is not assignable to type 'ElementHandle<HTMLElement>'",
],
],
[
'js-cjs-import-cjs-output',
[
"bad.js(5,35): error TS2551: Property 'launh' does not exist on type",
"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<Element> | null' is not assignable to type 'ElementHandle<HTMLElement>'",
],
],
]);
const PROJECT_FOLDERS = [...EXPECTED_ERRORS.keys()];
function packPuppeteer() {
console.log('Packing Puppeteer');
const result = spawnSync('npm', ['pack'], {
encoding: 'utf-8',
});
if (result.status !== 0) {
console.log('Failed to pack Puppeteer', result.stderr);
process.exit(1);
}
// Move from puppeteer-X.Y.Z.tgz to puppeteer.tgz so we don't have to update
// it when versions change.
const moveResult = spawnSync('mv', [
`puppeteer-${version}.tgz`,
'puppeteer.tgz',
]);
if (moveResult.status !== 0) {
console.log('Failed to rename Puppeteer tar', moveResult.stderr);
process.exit(1);
}
return `puppeteer.tgz`;
}
const tar = packPuppeteer();
const tarPath = path.join(process.cwd(), tar);
function compileAndCatchErrors(projectLocation) {
const { status, stdout, stderr } = spawnSync('npm', ['run', 'compile'], {
cwd: projectLocation,
encoding: 'utf-8',
});
const tsErrorMesssage = stdout.split('\n');
if (status === 0) {
console.error(
`Running tsc on ${projectLocation} succeeded without triggering the expected errors.`
);
console.log(stdout, stderr);
process.exit(1);
}
return {
tsErrorMesssage,
};
}
function testProject(folder: string) {
console.log('\nTesting:', folder);
const projectLocation = path.join(
process.cwd(),
PROJECT_FOLDERS_ROOT,
folder
);
const tarLocation = path.relative(projectLocation, tarPath);
console.log('===> Clearing left over node_modules to ensure clean slate');
try {
fs.rmdirSync(path.join(projectLocation, 'node_modules'), {
recursive: true,
});
} catch (_error) {
// We don't care if this errors because if it did it's most likely because
// there was no node_modules folder, which is fine.
}
console.log('===> Installing Puppeteer from tar file', tarLocation);
const { status, stderr, stdout } = spawnSync(
'npm',
['install', tarLocation],
{
env: {
...process.env,
PUPPETEER_SKIP_DOWNLOAD: '1',
},
cwd: projectLocation,
encoding: 'utf-8',
}
);
if (status > 0) {
console.error(
'Installing the tar file unexpectedly failed',
stdout,
stderr
);
process.exit(status);
}
console.log('===> Running compile to ensure expected errors only.');
const result = compileAndCatchErrors(projectLocation);
const expectedErrors = EXPECTED_ERRORS.get(folder) || [];
if (
result.tsErrorMesssage.find(
(line) => line.includes('good.ts') || line.includes('good.js')
)
) {
console.error(
`Error for ${projectLocation} contained unexpected failures in good.ts/good.js:\n${result.tsErrorMesssage.join(
'\n'
)}`
);
process.exit(1);
}
const errorsInTsMessage = result.tsErrorMesssage.filter(
(line) => line.includes('bad.ts') || line.includes('bad.js')
);
const expectedErrorsThatHaveOccurred = new Set<string>();
const unexpectedErrors = errorsInTsMessage.filter((message) => {
const isExpected = expectedErrors.some((expectedError) => {
const isExpected = message.startsWith(expectedError);
if (isExpected) {
expectedErrorsThatHaveOccurred.add(expectedError);
}
return isExpected;
});
return !isExpected;
});
if (unexpectedErrors.length) {
console.error(
`${projectLocation} had unexpected TS errors: ${unexpectedErrors.join(
'\n'
)}`
);
process.exit(1);
}
expectedErrors.forEach((expected) => {
if (!expectedErrorsThatHaveOccurred.has(expected)) {
console.error(
`${projectLocation} expected error that was not thrown: ${expected}`
);
process.exit(1);
}
});
console.log('===> ✅ Type-checked correctly.');
}
PROJECT_FOLDERS.forEach((folder) => {
testProject(folder);
});

View File

@ -14,6 +14,19 @@
* limitations under the License.
*/
import {
LaunchOptions,
BrowserLaunchArgumentOptions,
} from './node/LaunchOptions.js';
import { BrowserConnectOptions } from './common/BrowserConnector.js';
import { Product } from './common/Product.js';
import { Browser } from './common/Browser.js';
import { ConnectOptions } from './common/Puppeteer.js';
import { DevicesMap } from './common/DeviceDescriptors.js';
import { PuppeteerErrors } from './common/Errors.js';
import { PredefinedNetworkConditions } from './common/NetworkConditions.js';
import { CustomQueryHandler } from './common/QueryHandler.js';
/*
* This file re-exports any APIs that we want to have documentation generated
* for. It is used by API Extractor to determine what parts of the system to
@ -30,6 +43,7 @@ export * from './common/Accessibility.js';
export * from './common/Browser.js';
export * from './node/BrowserFetcher.js';
export * from './node/Puppeteer.js';
export * from './common/Coverage.js';
export * from './common/Connection.js';
export * from './common/ConsoleMessage.js';
export * from './common/Coverage.js';
@ -41,6 +55,7 @@ export * from './common/ExecutionContext.js';
export * from './common/EventEmitter.js';
export * from './common/FileChooser.js';
export * from './common/FrameManager.js';
export * from './common/PuppeteerViewport.js';
export * from './common/Input.js';
export * from './common/Page.js';
export * from './common/Product.js';
@ -62,4 +77,75 @@ export * from './common/PDFOptions.js';
export * from './common/TimeoutSettings.js';
export * from './common/LifecycleWatcher.js';
export * from './common/QueryHandler.js';
export * from './common/NetworkConditions.js';
export * from 'devtools-protocol/types/protocol';
/*
* We maintain a namespace that emulates the API of the Puppeteer instance you
* get when you `import puppeteer from 'puppeteer'.
*
* We do this as a namespace because export = PuppeteerDefault where
* PuppeteerDefault is a namespace seems to make sure that the types work in
* both ESM and CJS contexts.
*
* This namespace must be kept in sync with the public API offered by the
* PuppeteerNode class.
*/
/**
* @public
* {@inheritDoc PuppeteerNode.launch}
*/
export declare function launch(
options?: LaunchOptions &
BrowserLaunchArgumentOptions &
BrowserConnectOptions & {
product?: Product;
extraPrefsFirefox?: Record<string, unknown>;
}
): Promise<Browser>;
/**
* @public
* {@inheritDoc PuppeteerNode.connect}
*/
export declare function connect(options: ConnectOptions): Promise<Browser>;
/**
* @public
* {@inheritDoc Puppeteer.devices}
*/
export let devices: DevicesMap;
/**
* @public
*/
export let errors: PuppeteerErrors;
/**
* @public
*/
export let networkConditions: PredefinedNetworkConditions;
/**
* @public
* {@inheritDoc Puppeteer.registerCustomQueryHandler}
*/
export declare function registerCustomQueryHandler(
name: string,
queryHandler: CustomQueryHandler
): void;
/**
* @public
* {@inheritDoc Puppeteer.unregisterCustomQueryHandler}
*/
export declare function unregisterCustomQueryHandler(name: string): void;
/**
* @public
* {@inheritDoc Puppeteer.customQueryHandlerNames}
*/
export declare function customQueryHandlerNames(): string[];
/**
* @public
* {@inheritDoc Puppeteer.clearCustomQueryHandlers}
*/
export declare function clearCustomQueryHandlers(): void;

View File

@ -26,6 +26,52 @@ import { Viewport } from './PuppeteerViewport.js';
type BrowserCloseCallback = () => Promise<void> | void;
const WEB_PERMISSION_TO_PROTOCOL_PERMISSION = new Map<
Permission,
Protocol.Browser.PermissionType
>([
['geolocation', 'geolocation'],
['midi', 'midi'],
['notifications', 'notifications'],
// TODO: push isn't a valid type?
// ['push', 'push'],
['camera', 'videoCapture'],
['microphone', 'audioCapture'],
['background-sync', 'backgroundSync'],
['ambient-light-sensor', 'sensors'],
['accelerometer', 'sensors'],
['gyroscope', 'sensors'],
['magnetometer', 'sensors'],
['accessibility-events', 'accessibilityEvents'],
['clipboard-read', 'clipboardReadWrite'],
['clipboard-write', 'clipboardReadWrite'],
['payment-handler', 'paymentHandler'],
['idle-detection', 'idleDetection'],
// chrome-specific permissions we have.
['midi-sysex', 'midiSysex'],
]);
/**
* @public
*/
export type Permission =
| 'geolocation'
| 'midi'
| 'notifications'
| 'camera'
| 'microphone'
| 'background-sync'
| 'ambient-light-sensor'
| 'accelerometer'
| 'gyroscope'
| 'magnetometer'
| 'accessibility-events'
| 'clipboard-read'
| 'clipboard-write'
| 'payment-handler'
| 'idle-detection'
| 'midi-sysex';
/**
* @public
*/
@ -650,34 +696,12 @@ export class BrowserContext extends EventEmitter {
*/
async overridePermissions(
origin: string,
permissions: string[]
permissions: Permission[]
): Promise<void> {
const webPermissionToProtocol = new Map<
string,
Protocol.Browser.PermissionType
>([
['geolocation', 'geolocation'],
['midi', 'midi'],
['notifications', 'notifications'],
// TODO: push isn't a valid type?
// ['push', 'push'],
['camera', 'videoCapture'],
['microphone', 'audioCapture'],
['background-sync', 'backgroundSync'],
['ambient-light-sensor', 'sensors'],
['accelerometer', 'sensors'],
['gyroscope', 'sensors'],
['magnetometer', 'sensors'],
['accessibility-events', 'accessibilityEvents'],
['clipboard-read', 'clipboardReadWrite'],
['clipboard-write', 'clipboardReadWrite'],
['payment-handler', 'paymentHandler'],
['idle-detection', 'idleDetection'],
// chrome-specific permissions we have.
['midi-sysex', 'midiSysex'],
]);
const protocolPermissions = permissions.map((permission) => {
const protocolPermission = webPermissionToProtocol.get(permission);
const protocolPermission = WEB_PERMISSION_TO_PROTOCOL_PERMISSION.get(
permission
);
if (!protocolPermission)
throw new Error('Unknown permission: ' + permission);
return protocolPermission;

View File

@ -24,12 +24,24 @@ import { Viewport } from './PuppeteerViewport.js';
import { isNode } from '../environment.js';
/**
* Generic browser options that can be passed when launching any browser.
* Generic browser options that can be passed when launching any browser or when
* connecting to an existing browser instance.
* @public
*/
export interface BrowserOptions {
export interface BrowserConnectOptions {
/**
* Whether to ignore HTTPS errors during navigation.
* @defaultValue false
*/
ignoreHTTPSErrors?: boolean;
/**
* Sets the viewport for each page.
*/
defaultViewport?: Viewport;
/**
* Slows down Puppeteer operations by the specified amount of milliseconds to
* aid debugging.
*/
slowMo?: number;
}
@ -46,7 +58,7 @@ const getWebSocketTransportClass = async () => {
* @internal
*/
export const connectToBrowser = async (
options: BrowserOptions & {
options: BrowserConnectOptions & {
browserWSEndpoint?: string;
browserURL?: string;
transport?: ConnectionTransport;

View File

@ -115,6 +115,7 @@ export class DOMWorld {
async _setContext(context?: ExecutionContext): Promise<void> {
if (context) {
this._ctxBindings.clear();
this._contextResolveCallback.call(null, context);
this._contextResolveCallback = null;
for (const waitTask of this._waitTasks) waitTask.rerun();
@ -512,9 +513,12 @@ export class DOMWorld {
const bind = async (name: string) => {
const expression = helper.pageBindingInitString('internal', name);
try {
// TODO: In theory, it would be enough to call this just once
await context._client.send('Runtime.addBinding', {
name,
executionContextId: context._contextId,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore The protocol definition is not up to date.
executionContextName: context._contextName,
});
await context.evaluate(expression);
} catch (error) {

View File

@ -548,6 +548,84 @@ const devices: Device[] = [
isLandscape: true,
},
},
{
name: 'iPhone 11',
userAgent:
'Mozilla/5.0 (iPhone; CPU iPhone OS 13_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Mobile/15E148 Safari/604.1',
viewport: {
width: 414,
height: 828,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPhone 11 landscape',
userAgent:
'Mozilla/5.0 (iPhone; CPU iPhone OS 13_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Mobile/15E148 Safari/604.1',
viewport: {
width: 828,
height: 414,
deviceScaleFactor: 2,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPhone 11 Pro',
userAgent:
'Mozilla/5.0 (iPhone; CPU iPhone OS 13_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Mobile/15E148 Safari/604.1',
viewport: {
width: 375,
height: 812,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPhone 11 Pro landscape',
userAgent:
'Mozilla/5.0 (iPhone; CPU iPhone OS 13_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Mobile/15E148 Safari/604.1',
viewport: {
width: 812,
height: 375,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'iPhone 11 Pro Max',
userAgent:
'Mozilla/5.0 (iPhone; CPU iPhone OS 13_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Mobile/15E148 Safari/604.1',
viewport: {
width: 414,
height: 896,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: false,
},
},
{
name: 'iPhone 11 Pro Max landscape',
userAgent:
'Mozilla/5.0 (iPhone; CPU iPhone OS 13_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1 Mobile/15E148 Safari/604.1',
viewport: {
width: 896,
height: 414,
deviceScaleFactor: 3,
isMobile: true,
hasTouch: true,
isLandscape: true,
},
},
{
name: 'JioPhone 2',
userAgent:

View File

@ -19,9 +19,7 @@ import { JSHandle, ElementHandle } from './JSHandle.js';
/**
* @public
*/
export type EvaluateFn<T = unknown> =
| string
| ((arg1: T, ...args: unknown[]) => unknown);
export type EvaluateFn<T = any> = string | ((arg1: T, ...args: any[]) => any);
export type UnwrapPromiseLike<T> = T extends PromiseLike<infer U> ? U : T;
@ -29,15 +27,15 @@ export type UnwrapPromiseLike<T> = T extends PromiseLike<infer U> ? U : T;
* @public
*/
export type EvaluateFnReturnType<T extends EvaluateFn> = T extends (
...args: unknown[]
...args: any[]
) => infer R
? R
: unknown;
: any;
/**
* @public
*/
export type EvaluateHandleFn = string | ((...args: unknown[]) => unknown);
export type EvaluateHandleFn = string | ((...args: any[]) => any);
/**
* @public

View File

@ -56,6 +56,10 @@ export class ExecutionContext {
* @internal
*/
_contextId: number;
/**
* @internal
*/
_contextName: string;
/**
* @internal
@ -68,6 +72,7 @@ export class ExecutionContext {
this._client = client;
this._world = world;
this._contextId = contextPayload.id;
this._contextName = contextPayload.name;
}
/**

View File

@ -322,18 +322,19 @@ export class FrameManager extends EventEmitter {
await this._client.send('Page.addScriptToEvaluateOnNewDocument', {
source: `//# sourceURL=${EVALUATION_SCRIPT_URL}`,
worldName: name,
}),
await Promise.all(
this.frames().map((frame) =>
this._client
.send('Page.createIsolatedWorld', {
frameId: frame._id,
grantUniveralAccess: true,
worldName: name,
})
.catch(debugError)
)
); // frames might be removed before we send this
});
// Frames might be removed before we send this.
await Promise.all(
this.frames().map((frame) =>
this._client
.send('Page.createIsolatedWorld', {
frameId: frame._id,
worldName: name,
grantUniveralAccess: true,
})
.catch(debugError)
)
);
}
_onFrameNavigatedWithinDocument(frameId: string, url: string): void {
@ -369,8 +370,6 @@ export class FrameManager extends EventEmitter {
world = frame._secondaryWorld;
}
}
if (contextPayload.auxData && contextPayload.auxData['type'] === 'isolated')
this._isolatedWorlds.add(contextPayload.name);
const context = new ExecutionContext(this._client, contextPayload, world);
if (world) world._setContext(context);
this._contextIdToContext.set(contextPayload.id, context);

View File

@ -45,6 +45,13 @@ export interface ResponseForRequest {
body: string | Buffer;
}
/**
* Resource types for HTTPRequests as perceived by the rendering engine.
*
* @public
*/
export type ResourceType = Lowercase<Protocol.Network.ResourceType>;
/**
*
* Represents an HTTP request sent by a page.
@ -108,7 +115,7 @@ export class HTTPRequest {
private _allowInterception: boolean;
private _interceptionHandled = false;
private _url: string;
private _resourceType: string;
private _resourceType: ResourceType;
private _method: string;
private _postData?: string;
@ -133,7 +140,7 @@ export class HTTPRequest {
this._interceptionId = interceptionId;
this._allowInterception = allowInterception;
this._url = event.request.url;
this._resourceType = event.type.toLowerCase();
this._resourceType = event.type.toLowerCase() as ResourceType;
this._method = event.request.method;
this._postData = event.request.postData;
this._frame = frame;
@ -153,17 +160,8 @@ export class HTTPRequest {
/**
* Contains the request's resource type as it was perceived by the rendering
* engine.
* @remarks
* @returns one of the following: `document`, `stylesheet`, `image`, `media`,
* `font`, `script`, `texttrack`, `xhr`, `fetch`, `eventsource`, `websocket`,
* `manifest`, `other`.
*/
resourceType(): string {
// TODO (@jackfranklin): protocol.d.ts has a type for this, but all the
// string values are uppercase. The Puppeteer docs explicitly say the
// potential values are all lower case, and the constructor takes the event
// type and calls toLowerCase() on it, so we can't reuse the type from the
// protocol.d.ts. Why do we lower case?
resourceType(): ResourceType {
return this._resourceType;
}

View File

@ -243,7 +243,7 @@ export class JSHandle {
* on the object in page and consequent {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse | JSON.parse} in puppeteer.
* **NOTE** The method throws if the referenced object is not stringifiable.
*/
async jsonValue(): Promise<Record<string, unknown>> {
async jsonValue<T = unknown>(): Promise<T> {
if (this._remoteObject.objectId) {
const response = await this._client.send('Runtime.callFunctionOn', {
functionDeclaration: 'function() { return this; }',
@ -251,9 +251,9 @@ export class JSHandle {
returnByValue: true,
awaitPromise: true,
});
return helper.valueFromRemoteObject(response.result);
return helper.valueFromRemoteObject(response.result) as T;
}
return helper.valueFromRemoteObject(this._remoteObject);
return helper.valueFromRemoteObject(this._remoteObject) as T;
}
/**

View File

@ -0,0 +1,38 @@
/**
* 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 { NetworkConditions } from './NetworkManager.js';
/**
* @public
*/
export type PredefinedNetworkConditions = { [name: string]: NetworkConditions };
/**
* @public
*/
export const networkConditions: PredefinedNetworkConditions = {
'Slow 3G': {
download: ((500 * 1000) / 8) * 0.8,
upload: ((500 * 1000) / 8) * 0.8,
latency: 400 * 5,
},
'Fast 3G': {
download: ((1.6 * 1000 * 1000) / 8) * 0.9,
upload: ((750 * 1000) / 8) * 0.9,
latency: 150 * 3.75,
},
};

View File

@ -30,6 +30,22 @@ export interface Credentials {
password: string;
}
/**
* @public
*/
export interface NetworkConditions {
// Download speed (bytes/s)
download: number;
// Upload speed (bytes/s)
upload: number;
// Latency (ms)
latency: number;
}
export interface InternalNetworkConditions extends NetworkConditions {
offline: boolean;
}
/**
* We use symbols to prevent any external parties listening to these events.
* They are internal to Puppeteer.
@ -56,13 +72,18 @@ export class NetworkManager extends EventEmitter {
Protocol.Network.RequestWillBeSentEvent
>();
_extraHTTPHeaders: Record<string, string> = {};
_offline = false;
_credentials?: Credentials = null;
_attemptedAuthentications = new Set<string>();
_userRequestInterceptionEnabled = false;
_protocolRequestInterceptionEnabled = false;
_userCacheDisabled = false;
_requestIdToInterceptionId = new Map<string, string>();
_emulatedNetworkConditions: InternalNetworkConditions = {
offline: false,
upload: -1,
download: -1,
latency: 0,
};
constructor(
client: CDPSession,
@ -130,14 +151,32 @@ export class NetworkManager extends EventEmitter {
}
async setOfflineMode(value: boolean): Promise<void> {
if (this._offline === value) return;
this._offline = value;
this._emulatedNetworkConditions.offline = value;
await this._updateNetworkConditions();
}
async emulateNetworkConditions(
networkConditions: NetworkConditions | null
): Promise<void> {
this._emulatedNetworkConditions.upload = networkConditions
? networkConditions.upload
: -1;
this._emulatedNetworkConditions.download = networkConditions
? networkConditions.download
: -1;
this._emulatedNetworkConditions.latency = networkConditions
? networkConditions.latency
: 0;
await this._updateNetworkConditions();
}
async _updateNetworkConditions(): Promise<void> {
await this._client.send('Network.emulateNetworkConditions', {
offline: this._offline,
// values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0,
downloadThroughput: -1,
uploadThroughput: -1,
offline: this._emulatedNetworkConditions.offline,
latency: this._emulatedNetworkConditions.latency,
uploadThroughput: this._emulatedNetworkConditions.upload,
downloadThroughput: this._emulatedNetworkConditions.download,
});
}

View File

@ -37,7 +37,11 @@ import { Browser, BrowserContext } from './Browser.js';
import { Target } from './Target.js';
import { createJSHandle, JSHandle, ElementHandle } from './JSHandle.js';
import { Viewport } from './PuppeteerViewport.js';
import { Credentials, NetworkManagerEmittedEvents } from './NetworkManager.js';
import {
Credentials,
NetworkConditions,
NetworkManagerEmittedEvents,
} from './NetworkManager.js';
import { HTTPRequest } from './HTTPRequest.js';
import { HTTPResponse } from './HTTPResponse.js';
import { Accessibility } from './Accessibility.js';
@ -131,21 +135,54 @@ interface MediaFeature {
value: string;
}
interface ScreenshotClip {
/**
* @public
*/
export interface ScreenshotClip {
x: number;
y: number;
width: number;
height: number;
}
interface ScreenshotOptions {
/**
* @public
*/
export interface ScreenshotOptions {
/**
* @defaultValue 'png'
*/
type?: 'png' | 'jpeg';
/**
* The file path to save the image to. The screenshot type will be inferred
* from file extension. If path is a relative path, then it is resolved
* relative to current working directory. If no path is provided, the image
* won't be saved to the disk.
*/
path?: string;
/**
* When true, takes a screenshot of the full page.
* @defaultValue false
*/
fullPage?: boolean;
/**
* An object which specifies the clipping region of the page.
*/
clip?: ScreenshotClip;
/**
* Quality of the image, between 0-100. Not applicable to `png` images.
*/
quality?: number;
/**
* Hides default white background and allows capturing screenshots with transparency.
* @defaultValue false
*/
omitBackground?: boolean;
encoding?: string;
/**
* Encoding of the image.
* @defaultValue 'binary'
*/
encoding?: 'base64' | 'binary';
}
/**
@ -693,6 +730,14 @@ export class Page extends EventEmitter {
return this._frameManager.networkManager().setOfflineMode(enabled);
}
emulateNetworkConditions(
networkConditions: NetworkConditions | null
): Promise<void> {
return this._frameManager
.networkManager()
.emulateNetworkConditions(networkConditions);
}
/**
* @param timeout - Maximum navigation time in milliseconds.
*/
@ -1298,11 +1343,11 @@ export class Page extends EventEmitter {
return helper.waitForEvent(
this._frameManager.networkManager(),
NetworkManagerEmittedEvents.Response,
(response) => {
async (response) => {
if (helper.isString(urlOrPredicate))
return urlOrPredicate === response.url();
if (typeof urlOrPredicate === 'function')
return !!urlOrPredicate(response);
return !!(await urlOrPredicate(response));
return false;
},
timeout,
@ -1375,7 +1420,9 @@ export class Page extends EventEmitter {
features.every((mediaFeature) => {
const name = mediaFeature.name;
assert(
/^prefers-(?:color-scheme|reduced-motion)$/.test(name),
/^(?:prefers-(?:color-scheme|reduced-motion)|color-gamut)$/.test(
name
),
'Unsupported media feature: ' + name
);
return true;
@ -1668,20 +1715,8 @@ export class Page extends EventEmitter {
const width = Math.ceil(metrics.contentSize.width);
const height = Math.ceil(metrics.contentSize.height);
// Overwrite clip for full page at all times.
// Overwrite clip for full page.
clip = { x: 0, y: 0, width, height, scale: 1 };
const { isMobile = false, deviceScaleFactor = 1, isLandscape = false } =
this._viewport || {};
const screenOrientation: Protocol.Emulation.ScreenOrientation = isLandscape
? { angle: 90, type: 'landscapePrimary' }
: { angle: 0, type: 'portraitPrimary' };
await this._client.send('Emulation.setDeviceMetricsOverride', {
mobile: isMobile,
width,
height,
deviceScaleFactor,
screenOrientation,
});
}
const shouldSetDefaultBackground =
options.omitBackground && format === 'png';
@ -1693,6 +1728,7 @@ export class Page extends EventEmitter {
format,
quality: options.quality,
clip,
captureBeyondViewport: true,
});
if (shouldSetDefaultBackground)
await this._client.send('Emulation.setDefaultBackgroundColorOverride');

View File

@ -25,7 +25,11 @@ import {
CustomQueryHandler,
} from './QueryHandler.js';
import { Product } from './Product.js';
import { connectToBrowser, BrowserOptions } from './BrowserConnector.js';
import { connectToBrowser, BrowserConnectOptions } from './BrowserConnector.js';
import {
PredefinedNetworkConditions,
networkConditions,
} from './NetworkConditions.js';
/**
* Settings that are common to the Puppeteer class, regardless of enviroment.
@ -35,7 +39,7 @@ export interface CommonPuppeteerSettings {
isPuppeteerCore: boolean;
}
export interface ConnectOptions extends BrowserOptions {
export interface ConnectOptions extends BrowserConnectOptions {
browserWSEndpoint?: string;
browserURL?: string;
transport?: ConnectionTransport;
@ -125,6 +129,31 @@ export class Puppeteer {
return puppeteerErrors;
}
/**
* @remarks
* Returns a list of network conditions to be used with `page.emulateNetworkConditions(networkConditions)`. Actual list of predefined conditions can be found in {@link https://github.com/puppeteer/puppeteer/blob/main/src/common/NetworkConditions.ts | src/common/NetworkConditions.ts}.
*
* @example
*
* ```js
* const puppeteer = require('puppeteer');
* const slow3G = puppeteer.networkConditions['Slow 3G'];
*
* (async () => {
* const browser = await puppeteer.launch();
* const page = await browser.newPage();
* await page.emulateNetworkConditions(slow3G);
* await page.goto('https://www.google.com');
* // other actions...
* await browser.close();
* })();
* ```
*
*/
get networkConditions(): PredefinedNetworkConditions {
return networkConditions;
}
/**
* Registers a {@link CustomQueryHandler | custom query handler}. After
* registration, the handler can be used everywhere where a selector is

View File

@ -13,11 +13,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
* Sets the viewport of the page.
* @public
*/
export interface Viewport {
/**
* The page width in pixels.
*/
width: number;
/**
* The page height in pixels.
*/
height: number;
/**
* Specify device scale factor.
* See {@link https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio | devicePixelRatio} for more info.
* @defaultValue 1
*/
deviceScaleFactor?: number;
/**
* Whether the `meta viewport` tag is taken into account.
* @defaultValue false
*/
isMobile?: boolean;
/**
* Specifies if the viewport is in landscape mode.
* @defaultValue false
*/
isLandscape?: boolean;
/**
* Specify if the viewport supports touch events.
* @defaultValue false
*/
hasTouch?: boolean;
}

View File

@ -124,7 +124,7 @@ function isNumber(obj: unknown): obj is number {
async function waitForEvent<T extends any>(
emitter: CommonEventEmitter,
eventName: string | symbol,
predicate: (event: T) => boolean,
predicate: (event: T) => Promise<boolean> | boolean,
timeout: number,
abortPromise: Promise<Error>
): Promise<T> {
@ -133,8 +133,8 @@ async function waitForEvent<T extends any>(
resolveCallback = resolve;
rejectCallback = reject;
});
const listener = addEventListener(emitter, eventName, (event) => {
if (!predicate(event)) return;
const listener = addEventListener(emitter, eventName, async (event) => {
if (!(await predicate(event))) return;
resolveCallback(event);
});
if (timeout) {

View File

@ -28,7 +28,10 @@ import { debug } from '../common/Debug.js';
import { promisify } from 'util';
import removeRecursive from 'rimraf';
import * as URL from 'url';
import ProxyAgent from 'https-proxy-agent';
import createHttpsProxyAgent, {
HttpsProxyAgent,
HttpsProxyAgentOptions,
} from 'https-proxy-agent';
import { getProxyForUrl } from 'proxy-from-env';
import { assert } from '../common/assert.js';
@ -108,10 +111,15 @@ function downloadURL(
function handleArm64(): void {
fs.stat('/usr/bin/chromium-browser', function (err, stats) {
if (stats === undefined) {
console.error(`The chromium binary is not available for arm64: `);
console.error(`If you are on Ubuntu, you can install with: `);
console.error(`\n apt-get install chromium-browser\n`);
throw new Error();
fs.stat('/usr/bin/chromium', function (err, stats) {
if (stats === undefined) {
console.error(`The chromium binary is not available for arm64.`);
console.error(`If you are on Ubuntu, you can install with: `);
console.error(`\n sudo apt install chromium\n`);
console.error(`\n sudo apt install chromium-browser\n`);
throw new Error();
}
});
}
});
}
@ -557,7 +565,7 @@ function httpRequest(
type Options = Partial<URL.UrlWithStringQuery> & {
method?: string;
agent?: ProxyAgent;
agent?: HttpsProxyAgent;
rejectUnauthorized?: boolean;
};
@ -581,9 +589,9 @@ function httpRequest(
const proxyOptions = {
...parsedProxyURL,
secureProxy: parsedProxyURL.protocol === 'https:',
} as ProxyAgent.HttpsProxyAgentOptions;
} as HttpsProxyAgentOptions;
options.agent = new ProxyAgent(proxyOptions);
options.agent = createHttpsProxyAgent(proxyOptions);
options.rejectUnauthorized = false;
}
}

View File

@ -13,16 +13,37 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { BrowserConnectOptions } from '../common/BrowserConnector.js';
import { Product } from '../common/Product.js';
/**
* Launcher options that only apply to Chrome.
*
* @public
*/
export interface ChromeArgOptions {
export interface BrowserLaunchArgumentOptions {
/**
* Whether to run the browser in headless mode.
* @defaultValue true
*/
headless?: boolean;
args?: string[];
/**
* Path to a user data directory.
* {@link https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md | see the Chromium docs}
* for more info.
*/
userDataDir?: string;
/**
* Whether to auto-open a DevTools panel for each tab. If this is set to
* `true`, then `headless` will be set to `false` automatically.
* @defaultValue `false`
*/
devtools?: boolean;
/**
* Additional command line arguments to pass to the browser instance.
*/
args?: string[];
}
/**
@ -30,13 +51,72 @@ export interface ChromeArgOptions {
* @public
*/
export interface LaunchOptions {
/**
* Path to a browser executable to use instead of the bundled Chromium. Note
* that Puppeteer is only guaranteed to work with the bundled Chromium, so use
* this setting at your own risk.
*/
executablePath?: string;
/**
* If `true`, do not use `puppeteer.defaultArgs()` when creating a browser. If
* an array is provided, these args will be filtered out. Use this with care -
* you probably want the default arguments Puppeteer uses.
* @defaultValue false
*/
ignoreDefaultArgs?: boolean | string[];
/**
* Close the browser process on `Ctrl+C`.
* @defaultValue `true`
*/
handleSIGINT?: boolean;
/**
* Close the browser process on `SIGTERM`.
* @defaultValue `true`
*/
handleSIGTERM?: boolean;
/**
* Close the browser process on `SIGHUP`.
* @defaultValue `true`
*/
handleSIGHUP?: boolean;
/**
* Maximum time in milliseconds to wait for the browser to start.
* Pass `0` to disable the timeout.
* @defaultValue 30000 (30 seconds).
*/
timeout?: number;
/**
* If true, pipes the browser process stdout and stderr to `process.stdout`
* and `process.stderr`.
* @defaultValue false
*/
dumpio?: boolean;
/**
* Specify environment variables that will be visible to the browser.
* @defaultValue The contents of `process.env`.
*/
env?: Record<string, string | undefined>;
/**
* Connect to a browser over a pipe instead of a WebSocket.
* @defaultValue false
*/
pipe?: boolean;
/**
* Which browser to launch.
* @defaultValue `chrome`
*/
product?: Product;
/**
* {@link https://developer.mozilla.org/en-US/docs/Mozilla/Preferences/Preference_reference | Additional preferences } that can be passed when launching with Firefox.
*/
extraPrefsFirefox?: Record<string, unknown>;
}
/**
* Utility type exposed to enable users to define options that can be passed to
* `puppeteer.launch` without having to list the set of all types.
* @public
*/
export type PuppeteerNodeLaunchOptions = BrowserLaunchArgumentOptions &
LaunchOptions &
BrowserConnectOptions;

View File

@ -25,8 +25,10 @@ import { promisify } from 'util';
const mkdtempAsync = promisify(fs.mkdtemp);
const writeFileAsync = promisify(fs.writeFile);
import { ChromeArgOptions, LaunchOptions } from './LaunchOptions.js';
import { BrowserOptions } from '../common/BrowserConnector.js';
import {
BrowserLaunchArgumentOptions,
PuppeteerNodeLaunchOptions,
} from './LaunchOptions.js';
import { Product } from '../common/Product.js';
/**
@ -34,9 +36,9 @@ import { Product } from '../common/Product.js';
* @public
*/
export interface ProductLauncher {
launch(object);
launch(object: PuppeteerNodeLaunchOptions);
executablePath: () => string;
defaultArgs(object);
defaultArgs(object: BrowserLaunchArgumentOptions);
product: Product;
}
@ -58,9 +60,7 @@ class ChromeLauncher implements ProductLauncher {
this._isPuppeteerCore = isPuppeteerCore;
}
async launch(
options: LaunchOptions & ChromeArgOptions & BrowserOptions = {}
): Promise<Browser> {
async launch(options: PuppeteerNodeLaunchOptions = {}): Promise<Browser> {
const {
ignoreDefaultArgs = false,
args = [],
@ -104,12 +104,14 @@ class ChromeLauncher implements ProductLauncher {
}
let chromeExecutable = executablePath;
if (os.arch() === 'arm64') {
chromeExecutable = '/usr/bin/chromium-browser';
} else if (!executablePath) {
const { missingText, executablePath } = resolveExecutablePath(this);
if (missingText) throw new Error(missingText);
chromeExecutable = executablePath;
if (!executablePath) {
if (os.arch() === 'arm64') {
chromeExecutable = '/usr/bin/chromium-browser';
} else {
const { missingText, executablePath } = resolveExecutablePath(this);
if (missingText) throw new Error(missingText);
chromeExecutable = executablePath;
}
}
const usePipe = chromeArguments.includes('--remote-debugging-pipe');
@ -150,11 +152,7 @@ class ChromeLauncher implements ProductLauncher {
}
}
/**
* @param {!Launcher.ChromeArgOptions=} options
* @returns {!Array<string>}
*/
defaultArgs(options: ChromeArgOptions = {}): string[] {
defaultArgs(options: BrowserLaunchArgumentOptions = {}): string[] {
const chromeArguments = [
'--disable-background-networking',
'--enable-features=NetworkService,NetworkServiceInProcess',
@ -166,7 +164,7 @@ class ChromeLauncher implements ProductLauncher {
'--disable-default-apps',
'--disable-dev-shm-usage',
'--disable-extensions',
'--disable-features=TranslateUI',
'--disable-features=Translate',
'--disable-hang-monitor',
'--disable-ipc-flooding-protection',
'--disable-popup-blocking',
@ -228,13 +226,7 @@ class FirefoxLauncher implements ProductLauncher {
this._isPuppeteerCore = isPuppeteerCore;
}
async launch(
options: LaunchOptions &
ChromeArgOptions &
BrowserOptions & {
extraPrefsFirefox?: { [x: string]: unknown };
} = {}
): Promise<Browser> {
async launch(options: PuppeteerNodeLaunchOptions = {}): Promise<Browser> {
const {
ignoreDefaultArgs = false,
args = [],
@ -344,7 +336,7 @@ class FirefoxLauncher implements ProductLauncher {
return 'firefox';
}
defaultArgs(options: ChromeArgOptions = {}): string[] {
defaultArgs(options: BrowserLaunchArgumentOptions = {}): string[] {
const firefoxArguments = ['--no-remote', '--foreground'];
if (os.platform().startsWith('win')) {
firefoxArguments.push('--wait-for-browser');
@ -611,6 +603,7 @@ function resolveExecutablePath(
product: launcher.product,
path: downloadPath,
});
if (!launcher._isPuppeteerCore && launcher.product === 'chrome') {
const revision = process.env['PUPPETEER_CHROMIUM_REVISION'];
if (revision) {
@ -623,8 +616,13 @@ function resolveExecutablePath(
}
}
const revisionInfo = browserFetcher.revisionInfo(launcher._preferredRevision);
const firefoxHelp = `Run \`PUPPETEER_PRODUCT=firefox npm install\` to download a supported Firefox browser binary.`;
const chromeHelp = `Run \`npm install\` to download the correct Chromium revision (${launcher._preferredRevision}).`;
const missingText = !revisionInfo.local
? `Could not find browser revision ${launcher._preferredRevision}. Run "PUPPETEER_PRODUCT=firefox npm install" or "PUPPETEER_PRODUCT=firefox yarn install" to download a supported Firefox browser binary.`
? `Could not find expected browser (${launcher.product}) locally. ${
launcher.product === 'chrome' ? chromeHelp : firefoxHelp
}`
: null;
return { executablePath: revisionInfo.executablePath, missingText };
}

View File

@ -20,8 +20,11 @@ import {
ConnectOptions,
} from '../common/Puppeteer.js';
import { BrowserFetcher, BrowserFetcherOptions } from './BrowserFetcher.js';
import { LaunchOptions, ChromeArgOptions } from './LaunchOptions.js';
import { BrowserOptions } from '../common/BrowserConnector.js';
import {
LaunchOptions,
BrowserLaunchArgumentOptions,
} from './LaunchOptions.js';
import { BrowserConnectOptions } from '../common/BrowserConnector.js';
import { Browser } from '../common/Browser.js';
import Launcher, { ProductLauncher } from './Launcher.js';
import { PUPPETEER_REVISIONS } from '../revisions.js';
@ -146,8 +149,8 @@ export class PuppeteerNode extends Puppeteer {
*/
launch(
options: LaunchOptions &
ChromeArgOptions &
BrowserOptions & {
BrowserLaunchArgumentOptions &
BrowserConnectOptions & {
product?: Product;
extraPrefsFirefox?: Record<string, unknown>;
} = {}
@ -215,7 +218,7 @@ export class PuppeteerNode extends Puppeteer {
* @param options - Set of configurable options to set on the browser.
* @returns The default flags that Chromium will be launched with.
*/
defaultArgs(options: ChromeArgOptions = {}): string[] {
defaultArgs(options: BrowserLaunchArgumentOptions = {}): string[] {
return this._launcher.defaultArgs(options);
}

View File

@ -20,6 +20,6 @@ type Revisions = Readonly<{
}>;
export const PUPPETEER_REVISIONS: Revisions = {
chromium: '818858',
chromium: '856583',
firefox: 'latest',
};

View File

@ -5,7 +5,5 @@
"outDir": "../lib/esm/puppeteer",
"module": "esnext"
},
"references": [
{ "path": "../vendor/tsconfig.esm.json"}
]
"references": [{ "path": "../vendor/tsconfig.esm.json" }]
}

View File

@ -0,0 +1,18 @@
const puppeteer = require('puppeteer');
async function run() {
// Typo in "launch"
const browser = await puppeteer.launh();
// Typo: "devices"
const devices = puppeteer.devics;
console.log(devices);
const browser2 = await puppeteer.launch();
// 'foo' is invalid argument
const page = await browser2.newPage('foo');
/**
* @type {puppeteer.ElementHandle<HTMLElement>}
*/
const div = await page.$('div');
console.log('got a div!', div);
}
run();

View File

@ -0,0 +1,17 @@
const puppeteer = require('puppeteer');
async function run() {
const browser = await puppeteer.launch();
const devices = puppeteer.devices;
console.log(devices);
const page = await browser.newPage();
const div = await page.$('div');
if (div) {
/**
* @type {puppeteer.ElementHandle<HTMLAnchorElement>}
*/
const newDiv = div;
console.log('got a div!', newDiv);
}
}
run();

View File

@ -0,0 +1,12 @@
{
"name": "test-ts-types-ts-esm",
"version": "1.0.0",
"private": true,
"description": "Test project with TypeScript, ESM output",
"scripts": {
"compile": "../../node_modules/.bin/tsc"
},
"dependencies": {
"puppeteer": "file:../../puppeteer.tgz"
}
}

View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "commonjs",
"checkJs": true,
"allowJs": true,
"strict": true,
"outDir": "dist",
"moduleResolution": "node"
},
"files": ["good.js", "bad.js"]
}

View File

@ -0,0 +1,18 @@
const puppeteer = require('puppeteer');
async function run() {
// Typo in "launch"
const browser = await puppeteer.launh();
// Typo: "devices"
const devices = puppeteer.devics;
console.log(devices);
const browser2 = await puppeteer.launch();
// 'foo' is invalid argument
const page = await browser2.newPage('foo');
/**
* @type {puppeteer.ElementHandle<HTMLElement>}
*/
const div = await page.$('div');
console.log('got a div!', div);
}
run();

View File

@ -0,0 +1,17 @@
const puppeteer = require('puppeteer');
async function run() {
const browser = await puppeteer.launch();
const devices = puppeteer.devices;
console.log(devices);
const page = await browser.newPage();
const div = await page.$('div');
if (div) {
/**
* @type {puppeteer.ElementHandle<HTMLAnchorElement>}
*/
const newDiv = div;
console.log('got a div!', newDiv);
}
}
run();

View File

@ -0,0 +1,15 @@
{
"name": "test-ts-types-ts-esm",
"version": "1.0.0",
"private": true,
"description": "Test project with TypeScript, ESM output",
"scripts": {
"compile": "../../node_modules/.bin/tsc"
},
"devDependencies": {
"typescript": "^4.1.3"
},
"dependencies": {
"puppeteer": "file:../../puppeteer.tgz"
}
}

View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "esnext",
"checkJs": true,
"allowJs": true,
"strict": true,
"outDir": "dist",
"moduleResolution": "node"
},
"files": ["good.js", "bad.js"]
}

View File

@ -0,0 +1,18 @@
import * as puppeteer from 'puppeteer';
async function run() {
// Typo in "launch"
const browser = await puppeteer.launh();
// Typo: "devices"
const devices = puppeteer.devics;
console.log(devices);
const browser2 = await puppeteer.launch();
// 'foo' is invalid argument
const page = await browser2.newPage('foo');
/**
* @type {puppeteer.ElementHandle<HTMLElement>}
*/
const div = await page.$('div');
console.log('got a div!', div);
}
run();

View File

@ -0,0 +1,17 @@
import * as puppeteer from 'puppeteer';
async function run() {
const browser = await puppeteer.launch();
const devices = puppeteer.devices;
console.log(devices);
const page = await browser.newPage();
const div = await page.$('div');
if (div) {
/**
* @type {puppeteer.ElementHandle<HTMLAnchorElement>}
*/
const newDiv = div;
console.log('got a div!', newDiv);
}
}
run();

View File

@ -0,0 +1,15 @@
{
"name": "test-ts-types-ts-esm",
"version": "1.0.0",
"private": true,
"description": "Test project with TypeScript, ESM output",
"scripts": {
"compile": "../../node_modules/.bin/tsc"
},
"devDependencies": {
"typescript": "^4.1.3"
},
"dependencies": {
"puppeteer": "file:../../puppeteer.tgz"
}
}

View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "commonjs",
"checkJs": true,
"allowJs": true,
"strict": true,
"outDir": "dist",
"moduleResolution": "node"
},
"files": ["good.js", "bad.js"]
}

View File

@ -0,0 +1,18 @@
import * as puppeteer from 'puppeteer';
async function run() {
// Typo in "launch"
const browser = await puppeteer.launh();
// Typo: "devices"
const devices = puppeteer.devics;
console.log(devices);
const browser2 = await puppeteer.launch();
// 'foo' is invalid argument
const page = await browser2.newPage('foo');
/**
* @type {puppeteer.ElementHandle<HTMLElement>}
*/
const div = await page.$('div');
console.log('got a div!', div);
}
run();

View File

@ -0,0 +1,17 @@
import * as puppeteer from 'puppeteer';
async function run() {
const browser = await puppeteer.launch();
const devices = puppeteer.devices;
console.log(devices);
const page = await browser.newPage();
const div = await page.$('div');
if (div) {
/**
* @type {puppeteer.ElementHandle<HTMLAnchorElement>}
*/
const newDiv = div;
console.log('got a div!', newDiv);
}
}
run();

View File

@ -0,0 +1,15 @@
{
"name": "test-ts-types-ts-esm",
"version": "1.0.0",
"private": true,
"description": "Test project with TypeScript, ESM output",
"scripts": {
"compile": "../../node_modules/.bin/tsc"
},
"devDependencies": {
"typescript": "^4.1.3"
},
"dependencies": {
"puppeteer": "file:../../puppeteer.tgz"
}
}

View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "esnext",
"checkJs": true,
"allowJs": true,
"strict": true,
"outDir": "dist",
"moduleResolution": "node"
},
"files": ["good.js", "bad.js"]
}

View File

@ -0,0 +1,17 @@
import puppeteer = require('puppeteer');
async function run() {
// Typo in "launch"
const browser = await puppeteer.launh();
// Typo: "devices"
const devices = puppeteer.devics;
console.log(devices);
const browser2 = await puppeteer.launch();
// 'foo' is invalid argument
const page = await browser2.newPage('foo');
const div = (await page.$('div')) as puppeteer.ElementHandle<
HTMLAnchorElement
>;
console.log('got a div!', div);
}
run();

View File

@ -0,0 +1,13 @@
import puppeteer = require('puppeteer');
async function run() {
const browser = await puppeteer.launch();
const devices = puppeteer.devices;
console.log(devices);
const page = await browser.newPage();
const div = (await page.$('div')) as puppeteer.ElementHandle<
HTMLAnchorElement
>;
console.log('got a div!', div);
}
run();

View File

@ -0,0 +1,15 @@
{
"name": "test-ts-types-ts-esm",
"version": "1.0.0",
"private": true,
"description": "Test project with TypeScript, ESM output",
"scripts": {
"compile": "../../node_modules/.bin/tsc"
},
"devDependencies": {
"typescript": "^4.1.3"
},
"dependencies": {
"puppeteer": "file:../../puppeteer.tgz"
}
}

View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"module": "commonjs",
"strict": true,
"outDir": "dist",
"moduleResolution": "node"
},
"files": ["good.ts", "bad.ts"]
}

View File

@ -0,0 +1,18 @@
// eslint-disable-next-line import/extensions
import * as puppeteer from 'puppeteer';
async function run() {
// Typo in "launch"
const browser = await puppeteer.launh();
// Typo: "devices"
const devices = puppeteer.devics;
console.log(devices);
const browser2 = await puppeteer.launch();
// 'foo' is invalid argument
const page = await browser2.newPage('foo');
const div = (await page.$('div')) as puppeteer.ElementHandle<
HTMLAnchorElement
>;
console.log('got a div!', div);
}
run();

View File

@ -0,0 +1,13 @@
// eslint-disable-next-line import/extensions
import * as puppeteer from 'puppeteer';
import type { ElementHandle } from 'puppeteer';
async function run() {
const browser = await puppeteer.launch();
const devices = puppeteer.devices;
console.log(devices);
const page = await browser.newPage();
const div = (await page.$('div')) as ElementHandle<HTMLAnchorElement>;
console.log('got a div!', div);
}
run();

View File

@ -0,0 +1,15 @@
{
"name": "test-ts-types-ts-esm",
"version": "1.0.0",
"private": true,
"description": "Test project with TypeScript, ESM output",
"scripts": {
"compile": "../../node_modules/.bin/tsc"
},
"devDependencies": {
"typescript": "^4.1.3"
},
"dependencies": {
"puppeteer": "file:../../puppeteer.tgz"
}
}

View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"module": "commonjs",
"strict": true,
"outDir": "dist",
"moduleResolution": "node"
},
"files": ["good.ts", "bad.ts"]
}

View File

@ -0,0 +1,18 @@
// eslint-disable-next-line import/extensions
import * as puppeteer from 'puppeteer';
async function run() {
// Typo in "launch"
const browser = await puppeteer.launh();
// Typo: "devices"
const devices = puppeteer.devics;
console.log(devices);
const browser2 = await puppeteer.launch();
// 'foo' is invalid argument
const page = await browser2.newPage('foo');
const div = (await page.$('div')) as puppeteer.ElementHandle<
HTMLAnchorElement
>;
console.log('got a div!', div);
}
run();

View File

@ -0,0 +1,13 @@
// eslint-disable-next-line import/extensions
import * as puppeteer from 'puppeteer';
import type { ElementHandle } from 'puppeteer';
async function run() {
const browser = await puppeteer.launch();
const devices = puppeteer.devices;
console.log(devices);
const page = await browser.newPage();
const div = (await page.$('div')) as ElementHandle<HTMLAnchorElement>;
console.log('got a div!', div);
}
run();

View File

@ -0,0 +1,15 @@
{
"name": "test-ts-types-ts-esm",
"version": "1.0.0",
"private": true,
"description": "Test project with TypeScript, ESM output",
"scripts": {
"compile": "../../node_modules/.bin/tsc"
},
"devDependencies": {
"typescript": "^4.1.3"
},
"dependencies": {
"puppeteer": "file:../../puppeteer.tgz"
}
}

View File

@ -0,0 +1,9 @@
{
"compilerOptions": {
"module": "esnext",
"strict": true,
"outDir": "dist",
"moduleResolution": "node"
},
"files": ["good.ts", "bad.ts"]
}

View File

@ -185,6 +185,32 @@ describeChromeOnly('AriaQueryHandler', () => {
await page.waitForSelector('aria/[role="button"]');
});
it('should persist query handler bindings across reloads', async () => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.evaluate(addElement, 'button');
await page.waitForSelector('aria/[role="button"]');
await page.reload();
await page.evaluate(addElement, 'button');
await page.waitForSelector('aria/[role="button"]');
});
it('should persist query handler bindings across navigations', async () => {
const { page, server } = getTestState();
// Reset page but make sure that execution context ids start with 1.
await page.goto('data:text/html,');
await page.goto(server.EMPTY_PAGE);
await page.evaluate(addElement, 'button');
await page.waitForSelector('aria/[role="button"]');
// Reset page but again make sure that execution context ids start with 1.
await page.goto('data:text/html,');
await page.goto(server.EMPTY_PAGE);
await page.evaluate(addElement, 'button');
await page.waitForSelector('aria/[role="button"]');
});
it('should work independently of `exposeFunction`', async () => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
@ -553,13 +579,13 @@ describeChromeOnly('AriaQueryHandler', () => {
const { page } = getTestState();
const found = await page.$$('aria/[role="heading"]');
const ids = await getIds(found);
expect(ids).toEqual(['shown', 'hidden', 'node11', 'node13']);
expect(ids).toEqual(['shown', 'node11', 'node13']);
});
it('should find both ignored and unignored', async () => {
it('should not find ignored', async () => {
const { page } = getTestState();
const found = await page.$$('aria/title');
const ids = await getIds(found);
expect(ids).toEqual(['shown', 'hidden']);
expect(ids).toEqual(['shown']);
});
});
});

View File

@ -1,3 +1,4 @@
<!DOCTYPE html>
<style>
button {
position: absolute;
@ -29,8 +30,9 @@
<button id=btn9>9</button>
<button id=btn10>10</button>
<script>
window.addEventListener('DOMContentLoaded', () => {
for (const button of Array.from(document.querySelectorAll('button')))
button.addEventListener('click', () => console.log('button #' + button.textContent + ' clicked'), false);
}, false);
for (const button of document.querySelectorAll('button')) {
button.addEventListener('click', () => {
console.log(`button #${button.textContent} clicked`);
});
}
</script>

View File

@ -43,11 +43,14 @@ describe('Cookie specs', () => {
value: 'John Doe',
domain: 'localhost',
path: '/',
sameParty: false,
expires: -1,
size: 16,
httpOnly: false,
secure: false,
session: true,
sourcePort: 8907,
sourceScheme: 'NonSecure',
},
]);
});
@ -99,22 +102,28 @@ describe('Cookie specs', () => {
value: '1234',
domain: 'localhost',
path: '/',
sameParty: false,
expires: -1,
size: 12,
httpOnly: false,
secure: false,
session: true,
sourcePort: 8907,
sourceScheme: 'NonSecure',
},
{
name: 'username',
value: 'John Doe',
domain: 'localhost',
path: '/',
sameParty: false,
expires: -1,
size: 16,
httpOnly: false,
secure: false,
session: true,
sourcePort: 8907,
sourceScheme: 'NonSecure',
},
]);
});
@ -145,22 +154,28 @@ describe('Cookie specs', () => {
value: 'tweets',
domain: 'baz.com',
path: '/',
sameParty: false,
expires: -1,
size: 11,
httpOnly: false,
secure: true,
session: true,
sourcePort: 443,
sourceScheme: 'Secure',
},
{
name: 'doggo',
value: 'woofs',
domain: 'foo.com',
path: '/',
sameParty: false,
expires: -1,
size: 10,
httpOnly: false,
secure: true,
session: true,
sourcePort: 443,
sourceScheme: 'Secure',
},
]);
});
@ -248,11 +263,14 @@ describe('Cookie specs', () => {
value: '123456',
domain: 'localhost',
path: '/',
sameParty: false,
expires: -1,
size: 14,
httpOnly: false,
secure: false,
session: true,
sourcePort: 80,
sourceScheme: 'NonSecure',
},
]);
});
@ -271,11 +289,14 @@ describe('Cookie specs', () => {
value: 'GRID',
domain: 'localhost',
path: '/grid.html',
sameParty: false,
expires: -1,
size: 14,
httpOnly: false,
secure: false,
session: true,
sourcePort: 80,
sourceScheme: 'NonSecure',
},
]);
expect(await page.evaluate('document.cookie')).toBe('gridcookie=GRID');
@ -376,11 +397,14 @@ describe('Cookie specs', () => {
value: 'best',
domain: 'www.example.com',
path: '/',
sameParty: false,
expires: -1,
size: 18,
httpOnly: false,
secure: true,
session: true,
sourcePort: 443,
sourceScheme: 'Secure',
},
]);
});
@ -414,11 +438,14 @@ describe('Cookie specs', () => {
value: 'best',
domain: 'localhost',
path: '/',
sameParty: false,
expires: -1,
size: 20,
httpOnly: false,
secure: false,
session: true,
sourcePort: 80,
sourceScheme: 'NonSecure',
},
]);
@ -428,11 +455,14 @@ describe('Cookie specs', () => {
value: 'worst',
domain: '127.0.0.1',
path: '/',
sameParty: false,
expires: -1,
size: 15,
httpOnly: false,
secure: false,
session: true,
sourcePort: 80,
sourceScheme: 'NonSecure',
},
]);
});
@ -479,12 +509,15 @@ describe('Cookie specs', () => {
value: 'best',
domain: '127.0.0.1',
path: '/',
sameParty: false,
expires: -1,
size: 24,
httpOnly: false,
sameSite: 'None',
secure: true,
session: true,
sourcePort: 443,
sourceScheme: 'Secure',
},
]);
} finally {

View File

@ -37,11 +37,14 @@ describe('DefaultBrowserContext', function () {
value: 'John Doe',
domain: 'localhost',
path: '/',
sameParty: false,
expires: -1,
size: 16,
httpOnly: false,
secure: false,
session: true,
sourcePort: 8907,
sourceScheme: 'NonSecure',
},
]);
});
@ -62,11 +65,14 @@ describe('DefaultBrowserContext', function () {
value: 'John Doe',
domain: 'localhost',
path: '/',
sameParty: false,
expires: -1,
size: 16,
httpOnly: false,
secure: false,
session: true,
sourcePort: 80,
sourceScheme: 'NonSecure',
},
]);
});
@ -93,11 +99,14 @@ describe('DefaultBrowserContext', function () {
value: '1',
domain: 'localhost',
path: '/',
sameParty: false,
expires: -1,
size: 8,
httpOnly: false,
secure: false,
session: true,
sourcePort: 80,
sourceScheme: 'NonSecure',
},
]);
});

View File

@ -240,6 +240,38 @@ describe('Emulation', () => {
() => matchMedia('(prefers-color-scheme: dark)').matches
)
).toBe(false);
await page.emulateMediaFeatures([{ name: 'color-gamut', value: 'srgb' }]);
expect(
await page.evaluate(() => matchMedia('(color-gamut: p3)').matches)
).toBe(false);
expect(
await page.evaluate(() => matchMedia('(color-gamut: srgb)').matches)
).toBe(true);
expect(
await page.evaluate(() => matchMedia('(color-gamut: rec2020)').matches)
).toBe(false);
await page.emulateMediaFeatures([{ name: 'color-gamut', value: 'p3' }]);
expect(
await page.evaluate(() => matchMedia('(color-gamut: p3)').matches)
).toBe(true);
expect(
await page.evaluate(() => matchMedia('(color-gamut: srgb)').matches)
).toBe(true);
expect(
await page.evaluate(() => matchMedia('(color-gamut: rec2020)').matches)
).toBe(false);
await page.emulateMediaFeatures([
{ name: 'color-gamut', value: 'rec2020' },
]);
expect(
await page.evaluate(() => matchMedia('(color-gamut: p3)').matches)
).toBe(true);
expect(
await page.evaluate(() => matchMedia('(color-gamut: srgb)').matches)
).toBe(true);
expect(
await page.evaluate(() => matchMedia('(color-gamut: rec2020)').matches)
).toBe(true);
});
it('should throw in case of bad argument', async () => {
const { page } = getTestState();

View File

@ -276,7 +276,7 @@ describe('Evaluation specs', function () {
const windowHandle = await page.evaluateHandle(() => window);
const errorText = await windowHandle
.jsonValue()
.jsonValue<string>()
.catch((error_) => error_.message);
const error = await page
.evaluate<(errorText: string) => Error>((errorText) => {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 279 B

View File

@ -15,9 +15,7 @@
*/
import expect from 'expect';
import {
getTestState,
} from './mocha-utils'; // eslint-disable-line import/extensions
import { getTestState } from './mocha-utils'; // eslint-disable-line import/extensions
describe('ignoreHTTPSErrors', function () {
/* Note that this test creates its own browser rather than use

View File

@ -113,9 +113,26 @@ describe('JSHandle', function () {
const { page } = getTestState();
const aHandle = await page.evaluateHandle(() => ({ foo: 'bar' }));
const json = await aHandle.jsonValue();
const json = await aHandle.jsonValue<Record<string, string>>();
expect(json).toEqual({ foo: 'bar' });
});
it('works with jsonValues that are not objects', async () => {
const { page } = getTestState();
const aHandle = await page.evaluateHandle(() => ['a', 'b']);
const json = await aHandle.jsonValue<string[]>();
expect(json).toEqual(['a', 'b']);
});
it('works with jsonValues that are primitives', async () => {
const { page } = getTestState();
const aHandle = await page.evaluateHandle(() => 'foo');
const json = await aHandle.jsonValue<string>();
expect(json).toEqual('foo');
});
it('should not work with dates', async () => {
const { page } = getTestState();

View File

@ -21,6 +21,7 @@ import { promisify } from 'util';
import {
getTestState,
itOnlyRegularInstall,
itFailsWindows,
} from './mocha-utils'; // eslint-disable-line import/extensions
import utils from './utils.js';
import expect from 'expect';
@ -472,7 +473,7 @@ describe('Launcher specs', function () {
* combo of that plus it running on CI, but it's hard to track down.
* See comment here: https://github.com/puppeteer/puppeteer/issues/5673#issuecomment-670141377.
*/
itOnlyRegularInstall('should be able to launch Firefox', async function () {
itFailsWindows('should be able to launch Firefox', async function () {
this.timeout(FIREFOX_TIMEOUT);
const { puppeteer } = getTestState();
const browser = await puppeteer.launch({ product: 'firefox' });
@ -587,6 +588,25 @@ describe('Launcher specs', function () {
await browserOne.close();
}
);
// @see https://github.com/puppeteer/puppeteer/issues/6527
it('should be able to reconnect', async () => {
const { puppeteer, server } = getTestState();
const browserOne = await puppeteer.launch();
const browserWSEndpoint = browserOne.wsEndpoint();
const pageOne = await browserOne.newPage();
await pageOne.goto(server.EMPTY_PAGE);
browserOne.disconnect();
const browserTwo = await puppeteer.connect({ browserWSEndpoint });
const pages = await browserTwo.pages();
const pageTwo = pages.find((page) => page.url() === server.EMPTY_PAGE);
await pageTwo.reload();
const bodyHandle = await pageTwo.waitForSelector('body', {
timeout: 10000,
});
await bodyHandle.dispose();
await browserTwo.close();
});
});
describe('Puppeteer.executablePath', function () {
itOnlyRegularInstall('should work', async () => {

View File

@ -611,7 +611,7 @@ describe('navigation', function () {
server.PREFIX + '/frames/one-frame.html'
);
const frame = await utils.waitEvent(page, 'frameattached');
await new Promise((fulfill) => {
await new Promise<void>((fulfill) => {
page.on('framenavigated', (f) => {
if (f === frame) fulfill();
});

View File

@ -172,7 +172,7 @@ describe('Page', function () {
expect(await page.evaluate(() => !!window.opener)).toBe(false);
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
});
it('should work with clicking target=_blank', async () => {
it('should work with clicking target=_blank and without rel=opener', async () => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
@ -182,6 +182,20 @@ describe('Page', function () {
page.click('a'),
]);
expect(await page.evaluate(() => !!window.opener)).toBe(false);
expect(await popup.evaluate(() => !!window.opener)).toBe(false);
});
it('should work with clicking target=_blank and with rel=opener', async () => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
await page.setContent(
'<a target=_blank rel=opener href="/one-style.html">yo</a>'
);
const [popup] = await Promise.all([
new Promise<Page>((x) => page.once('popup', x)),
page.click('a'),
]);
expect(await page.evaluate(() => !!window.opener)).toBe(false);
expect(await popup.evaluate(() => !!window.opener)).toBe(true);
});
it('should work with fake-clicking target=_blank and rel=noopener', async () => {
@ -242,6 +256,7 @@ describe('Page', function () {
await page.goto(server.EMPTY_PAGE);
let error = null;
await context
// @ts-expect-error purposeful bad input for test
.overridePermissions(server.EMPTY_PAGE, ['foo'])
.catch((error_) => (error = error_));
expect(error.message).toBe('Unknown permission: foo');
@ -384,6 +399,28 @@ describe('Page', function () {
});
});
describe('Page.emulateNetworkConditions', function () {
it('should change navigator.connection.effectiveType', async () => {
const { page, puppeteer } = getTestState();
const slow3G = puppeteer.networkConditions['Slow 3G'];
const fast3G = puppeteer.networkConditions['Fast 3G'];
expect(
await page.evaluate('window.navigator.connection.effectiveType')
).toBe('4g');
await page.emulateNetworkConditions(fast3G);
expect(
await page.evaluate('window.navigator.connection.effectiveType')
).toBe('3g');
await page.emulateNetworkConditions(slow3G);
expect(
await page.evaluate('window.navigator.connection.effectiveType')
).toBe('2g');
await page.emulateNetworkConditions(null);
});
});
describe('ExecutionContext.queryObjects', function () {
it('should work', async () => {
const { page } = getTestState();
@ -708,6 +745,22 @@ describe('Page', function () {
.catch((error_) => (error = error_));
expect(error).toBeInstanceOf(puppeteer.errors.TimeoutError);
});
it('should work with async predicate', async () => {
const { page, server } = getTestState();
await page.goto(server.EMPTY_PAGE);
const [response] = await Promise.all([
page.waitForResponse(async (response) => {
console.log(response.url());
return response.url() === server.PREFIX + '/digits/2.png';
}),
page.evaluate(() => {
fetch('/digits/1.png');
fetch('/digits/2.png');
fetch('/digits/3.png');
}),
]);
expect(response.url()).toBe(server.PREFIX + '/digits/2.png');
});
it('should work with no timeout', async () => {
const { page, server } = getTestState();

View File

@ -49,21 +49,23 @@ describe('Screenshots', function () {
});
expect(screenshot).toBeGolden('screenshot-clip-rect.png');
});
it('should clip elements to the viewport', async () => {
const { page, server } = getTestState();
await page.setViewport({ width: 500, height: 500 });
await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({
clip: {
x: 50,
y: 600,
width: 100,
height: 100,
},
});
expect(screenshot).toBeGolden('screenshot-offscreen-clip.png');
});
it(
'should get screenshot bigger than the viewport',
async () => {
const { page, server } = getTestState();
await page.setViewport({ width: 50, height: 50 });
await page.goto(server.PREFIX + '/grid.html');
const screenshot = await page.screenshot({
clip: {
x: 25,
y: 25,
width: 100,
height: 100,
},
});
expect(screenshot).toBeGolden('screenshot-offscreen-clip.png');
}
);
it('should run in parallel', async () => {
const { page, server } = getTestState();

View File

@ -79,7 +79,7 @@ describeFailsFirefox('Workers', function () {
]);
expect(message.text()).toBe('1');
expect(message.location()).toEqual({
url: 'data:text/javascript,console.log(1)',
url: '',
lineNumber: 0,
columnNumber: 8,
});

View File

@ -1,7 +0,0 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./lib/esm",
"module": "ES2015",
},
}

View File

@ -8,6 +8,7 @@
"module": "ESNext",
"declaration": true,
"declarationMap": true,
"resolveJsonModule": true
"resolveJsonModule": true,
"sourceMap": true
}
}

View File

@ -576,6 +576,13 @@ function compareDocumentations(actual, expected) {
expectedName: 'Viewport',
},
],
[
'Method Page.emulateNetworkConditions() networkConditions',
{
actualName: 'Object',
expectedName: 'NetworkConditions',
},
],
[
'Method Page.setViewport() options.viewport',
{
@ -835,6 +842,28 @@ function compareDocumentations(actual, expected) {
expectedName: 'ConnectOptions',
},
],
[
'Method Page.deleteCookie() ...cookies',
{
actualName: '...Object',
expectedName: '...DeleteCookiesRequest',
},
],
[
'Method Page.emulateVisionDeficiency() type',
{
actualName: 'string',
expectedName:
'"none"|"achromatopsia"|"blurredVision"|"deuteranopia"|"protanopia"|"tritanopia"',
},
],
[
'Method BrowserContext.overridePermissions() permissions',
{
actualName: 'Array<string>',
expectedName: 'Array<Permission>',
},
],
]);
const expectedForSource = expectedNamingMismatches.get(source);

View File

@ -26,6 +26,8 @@ const RED_COLOR = '\x1b[31m';
const YELLOW_COLOR = '\x1b[33m';
const RESET_COLOR = '\x1b[0m';
const IS_RELEASE = Boolean(process.env.IS_RELEASE);
run();
async function run() {
@ -35,11 +37,13 @@ async function run() {
const messages = [];
let changedFiles = false;
if (!VERSION.endsWith('-post')) {
if (IS_RELEASE) {
const versions = await Source.readFile(
path.join(PROJECT_DIR, 'versions.js')
);
versions.setText(versions.text().replace(`, 'NEXT'],`, `, '${VERSION}'],`));
versions.setText(
versions.text().replace(`, 'NEXT'],`, `, 'v${VERSION}'],`)
);
await versions.save();
}
@ -125,12 +129,12 @@ async function run() {
}
console.log(`${errors.length} failures, ${warnings.length} warnings.`);
if (!clearExit && !process.env.TRAVIS)
if (!clearExit && !process.env.GITHUB_ACTIONS)
console.log(
'\nIs your lib/ directory up to date? You might need to `npm run tsc`.\n'
);
const runningTime = Date.now() - startTime;
console.log(`DocLint Finished in ${runningTime / 1000} seconds`);
process.exit(clearExit ? 0 : 1);
process.exit(clearExit || IS_RELEASE ? 0 : 1);
}

View File

@ -16,6 +16,8 @@
const Message = require('../Message');
const IS_RELEASE = Boolean(process.env.IS_RELEASE);
module.exports.ensureReleasedAPILinks = function (sources, version) {
// Release version is everything that doesn't include "-".
const apiLinkRegex = /https:\/\/github.com\/puppeteer\/puppeteer\/blob\/v[^/]*\/docs\/api.md/gi;
@ -35,7 +37,7 @@ module.exports.ensureReleasedAPILinks = function (sources, version) {
module.exports.runCommands = function (sources, version) {
// Release version is everything that doesn't include "-".
const isReleaseVersion = !version.includes('-');
const isReleaseVersion = IS_RELEASE || !version.includes('-');
const messages = [];
const commands = [];
@ -70,7 +72,7 @@ module.exports.runCommands = function (sources, version) {
for (const command of commands) {
let newText = null;
if (command.name === 'version')
newText = isReleaseVersion ? 'v' + version : 'Tip-Of-Tree';
newText = isReleaseVersion ? `v${version}` : 'Tip-Of-Tree';
else if (command.name === 'empty-if-release')
newText = isReleaseVersion ? '' : command.originalText;
else if (command.name === 'toc')

View File

@ -0,0 +1,26 @@
/**
* Copyright 2020 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = require('fs');
const json = fs.readFileSync('./package.json', 'utf8').toString();
const pkg = JSON.parse(json);
const oldVersion = pkg.version;
const version = oldVersion.replace(/-post$/, '');
const updated = json.replace(
`"version": "${oldVersion}"`,
`"version": "${version}"`
);
fs.writeFileSync('./package.json', updated);

View File

@ -17,7 +17,9 @@
const versionsPerRelease = new Map([
// This is a mapping from Chromium version => Puppeteer version.
// In Chromium roll patches, use 'NEXT' for the Puppeteer version.
['88.0.4298.0', '5.5.0'],
['90.0.4403.0', 'v7.0.0'],
['89.0.4389.0', 'v6.0.0'],
['88.0.4298.0', 'v5.5.0'],
['87.0.4272.0', 'v5.4.0'],
['86.0.4240.0', 'v5.3.0'],
['85.0.4182.0', 'v5.2.1'],

View File

@ -17,6 +17,7 @@ const { chromeLauncher } = require('@web/test-runner-chrome');
module.exports = {
files: ['test-browser/**/*.spec.js'],
browserStartTimeout: 60 * 1000,
browsers: [
chromeLauncher({
async createPage({ browser }) {