mirror of
https://github.com/puppeteer/puppeteer
synced 2024-06-14 14:02:48 +00:00
docs: restructure documentation guides (#12236)
This commit is contained in:
parent
a2f3415df2
commit
964d99efb9
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
/* eslint-disable import/order */
|
/* eslint-disable import/order */
|
||||||
|
|
||||||
import {copyFile, readFile, writeFile} from 'fs/promises';
|
import {readFile, writeFile} from 'fs/promises';
|
||||||
|
|
||||||
import {docgen, spliceIntoSection} from '@puppeteer/docgen';
|
import {docgen, spliceIntoSection} from '@puppeteer/docgen';
|
||||||
import {execa} from 'execa';
|
import {execa} from 'execa';
|
||||||
@ -17,7 +17,7 @@ export const docsNgSchematicsTask = task({
|
|||||||
name: 'docs:ng-schematics',
|
name: 'docs:ng-schematics',
|
||||||
run: async () => {
|
run: async () => {
|
||||||
const readme = await readFile('packages/ng-schematics/README.md', 'utf-8');
|
const readme = await readFile('packages/ng-schematics/README.md', 'utf-8');
|
||||||
await writeFile('docs/integrations/ng-schematics.md', readme);
|
await writeFile('docs/guides/ng-schematics.md', readme);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -71,9 +71,6 @@ export const docsTask = task({
|
|||||||
name: 'docs',
|
name: 'docs',
|
||||||
dependencies: [docsNgSchematicsTask, docsChromiumSupportTask],
|
dependencies: [docsNgSchematicsTask, docsChromiumSupportTask],
|
||||||
run: async () => {
|
run: async () => {
|
||||||
// Copy main page.
|
|
||||||
await copyFile('README.md', 'docs/index.md');
|
|
||||||
|
|
||||||
// Generate documentation
|
// Generate documentation
|
||||||
for (const [name, folder] of [
|
for (const [name, folder] of [
|
||||||
['browsers', 'browsers-api'],
|
['browsers', 'browsers-api'],
|
||||||
|
223
README.md
223
README.md
@ -5,141 +5,17 @@
|
|||||||
|
|
||||||
<img src="https://user-images.githubusercontent.com/10379601/29446482-04f7036a-841f-11e7-9872-91d1fc2ea683.png" height="200" align="right"/>
|
<img src="https://user-images.githubusercontent.com/10379601/29446482-04f7036a-841f-11e7-9872-91d1fc2ea683.png" height="200" align="right"/>
|
||||||
|
|
||||||
#### [Guides](https://pptr.dev/category/guides) | [API](https://pptr.dev/api) | [FAQ](https://pptr.dev/faq) | [Contributing](https://pptr.dev/contributing) | [Troubleshooting](https://pptr.dev/troubleshooting)
|
#### [Docs](https://pptr.dev/docs) | [API](https://pptr.dev/api) | [FAQ](https://pptr.dev/faq) | [Contributing](https://pptr.dev/contributing) | [Troubleshooting](https://pptr.dev/troubleshooting)
|
||||||
|
|
||||||
> Puppeteer is a Node.js library which provides a high-level API to control
|
Puppeteer is a Node.js library which provides a high-level API to control
|
||||||
> Chrome/Chromium over the
|
Chrome/Chromium over the
|
||||||
> [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/).
|
[DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/).
|
||||||
> Puppeteer runs in
|
Puppeteer runs in
|
||||||
> [headless](https://developer.chrome.com/docs/chromium/new-headless/)
|
[headless](https://developer.chrome.com/docs/chromium/new-headless/)
|
||||||
> mode by default, but can be configured to run in full ("headful")
|
mode by default, but can be configured to run in full ("headful")
|
||||||
> Chrome/Chromium.
|
Chrome/Chromium.
|
||||||
|
|
||||||
#### What can I do?
|
## Example
|
||||||
|
|
||||||
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 automated testing environment using the latest JavaScript and
|
|
||||||
browser features.
|
|
||||||
- Capture a
|
|
||||||
[timeline trace](https://developer.chrome.com/docs/devtools/performance/reference)
|
|
||||||
of your site to help diagnose performance issues.
|
|
||||||
- [Test Chrome Extensions](https://pptr.dev/guides/chrome-extensions).
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
To use Puppeteer in your project, run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i puppeteer
|
|
||||||
# or using yarn
|
|
||||||
yarn add puppeteer
|
|
||||||
# or using pnpm
|
|
||||||
pnpm i puppeteer
|
|
||||||
```
|
|
||||||
|
|
||||||
When you install Puppeteer, it automatically downloads a recent version of
|
|
||||||
[Chrome for Testing](https://developer.chrome.com/blog/chrome-for-testing/) (~170MB macOS, ~282MB Linux, ~280MB Windows) and a `chrome-headless-shell` binary (starting with Puppeteer v21.6.0) that is [guaranteed to
|
|
||||||
work](https://pptr.dev/faq#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy)
|
|
||||||
with Puppeteer. The browser is downloaded to the `$HOME/.cache/puppeteer` folder
|
|
||||||
by default (starting with Puppeteer v19.0.0). See [configuration](https://pptr.dev/api/puppeteer.configuration) for configuration options and environmental variables to control the download behavior.
|
|
||||||
|
|
||||||
If you deploy a project using Puppeteer to a hosting provider, such as Render or
|
|
||||||
Heroku, you might need to reconfigure the location of the cache to be within
|
|
||||||
your project folder (see an example below) because not all hosting providers
|
|
||||||
include `$HOME/.cache` into the project's deployment.
|
|
||||||
|
|
||||||
For a version of Puppeteer without the browser installation, see
|
|
||||||
[`puppeteer-core`](#puppeteer-core).
|
|
||||||
|
|
||||||
If used with TypeScript, the minimum supported TypeScript version is `4.7.4`.
|
|
||||||
|
|
||||||
#### Configuration
|
|
||||||
|
|
||||||
Puppeteer uses several defaults that can be customized through configuration
|
|
||||||
files.
|
|
||||||
|
|
||||||
For example, to change the default cache directory Puppeteer uses to install
|
|
||||||
browsers, you can add a `.puppeteerrc.cjs` (or `puppeteer.config.cjs`) at the
|
|
||||||
root of your application with the contents
|
|
||||||
|
|
||||||
```js
|
|
||||||
const {join} = require('path');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {import("puppeteer").Configuration}
|
|
||||||
*/
|
|
||||||
module.exports = {
|
|
||||||
// Changes the cache location for Puppeteer.
|
|
||||||
cacheDirectory: join(__dirname, '.cache', 'puppeteer'),
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
After adding the configuration file, you will need to remove and reinstall
|
|
||||||
`puppeteer` for it to take effect.
|
|
||||||
|
|
||||||
See the [configuration guide](https://pptr.dev/guides/configuration) for more
|
|
||||||
information.
|
|
||||||
|
|
||||||
#### `puppeteer-core`
|
|
||||||
|
|
||||||
For every release since v1.7.0 we publish two packages:
|
|
||||||
|
|
||||||
- [`puppeteer`](https://www.npmjs.com/package/puppeteer)
|
|
||||||
- [`puppeteer-core`](https://www.npmjs.com/package/puppeteer-core)
|
|
||||||
|
|
||||||
`puppeteer` is a _product_ for browser automation. When installed, it downloads
|
|
||||||
a version of Chrome, which it then drives using `puppeteer-core`. Being an
|
|
||||||
end-user product, `puppeteer` automates several workflows using reasonable
|
|
||||||
defaults [that can be customized](https://pptr.dev/guides/configuration).
|
|
||||||
|
|
||||||
`puppeteer-core` is a _library_ to help drive anything that supports DevTools
|
|
||||||
protocol. Being a library, `puppeteer-core` is fully driven through its
|
|
||||||
programmatic interface implying no defaults are assumed and `puppeteer-core`
|
|
||||||
will not download Chrome when installed.
|
|
||||||
|
|
||||||
You should use `puppeteer-core` if you are
|
|
||||||
[connecting to a remote browser](https://pptr.dev/api/puppeteer.puppeteer.connect)
|
|
||||||
or [managing browsers yourself](https://pptr.dev/browsers-api/).
|
|
||||||
If you are managing browsers yourself, you will need to call
|
|
||||||
[`puppeteer.launch`](https://pptr.dev/api/puppeteer.puppeteernode.launch) with
|
|
||||||
an explicit
|
|
||||||
[`executablePath`](https://pptr.dev/api/puppeteer.launchoptions)
|
|
||||||
(or [`channel`](https://pptr.dev/api/puppeteer.launchoptions) if it's
|
|
||||||
installed in a standard location).
|
|
||||||
|
|
||||||
When using `puppeteer-core`, remember to change the import:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import puppeteer from 'puppeteer-core';
|
|
||||||
```
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
Puppeteer follows the latest
|
|
||||||
[maintenance LTS](https://github.com/nodejs/Release#release-schedule) version of
|
|
||||||
Node.
|
|
||||||
|
|
||||||
Puppeteer will be familiar to people using other browser testing frameworks. You
|
|
||||||
[launch](https://pptr.dev/api/puppeteer.puppeteernode.launch)/[connect](https://pptr.dev/api/puppeteer.puppeteernode.connect)
|
|
||||||
a [browser](https://pptr.dev/api/puppeteer.browser),
|
|
||||||
[create](https://pptr.dev/api/puppeteer.browser.newpage) some
|
|
||||||
[pages](https://pptr.dev/api/puppeteer.page), and then manipulate them with
|
|
||||||
[Puppeteer's API](https://pptr.dev/api).
|
|
||||||
|
|
||||||
For more in-depth usage, check our [guides](https://pptr.dev/category/guides)
|
|
||||||
and [examples](https://github.com/puppeteer/puppeteer/tree/main/examples).
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
The following example searches [developer.chrome.com](https://developer.chrome.com/) for blog posts with text "automate beyond recorder", click on the first result and print the full title of the blog post.
|
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
import puppeteer from 'puppeteer';
|
import puppeteer from 'puppeteer';
|
||||||
@ -176,86 +52,7 @@ import puppeteer from 'puppeteer';
|
|||||||
})();
|
})();
|
||||||
```
|
```
|
||||||
|
|
||||||
### Default runtime settings
|
|
||||||
|
|
||||||
**1. Uses Headless mode**
|
|
||||||
|
|
||||||
By default Puppeteer launches Chrome in
|
|
||||||
[the Headless mode](https://developer.chrome.com/docs/chromium/new-headless/).
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const browser = await puppeteer.launch();
|
|
||||||
// Equivalent to
|
|
||||||
const browser = await puppeteer.launch({headless: true});
|
|
||||||
```
|
|
||||||
|
|
||||||
Before v22, Puppeteer launched the [old Headless mode](https://developer.chrome.com/docs/chromium/new-headless/) by default.
|
|
||||||
The old headless mode is now known as
|
|
||||||
[`chrome-headless-shell`](https://developer.chrome.com/blog/chrome-headless-shell)
|
|
||||||
and ships as a separate binary. `chrome-headless-shell` does not match the
|
|
||||||
behavior of the regular Chrome completely but it is currently more performant
|
|
||||||
for automation tasks where the complete Chrome feature set is not needed. If the performance
|
|
||||||
is more important for your use case, switch to `chrome-headless-shell` as following:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const browser = await puppeteer.launch({headless: 'shell'});
|
|
||||||
```
|
|
||||||
|
|
||||||
To launch a "headful" version of Chrome, set the
|
|
||||||
[`headless`](https://pptr.dev/api/puppeteer.browserlaunchargumentoptions) to `false`
|
|
||||||
option when launching a browser:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const browser = await puppeteer.launch({headless: false});
|
|
||||||
```
|
|
||||||
|
|
||||||
**2. Runs a bundled version of Chrome**
|
|
||||||
|
|
||||||
By default, Puppeteer downloads and uses a specific version of Chrome so its
|
|
||||||
API is guaranteed to work out of the box. To use Puppeteer with a different
|
|
||||||
version of Chrome or Chromium, pass in the executable's path when creating a
|
|
||||||
`Browser` instance:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const browser = await puppeteer.launch({executablePath: '/path/to/Chrome'});
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also use Puppeteer with Firefox. See
|
|
||||||
[status of cross-browser support](https://pptr.dev/faq#q-what-is-the-status-of-cross-browser-support) 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/+/refs/heads/main/docs/chromium_browser_vs_google_chrome.md)
|
|
||||||
describes some differences for Linux users.
|
|
||||||
|
|
||||||
**3. Creates a fresh user profile**
|
|
||||||
|
|
||||||
Puppeteer creates its own browser user profile which it **cleans up on every
|
|
||||||
run**.
|
|
||||||
|
|
||||||
#### Using Docker
|
|
||||||
|
|
||||||
See our [Docker guide](https://pptr.dev/guides/docker).
|
|
||||||
|
|
||||||
#### Using Chrome Extensions
|
|
||||||
|
|
||||||
See our [Chrome extensions guide](https://pptr.dev/guides/chrome-extensions).
|
|
||||||
|
|
||||||
## Resources
|
|
||||||
|
|
||||||
- [API Documentation](https://pptr.dev/api)
|
|
||||||
- [Guides](https://pptr.dev/category/guides)
|
|
||||||
- [Examples](https://github.com/puppeteer/puppeteer/tree/main/examples)
|
|
||||||
- [Community list of Puppeteer resources](https://github.com/transitive-bullshit/awesome-puppeteer)
|
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Check out our [contributing guide](https://pptr.dev/contributing) to get an
|
Check out our [contributing guide](https://pptr.dev/community/contributing) to get an
|
||||||
overview of Puppeteer development.
|
overview of Puppeteer development.
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
Our [FAQ](https://pptr.dev/faq) has migrated to
|
|
||||||
[our site](https://pptr.dev/faq).
|
|
||||||
|
79
docs/guides/browser-management.md
Normal file
79
docs/guides/browser-management.md
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
# Browser management
|
||||||
|
|
||||||
|
Usually, you start working with Puppeteer by either launching [launching](https://pptr.dev/api/puppeteer.puppeteernode.launch) or [connecting](https://pptr.dev/api/puppeteer.puppeteernode.connect) to a browser.
|
||||||
|
|
||||||
|
## Launching a browser
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import puppeteer from 'puppeteer';
|
||||||
|
|
||||||
|
const browser = await puppeteer.launch();
|
||||||
|
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
## Closing a browser
|
||||||
|
|
||||||
|
To gracefully close the browser, you the [`browser.close()`](https://pptr.dev/api/puppeteer.browser.close) method:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import puppeteer from 'puppeteer';
|
||||||
|
|
||||||
|
const browser = await puppeteer.launch();
|
||||||
|
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Browser contexts
|
||||||
|
|
||||||
|
If you need to isolate your automation taks, use [BrowserContexts](https://pptr.dev/api/puppeteer.browser.createbrowsercontext/). Cookies and local storage are not shared between browser contexts. Also, you can close all pages in the context by closing the context.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import puppeteer from 'puppeteer';
|
||||||
|
|
||||||
|
const browser = await puppeteer.launch();
|
||||||
|
|
||||||
|
const context = await browser.createBrowserContext();
|
||||||
|
|
||||||
|
const page1 = await context.newPage();
|
||||||
|
const page2 = await context.newPage();
|
||||||
|
|
||||||
|
await context.close();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Permissions
|
||||||
|
|
||||||
|
You can also configure permissions for a browser context:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import puppeteer from 'puppeteer';
|
||||||
|
|
||||||
|
const browser = await puppeteer.launch();
|
||||||
|
const context = browser.defaultBrowserContext();
|
||||||
|
|
||||||
|
await context.overridePermissions('https://html5demos.com', ['geolocation']);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Connecting to a running browser
|
||||||
|
|
||||||
|
If you launched a browser outside of Puppeteer, you can connect to it using [`connect`](https://pptr.dev/api/puppeteer.puppeteernode.connect/) method. Usually, you can grab a WebSocket endpoint URL from the browser output:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const browser = await puppeteer.connect({
|
||||||
|
browserWSEndpoint: 'ws://127.0.0.1:9222/...',
|
||||||
|
});
|
||||||
|
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
browser.disconnect();
|
||||||
|
```
|
||||||
|
|
||||||
|
:::note
|
||||||
|
|
||||||
|
Unlike `browser.close()`, `browser.disconnect()` does not shut down the browser or close any pages.
|
||||||
|
|
||||||
|
:::
|
@ -1,5 +1,18 @@
|
|||||||
# Configuration
|
# Configuration
|
||||||
|
|
||||||
|
By default, Puppeteer downloads and uses a specific version of Chrome so its
|
||||||
|
API is guaranteed to work out of the box. To use Puppeteer with a different
|
||||||
|
version of Chrome or Chromium, pass in the executable's path when creating a
|
||||||
|
`Browser` instance:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const browser = await puppeteer.launch({executablePath: '/path/to/Chrome'});
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use Puppeteer with Firefox. See
|
||||||
|
[status of cross-browser support](https://pptr.dev/faq#q-what-is-the-status-of-cross-browser-support) for
|
||||||
|
more information.
|
||||||
|
|
||||||
```mdx-code-block
|
```mdx-code-block
|
||||||
import Tabs from '@theme/Tabs';
|
import Tabs from '@theme/Tabs';
|
||||||
import TabItem from '@theme/TabItem';
|
import TabItem from '@theme/TabItem';
|
||||||
|
4
docs/guides/cookies.md
Normal file
4
docs/guides/cookies.md
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Cookies
|
||||||
|
|
||||||
|
Puppeteer allows modifying cookies for a page ahead of time by using [Page.setCookie()](https://pptr.dev/api/puppeteer.page.setcookie/).
|
||||||
|
You can also read the cookies set for a page using [Page.cookies()](https://pptr.dev/api/puppeteer.page.cookies/).
|
9
docs/guides/files.md
Normal file
9
docs/guides/files.md
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Files
|
||||||
|
|
||||||
|
Currently, Puppeteer does not offer a way to handle file downloads in a programmtic way.
|
||||||
|
For uploading files, you need to locate a file input element and call [`ElementHandle.uploadFile`](https://pptr.dev/api/puppeteer.elementhandle.uploadfile/).
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const fileElement = await page.waitForSelector('input[type=file]');
|
||||||
|
await fileElement.uploadFile(['./path-to-local-file']);
|
||||||
|
```
|
48
docs/guides/getting-started.md
Normal file
48
docs/guides/getting-started.md
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Getting started
|
||||||
|
|
||||||
|
Puppeteer will be familiar to people using other browser testing frameworks. You
|
||||||
|
[launch](https://pptr.dev/api/puppeteer.puppeteernode.launch)/[connect](https://pptr.dev/api/puppeteer.puppeteernode.connect)
|
||||||
|
a [browser](https://pptr.dev/api/puppeteer.browser),
|
||||||
|
[create](https://pptr.dev/api/puppeteer.browser.newpage) some
|
||||||
|
[pages](https://pptr.dev/api/puppeteer.page), and then manipulate them with
|
||||||
|
[Puppeteer's API](https://pptr.dev/api).
|
||||||
|
|
||||||
|
The following example searches [developer.chrome.com](https://developer.chrome.com/) for blog posts with text "automate beyond recorder", click on the first result and print the full title of the blog post.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import puppeteer from 'puppeteer';
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
// Launch the browser and open a new blank page
|
||||||
|
const browser = await puppeteer.launch();
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
// Navigate the page to a URL
|
||||||
|
await page.goto('https://developer.chrome.com/');
|
||||||
|
|
||||||
|
// Set screen size
|
||||||
|
await page.setViewport({width: 1080, height: 1024});
|
||||||
|
|
||||||
|
// Type into search box
|
||||||
|
await page.type('.devsite-search-field', 'automate beyond recorder');
|
||||||
|
|
||||||
|
// Wait and click on first result
|
||||||
|
const searchResultSelector = '.devsite-result-item-link';
|
||||||
|
await page.waitForSelector(searchResultSelector);
|
||||||
|
await page.click(searchResultSelector);
|
||||||
|
|
||||||
|
// Locate the full title with a unique string
|
||||||
|
const textSelector = await page.waitForSelector(
|
||||||
|
'text/Customize and automate'
|
||||||
|
);
|
||||||
|
const fullTitle = await textSelector?.evaluate(el => el.textContent);
|
||||||
|
|
||||||
|
// Print the full title
|
||||||
|
console.log('The title of this blog post is "%s".', fullTitle);
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
|
For more in-depth usage, check our [documentation](https://pptr.dev/docs)
|
||||||
|
and [examples](https://github.com/puppeteer/puppeteer/tree/main/examples).
|
30
docs/guides/headless-modes.md
Normal file
30
docs/guides/headless-modes.md
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Headless mode
|
||||||
|
|
||||||
|
By default Puppeteer launches the browser in
|
||||||
|
[the Headless mode](https://developer.chrome.com/docs/chromium/new-headless/).
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const browser = await puppeteer.launch();
|
||||||
|
// Equivalent to
|
||||||
|
const browser = await puppeteer.launch({headless: true});
|
||||||
|
```
|
||||||
|
|
||||||
|
Before v22, Puppeteer launched the [old Headless mode](https://developer.chrome.com/docs/chromium/new-headless/) by default.
|
||||||
|
The old headless mode is now known as
|
||||||
|
[`chrome-headless-shell`](https://developer.chrome.com/blog/chrome-headless-shell)
|
||||||
|
and ships as a separate binary. `chrome-headless-shell` does not match the
|
||||||
|
behavior of the regular Chrome completely but it is currently more performant
|
||||||
|
for automation tasks where the complete Chrome feature set is not needed. If the performance
|
||||||
|
is more important for your use case, switch to `chrome-headless-shell` as following:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const browser = await puppeteer.launch({headless: 'shell'});
|
||||||
|
```
|
||||||
|
|
||||||
|
To launch a "headful" version of Chrome, set the
|
||||||
|
[`headless`](https://pptr.dev/api/puppeteer.browserlaunchargumentoptions) to `false`
|
||||||
|
option when launching a browser:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const browser = await puppeteer.launch({headless: false});
|
||||||
|
```
|
48
docs/guides/installation.md
Normal file
48
docs/guides/installation.md
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# Installation
|
||||||
|
|
||||||
|
To use Puppeteer in your project, run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm i puppeteer
|
||||||
|
# or using yarn
|
||||||
|
yarn add puppeteer
|
||||||
|
# or using pnpm
|
||||||
|
pnpm i puppeteer
|
||||||
|
```
|
||||||
|
|
||||||
|
When you install Puppeteer, it automatically downloads a recent version of
|
||||||
|
[Chrome for Testing](https://developer.chrome.com/blog/chrome-for-testing/) (~170MB macOS, ~282MB Linux, ~280MB Windows) and a `chrome-headless-shell` binary (starting with Puppeteer v21.6.0) that is [guaranteed to
|
||||||
|
work](https://pptr.dev/faq#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy)
|
||||||
|
with Puppeteer. The browser is downloaded to the `$HOME/.cache/puppeteer` folder
|
||||||
|
by default (starting with Puppeteer v19.0.0). See [configuration](https://pptr.dev/api/puppeteer.configuration) for configuration options and environmental variables to control the download behavior.
|
||||||
|
|
||||||
|
For every release since v1.7.0 we publish two packages:
|
||||||
|
|
||||||
|
- [`puppeteer`](https://www.npmjs.com/package/puppeteer)
|
||||||
|
- [`puppeteer-core`](https://www.npmjs.com/package/puppeteer-core)
|
||||||
|
|
||||||
|
`puppeteer` is a _product_ for browser automation. When installed, it downloads
|
||||||
|
a version of Chrome, which it then drives using `puppeteer-core`. Being an
|
||||||
|
end-user product, `puppeteer` automates several workflows using reasonable
|
||||||
|
defaults [that can be customized](https://pptr.dev/guides/configuration).
|
||||||
|
|
||||||
|
`puppeteer-core` is a _library_ to help drive anything that supports DevTools
|
||||||
|
protocol. Being a library, `puppeteer-core` is fully driven through its
|
||||||
|
programmatic interface implying no defaults are assumed and `puppeteer-core`
|
||||||
|
will not download Chrome when installed.
|
||||||
|
|
||||||
|
You should use `puppeteer-core` if you are
|
||||||
|
[connecting to a remote browser](https://pptr.dev/api/puppeteer.puppeteer.connect)
|
||||||
|
or [managing browsers yourself](https://pptr.dev/browsers-api/).
|
||||||
|
If you are managing browsers yourself, you will need to call
|
||||||
|
[`puppeteer.launch`](https://pptr.dev/api/puppeteer.puppeteernode.launch) with
|
||||||
|
an explicit
|
||||||
|
[`executablePath`](https://pptr.dev/api/puppeteer.launchoptions)
|
||||||
|
(or [`channel`](https://pptr.dev/api/puppeteer.launchoptions) if it's
|
||||||
|
installed in a standard location).
|
||||||
|
|
||||||
|
When using `puppeteer-core`, remember to change the import:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import puppeteer from 'puppeteer-core';
|
||||||
|
```
|
@ -1,4 +1,4 @@
|
|||||||
# Evaluate JavaScript
|
# JavaScript execution
|
||||||
|
|
||||||
Puppeteer allows evaluating JavaScript functions in the context of the page
|
Puppeteer allows evaluating JavaScript functions in the context of the page
|
||||||
driven by Puppeteer:
|
driven by Puppeteer:
|
6
docs/guides/links.md
Normal file
6
docs/guides/links.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Links
|
||||||
|
|
||||||
|
- [API Documentation](https://pptr.dev/api)
|
||||||
|
- [Guides](https://pptr.dev/category/guides)
|
||||||
|
- [Examples](https://github.com/puppeteer/puppeteer/tree/main/examples)
|
||||||
|
- [Community list of Puppeteer resources](https://github.com/transitive-bullshit/awesome-puppeteer)
|
@ -1,170 +0,0 @@
|
|||||||
# Locators
|
|
||||||
|
|
||||||
Locators is a new, experimental API that combines the functionalities of
|
|
||||||
waiting and actions. With additional precondition checks, it
|
|
||||||
enables automatic retries for failed actions, resulting in more reliable and
|
|
||||||
less flaky automation scripts.
|
|
||||||
|
|
||||||
:::note
|
|
||||||
|
|
||||||
Locators API is experimental and we will not follow semver for breaking changes
|
|
||||||
in the Locators API.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Use cases
|
|
||||||
|
|
||||||
### Waiting for an element
|
|
||||||
|
|
||||||
```ts
|
|
||||||
await page.locator('button').wait();
|
|
||||||
```
|
|
||||||
|
|
||||||
The following preconditions are automatically checked:
|
|
||||||
|
|
||||||
- Waits for the element to become
|
|
||||||
[visible](https://pptr.dev/api/puppeteer.elementhandle.isvisible/) or hidden.
|
|
||||||
|
|
||||||
### Waiting for a function
|
|
||||||
|
|
||||||
```ts
|
|
||||||
await page
|
|
||||||
.locator(() => {
|
|
||||||
let resolve!: (node: HTMLCanvasElement) => void;
|
|
||||||
const promise = new Promise(res => {
|
|
||||||
return (resolve = res);
|
|
||||||
});
|
|
||||||
const observer = new MutationObserver(records => {
|
|
||||||
for (const record of records) {
|
|
||||||
if (record.target instanceof HTMLCanvasElement) {
|
|
||||||
resolve(record.target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
observer.observe(document);
|
|
||||||
return promise;
|
|
||||||
})
|
|
||||||
.wait();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Clicking an element
|
|
||||||
|
|
||||||
```ts
|
|
||||||
await page.locator('button').click();
|
|
||||||
```
|
|
||||||
|
|
||||||
The following preconditions are automatically checked:
|
|
||||||
|
|
||||||
- Ensures the element is in the viewport.
|
|
||||||
- Waits for the element to become
|
|
||||||
[visible](https://pptr.dev/api/puppeteer.elementhandle.isvisible/) or hidden.
|
|
||||||
- Waits for the element to become enabled.
|
|
||||||
- Waits for the element to have a stable bounding box over two consecutive
|
|
||||||
animation frames.
|
|
||||||
|
|
||||||
### Clicking an element matching a criteria
|
|
||||||
|
|
||||||
```ts
|
|
||||||
await page
|
|
||||||
.locator('button')
|
|
||||||
.filter(button => !button.disabled)
|
|
||||||
.click();
|
|
||||||
```
|
|
||||||
|
|
||||||
The following preconditions are automatically checked:
|
|
||||||
|
|
||||||
- Ensures the element is in the viewport.
|
|
||||||
- Waits for the element to become
|
|
||||||
[visible](https://pptr.dev/api/puppeteer.elementhandle.isvisible/) or hidden.
|
|
||||||
- Waits for the element to become enabled.
|
|
||||||
- Waits for the element to have a stable bounding box over two consecutive
|
|
||||||
animation frames.
|
|
||||||
|
|
||||||
### Filling out an input
|
|
||||||
|
|
||||||
```ts
|
|
||||||
await page.locator('input').fill('value');
|
|
||||||
```
|
|
||||||
|
|
||||||
Automatically detects the input type and choose an appropriate way to fill it out with the provided value.
|
|
||||||
|
|
||||||
The following preconditions are automatically checked:
|
|
||||||
|
|
||||||
- Ensures the element is in the viewport.
|
|
||||||
- Waits for the element to become
|
|
||||||
[visible](https://pptr.dev/api/puppeteer.elementhandle.isvisible/) or hidden.
|
|
||||||
- Waits for the element to become enabled.
|
|
||||||
- Waits for the element to have a stable bounding box over two consecutive
|
|
||||||
animation frames.
|
|
||||||
|
|
||||||
### Retrieving an element property
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const enabled = await page
|
|
||||||
.locator('button')
|
|
||||||
.map(button => !button.disabled)
|
|
||||||
.wait();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Hover over an element
|
|
||||||
|
|
||||||
```ts
|
|
||||||
await page.locator('div').hover();
|
|
||||||
```
|
|
||||||
|
|
||||||
The following preconditions are automatically checked:
|
|
||||||
|
|
||||||
- Ensures the element is in the viewport.
|
|
||||||
- Waits for the element to become
|
|
||||||
[visible](https://pptr.dev/api/puppeteer.elementhandle.isvisible/) or hidden.
|
|
||||||
- Waits for the element to have a stable bounding box over two consecutive
|
|
||||||
animation frames.
|
|
||||||
|
|
||||||
### Scroll an element
|
|
||||||
|
|
||||||
```ts
|
|
||||||
await page.locator('div').scroll({
|
|
||||||
scrollLeft: 10,
|
|
||||||
scrollTop: 20,
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
The following preconditions are automatically checked:
|
|
||||||
|
|
||||||
- Ensures the element is in the viewport.
|
|
||||||
- Waits for the element to become
|
|
||||||
[visible](https://pptr.dev/api/puppeteer.elementhandle.isvisible/) or hidden.
|
|
||||||
- Waits for the element to have a stable bounding box over two consecutive
|
|
||||||
animation frames.
|
|
||||||
|
|
||||||
## Configuring locators
|
|
||||||
|
|
||||||
Locators can be configured to tune configure the preconditions and other other options:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
await page
|
|
||||||
.locator('button')
|
|
||||||
.setEnsureElementIsInTheViewport(false)
|
|
||||||
.setTimeout(0)
|
|
||||||
.setVisibility(null)
|
|
||||||
.setWaitForEnabled(false)
|
|
||||||
.setWaitForStableBoundingBox(false)
|
|
||||||
.click();
|
|
||||||
```
|
|
||||||
|
|
||||||
## Getting locator events
|
|
||||||
|
|
||||||
Currently, locators support a single event that notifies you when the locator is about to perform the action:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
let willClick = false;
|
|
||||||
await page
|
|
||||||
.locator('button')
|
|
||||||
.on(LocatorEvent.Action, () => {
|
|
||||||
willClick = true;
|
|
||||||
})
|
|
||||||
.click();
|
|
||||||
```
|
|
||||||
|
|
||||||
This event can be used for logging/debugging or other purposes. The event might
|
|
||||||
fire multiple times if the locator retries the action.
|
|
14
docs/guides/network-logging.md
Normal file
14
docs/guides/network-logging.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Network logging
|
||||||
|
|
||||||
|
By default, Puppeteer listens for all network requests and responses and emits network events on the page.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const page = await browser.newPage();
|
||||||
|
page.on('request', request => {
|
||||||
|
console.log(request.url());
|
||||||
|
});
|
||||||
|
|
||||||
|
page.on('response', response => {
|
||||||
|
console.log(response.url());
|
||||||
|
});
|
||||||
|
```
|
513
docs/guides/page-interactions.md
Normal file
513
docs/guides/page-interactions.md
Normal file
@ -0,0 +1,513 @@
|
|||||||
|
# Page interactions
|
||||||
|
|
||||||
|
Puppeteer allows you interact with the pages in various ways.
|
||||||
|
|
||||||
|
## Locators
|
||||||
|
|
||||||
|
Locators is a new, experimental API that combines the functionalities of
|
||||||
|
waiting and actions. With additional precondition checks, it
|
||||||
|
enables automatic retries for failed actions, resulting in more reliable and
|
||||||
|
less flaky automation scripts.
|
||||||
|
|
||||||
|
:::note
|
||||||
|
|
||||||
|
Locators API is experimental and we will not follow semver for breaking changes
|
||||||
|
in the Locators API.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
### Use cases
|
||||||
|
|
||||||
|
#### Waiting for an element
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await page.locator('button').wait();
|
||||||
|
```
|
||||||
|
|
||||||
|
The following preconditions are automatically checked:
|
||||||
|
|
||||||
|
- Waits for the element to become
|
||||||
|
[visible](https://pptr.dev/api/puppeteer.elementhandle.isvisible/) or hidden.
|
||||||
|
|
||||||
|
#### Waiting for a function
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await page
|
||||||
|
.locator(() => {
|
||||||
|
let resolve!: (node: HTMLCanvasElement) => void;
|
||||||
|
const promise = new Promise(res => {
|
||||||
|
return (resolve = res);
|
||||||
|
});
|
||||||
|
const observer = new MutationObserver(records => {
|
||||||
|
for (const record of records) {
|
||||||
|
if (record.target instanceof HTMLCanvasElement) {
|
||||||
|
resolve(record.target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
observer.observe(document);
|
||||||
|
return promise;
|
||||||
|
})
|
||||||
|
.wait();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Clicking an element
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await page.locator('button').click();
|
||||||
|
```
|
||||||
|
|
||||||
|
The following preconditions are automatically checked:
|
||||||
|
|
||||||
|
- Ensures the element is in the viewport.
|
||||||
|
- Waits for the element to become
|
||||||
|
[visible](https://pptr.dev/api/puppeteer.elementhandle.isvisible/) or hidden.
|
||||||
|
- Waits for the element to become enabled.
|
||||||
|
- Waits for the element to have a stable bounding box over two consecutive
|
||||||
|
animation frames.
|
||||||
|
|
||||||
|
#### Clicking an element matching a criteria
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await page
|
||||||
|
.locator('button')
|
||||||
|
.filter(button => !button.disabled)
|
||||||
|
.click();
|
||||||
|
```
|
||||||
|
|
||||||
|
The following preconditions are automatically checked:
|
||||||
|
|
||||||
|
- Ensures the element is in the viewport.
|
||||||
|
- Waits for the element to become
|
||||||
|
[visible](https://pptr.dev/api/puppeteer.elementhandle.isvisible/) or hidden.
|
||||||
|
- Waits for the element to become enabled.
|
||||||
|
- Waits for the element to have a stable bounding box over two consecutive
|
||||||
|
animation frames.
|
||||||
|
|
||||||
|
#### Filling out an input
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await page.locator('input').fill('value');
|
||||||
|
```
|
||||||
|
|
||||||
|
Automatically detects the input type and choose an appropriate way to fill it out with the provided value.
|
||||||
|
|
||||||
|
The following preconditions are automatically checked:
|
||||||
|
|
||||||
|
- Ensures the element is in the viewport.
|
||||||
|
- Waits for the element to become
|
||||||
|
[visible](https://pptr.dev/api/puppeteer.elementhandle.isvisible/) or hidden.
|
||||||
|
- Waits for the element to become enabled.
|
||||||
|
- Waits for the element to have a stable bounding box over two consecutive
|
||||||
|
animation frames.
|
||||||
|
|
||||||
|
#### Retrieving an element property
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const enabled = await page
|
||||||
|
.locator('button')
|
||||||
|
.map(button => !button.disabled)
|
||||||
|
.wait();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Hover over an element
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await page.locator('div').hover();
|
||||||
|
```
|
||||||
|
|
||||||
|
The following preconditions are automatically checked:
|
||||||
|
|
||||||
|
- Ensures the element is in the viewport.
|
||||||
|
- Waits for the element to become
|
||||||
|
[visible](https://pptr.dev/api/puppeteer.elementhandle.isvisible/) or hidden.
|
||||||
|
- Waits for the element to have a stable bounding box over two consecutive
|
||||||
|
animation frames.
|
||||||
|
|
||||||
|
#### Scroll an element
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await page.locator('div').scroll({
|
||||||
|
scrollLeft: 10,
|
||||||
|
scrollTop: 20,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
The following preconditions are automatically checked:
|
||||||
|
|
||||||
|
- Ensures the element is in the viewport.
|
||||||
|
- Waits for the element to become
|
||||||
|
[visible](https://pptr.dev/api/puppeteer.elementhandle.isvisible/) or hidden.
|
||||||
|
- Waits for the element to have a stable bounding box over two consecutive
|
||||||
|
animation frames.
|
||||||
|
|
||||||
|
### Configuring locators
|
||||||
|
|
||||||
|
Locators can be configured to tune configure the preconditions and other other options:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
await page
|
||||||
|
.locator('button')
|
||||||
|
.setEnsureElementIsInTheViewport(false)
|
||||||
|
.setTimeout(0)
|
||||||
|
.setVisibility(null)
|
||||||
|
.setWaitForEnabled(false)
|
||||||
|
.setWaitForStableBoundingBox(false)
|
||||||
|
.click();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Getting locator events
|
||||||
|
|
||||||
|
Currently, locators support a single event that notifies you when the locator is about to perform the action:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
let willClick = false;
|
||||||
|
await page
|
||||||
|
.locator('button')
|
||||||
|
.on(LocatorEvent.Action, () => {
|
||||||
|
willClick = true;
|
||||||
|
})
|
||||||
|
.click();
|
||||||
|
```
|
||||||
|
|
||||||
|
This event can be used for logging/debugging or other purposes. The event might
|
||||||
|
fire multiple times if the locator retries the action.
|
||||||
|
|
||||||
|
## Query Selectors
|
||||||
|
|
||||||
|
Queries are the primary mechanism for interacting with the DOM on your site. For example, a typical workflow goes like:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Import puppeteer
|
||||||
|
import puppeteer from 'puppeteer';
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
// Launch the browser
|
||||||
|
const browser = await puppeteer.launch();
|
||||||
|
|
||||||
|
// Create a page
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
// Go to your site
|
||||||
|
await page.goto('YOUR_SITE');
|
||||||
|
|
||||||
|
// Query for an element handle.
|
||||||
|
const element = await page.waitForSelector('div > .class-name');
|
||||||
|
|
||||||
|
// Do something with element...
|
||||||
|
await element.click(); // Just an example.
|
||||||
|
|
||||||
|
// Dispose of handle
|
||||||
|
await element.dispose();
|
||||||
|
|
||||||
|
// Close browser.
|
||||||
|
await browser.close();
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
|
### `P` Selectors
|
||||||
|
|
||||||
|
Puppeteer uses a superset of the CSS selector syntax for querying. We call this syntax _P selectors_ and it's supercharged with extra capabilities such as deep combinators and text selection.
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
|
||||||
|
Although P selectors look like real CSS selectors (we intentionally designed it this way), they should not be used for actually CSS styling. They are designed only for Puppeteer.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
:::note
|
||||||
|
|
||||||
|
P selectors only work on the first "depth" of selectors; for example, `:is(div >>> a)` will not work.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
#### `>>>` and `>>>>` combinators
|
||||||
|
|
||||||
|
The `>>>` and `>>>>` are called _deep descendent_ and _deep_ combinators respectively. Both combinators have the effect of going into shadow hosts with `>>>` going into every shadow host under a node and `>>>>` going into the immediate one (if the node is a shadow host; otherwise, it's a no-op).
|
||||||
|
|
||||||
|
:::note
|
||||||
|
|
||||||
|
A common question is when should `>>>>` be chosen over `>>>` considering the flexibility of `>>>`. A similar question can be asked about `>` and a space; choose `>` if you do not need to query all elements under a given node and a space otherwise. This answer extends to `>>>>` (`>`) and `>>>` (space) naturally.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
Suppose we have the markup
|
||||||
|
|
||||||
|
```html
|
||||||
|
<custom-element>
|
||||||
|
<template shadowrootmode="open">
|
||||||
|
<slot></slot>
|
||||||
|
</template>
|
||||||
|
<custom-element>
|
||||||
|
<template shadowrootmode="open">
|
||||||
|
<slot></slot>
|
||||||
|
</template>
|
||||||
|
<custom-element>
|
||||||
|
<template shadowrootmode="open">
|
||||||
|
<slot></slot>
|
||||||
|
</template>
|
||||||
|
<h2>Light content</h2>
|
||||||
|
</custom-element>
|
||||||
|
</custom-element>
|
||||||
|
</custom-element>
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note: `<template shadowrootmode="open">` is not supported on Firefox.
|
||||||
|
> You can read more about it [here](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template#attributes).
|
||||||
|
|
||||||
|
Then `custom-element >>> h2` will return `h2`, but `custom-element >>>> h2` will return nothing since the inner `h2` is in a deeper shadow root.
|
||||||
|
|
||||||
|
#### `P`-elements
|
||||||
|
|
||||||
|
`P` elements are [pseudo-elements](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements) with a `-p` vendor prefix. It allows you to enhance your selectors with Puppeteer-specific query engines such as XPath, text queries, and ARIA.
|
||||||
|
|
||||||
|
##### Text selectors (`-p-text`)
|
||||||
|
|
||||||
|
Text selectors will select "minimal" elements containing the given text, even within (open) shadow roots. Here, "minimum" means the deepest elements that contain a given text, but not their parents (which technically will also contain the given text).
|
||||||
|
|
||||||
|
###### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const element = await page.waitForSelector('div ::-p-text(My name is Jun)');
|
||||||
|
// You can also use escapes.
|
||||||
|
const element = await page.waitForSelector(
|
||||||
|
':scope >>> ::-p-text(My name is Jun \\(pronounced like "June"\\))'
|
||||||
|
);
|
||||||
|
// or quotes
|
||||||
|
const element = await page.waitForSelector(
|
||||||
|
'div >>>> ::-p-text("My name is Jun (pronounced like \\"June\\")"):hover'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
##### XPath selectors (`-p-xpath`)
|
||||||
|
|
||||||
|
XPath selectors will use the browser's native [`Document.evaluate`](https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate) to query for elements.
|
||||||
|
|
||||||
|
###### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const element = await page.waitForSelector('::-p-xpath(h2)');
|
||||||
|
```
|
||||||
|
|
||||||
|
##### ARIA selectors (`-p-aria`)
|
||||||
|
|
||||||
|
ARIA selectors can be used to find elements with a given ARIA label. These labels are computed using Chrome's internal representation.
|
||||||
|
|
||||||
|
###### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const node = await page.waitForSelector('::-p-aria(Submit)');
|
||||||
|
const node = await page.waitForSelector(
|
||||||
|
'::-p-aria([name="Click me"][role="button"])'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Custom selectors
|
||||||
|
|
||||||
|
Puppeteer provides users the ability to add their own query selectors to Puppeteer using [Puppeteer.registerCustomQueryHandler](../api/puppeteer.registercustomqueryhandler.md). This is useful for creating custom selectors based on framework objects or other vendor-specific objects.
|
||||||
|
|
||||||
|
##### Custom Selectors
|
||||||
|
|
||||||
|
You can register a custom query handler that allows you to create custom selectors. For example, define a query handler for `getById` selectors:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
Puppeteer.registerCustomQueryHandler('getById', {
|
||||||
|
queryOne: (elementOrDocument, selector) => {
|
||||||
|
return elementOrDocument.querySelector(`[id="${CSS.escape(selector)}"]`);
|
||||||
|
},
|
||||||
|
// Note: for demonstation perpose only `id` should be page unique
|
||||||
|
queryAll: (elementOrDocument, selector) => {
|
||||||
|
return elementOrDocument.querySelectorAll(`[id="${CSS.escape(selector)}"]`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
You can now use it as following:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const node = await page.waitForSelector('::-p-getById(elementId)');
|
||||||
|
// OR used in conjunction with other selectors
|
||||||
|
const moreSpecificNode = await page.waitForSelector(
|
||||||
|
'.side-bar ::-p-getById(elementId)'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Custom framework components selector
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
|
||||||
|
Be careful when relying on internal APIs of libraries or frameworks. They can change at any time.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
Find Vue components by name by using Vue internals for querying:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
Puppeteer.registerCustomQueryHandler('vue', {
|
||||||
|
queryOne: (element, name) => {
|
||||||
|
const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT);
|
||||||
|
do {
|
||||||
|
const currentNode = walker.currentNode;
|
||||||
|
if (
|
||||||
|
currentNode.__vnode?.ctx?.type?.name.toLowerCase() ===
|
||||||
|
name.toLocaleLowerCase()
|
||||||
|
) {
|
||||||
|
return currentNode;
|
||||||
|
}
|
||||||
|
} while (walker.nextNode());
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Query the Vue component as following:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const element = await page.$('::-p-vue(MyComponent)');
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Web Components
|
||||||
|
|
||||||
|
Web Components create their own tag so you can query them by the tag name:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const element = await page.$('my-web-component');
|
||||||
|
```
|
||||||
|
|
||||||
|
Extend `HTMLElementTagNameMap` to define types for custom tags. This allows Puppeteer to infer the return type for the ElementHandle:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
'my-web-component': MyWebComponent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Query Selectors (legacy)
|
||||||
|
|
||||||
|
:::caution
|
||||||
|
|
||||||
|
While we maintin prefixed selectors, the recommended way is to use the selector syntax documented above.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
Queries are the primary mechanism for interacting with the DOM on your site. For example, a typical workflow goes like:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Import puppeteer
|
||||||
|
import puppeteer from 'puppeteer';
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
// Launch the browser
|
||||||
|
const browser = await puppeteer.launch();
|
||||||
|
|
||||||
|
// Create a page
|
||||||
|
const page = await browser.newPage();
|
||||||
|
|
||||||
|
// Go to your site
|
||||||
|
await page.goto('YOUR_SITE');
|
||||||
|
|
||||||
|
// Query for an element handle.
|
||||||
|
const element = await page.waitForSelector('div > .class-name');
|
||||||
|
|
||||||
|
// Do something with element...
|
||||||
|
await element.click(); // Just an example.
|
||||||
|
|
||||||
|
// Dispose of handle
|
||||||
|
await element.dispose();
|
||||||
|
|
||||||
|
// Close browser.
|
||||||
|
await browser.close();
|
||||||
|
})();
|
||||||
|
```
|
||||||
|
|
||||||
|
### CSS
|
||||||
|
|
||||||
|
CSS selectors follow the CSS spec of the browser being automated. We provide some basic type deduction for CSS selectors (such as `HTMLInputElement` for `input`), but any selector that contains no type information (such as `.class-name`) will need to be coerced manually using TypeScript's `as` coercion mechanism.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Automatic
|
||||||
|
const element = await page.waitForSelector('div > input');
|
||||||
|
// Manual
|
||||||
|
const element = (await page.waitForSelector(
|
||||||
|
'div > .class-name-for-input'
|
||||||
|
)) as HTMLInputElement;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Built-in selectors
|
||||||
|
|
||||||
|
Built-in selectors are Puppeteer's own class of selectors for doing things CSS cannot. Every built-in selector starts with a prefix `.../` to assist Puppeteer in distinguishing between CSS selectors and a built-in.
|
||||||
|
|
||||||
|
#### Text selectors (`text/`)
|
||||||
|
|
||||||
|
Text selectors will select "minimal" elements containing the given text, even within (open) shadow roots. Here, "minimum" means the deepest elements that contain a given text, but not their parents (which technically will also contain the given text).
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// Note we usually need type coercion since the type cannot be deduced, but for text selectors, `instanceof` checks may be better for runtime validation.
|
||||||
|
const element = await page.waitForSelector('text/My name is Jun');
|
||||||
|
```
|
||||||
|
|
||||||
|
#### XPath selectors (`xpath/`)
|
||||||
|
|
||||||
|
XPath selectors will use the browser's native [`Document.evaluate`](https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate) to query for elements.
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// There is not type deduction for XPaths.
|
||||||
|
const node = await page.waitForSelector('xpath/h2');
|
||||||
|
```
|
||||||
|
|
||||||
|
#### ARIA selectors (`aria/`)
|
||||||
|
|
||||||
|
ARIA selectors can be used to find elements with a given ARIA label. These labels are computed using Chrome's internal representation.
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const node = await page.waitForSelector('aria/Button name');
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Pierce selectors (`pierce/`)
|
||||||
|
|
||||||
|
Pierce selectors will run the `querySelector*` API on the document and all shadow roots to find an element.
|
||||||
|
|
||||||
|
:::danger
|
||||||
|
|
||||||
|
Selectors will **not** _partially_ pierce through shadow roots. See the examples below.
|
||||||
|
|
||||||
|
:::
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
Suppose the HTML is
|
||||||
|
|
||||||
|
```html
|
||||||
|
<div>
|
||||||
|
<custom-element>
|
||||||
|
<div></div>
|
||||||
|
</custom-element>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
Then
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// This will be two elements because of the outer and inner div.
|
||||||
|
expect((await page.$$('pierce/div')).length).toBe(2);
|
||||||
|
|
||||||
|
// Partial piercing doesn't work.
|
||||||
|
expect((await page.$$('pierce/div div')).length).toBe(0);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Custom selectors
|
||||||
|
|
||||||
|
Puppeteer provides users the ability to add their own query selectors to Puppeteer using [Puppeteer.registerCustomQueryHandler](../api/puppeteer.registercustomqueryhandler.md). This is useful for creating custom selectors based on framework objects or other vendor-specific objects.
|
19
docs/guides/pdf-generation.md
Normal file
19
docs/guides/pdf-generation.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# PDF generation
|
||||||
|
|
||||||
|
For printing PDFs use [`Page.pdf()`](https://pptr.dev/api/puppeteer.page.pdf).
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const browser = await puppeteer.launch();
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.goto('https://news.ycombinator.com', {
|
||||||
|
waitUntil: 'networkidle2',
|
||||||
|
});
|
||||||
|
// Saves the PDF to hn.pdf.
|
||||||
|
await page.pdf({
|
||||||
|
path: 'hn.pdf',
|
||||||
|
});
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, the [`Page.pdf()`](https://pptr.dev/api/puppeteer.page.pdf) waits for fonts to be loaded.
|
@ -1,118 +0,0 @@
|
|||||||
# Query Selectors (legacy)
|
|
||||||
|
|
||||||
Queries are the primary mechanism for interacting with the DOM on your site. For example, a typical workflow goes like:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Import puppeteer
|
|
||||||
import puppeteer from 'puppeteer';
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
// Launch the browser
|
|
||||||
const browser = await puppeteer.launch();
|
|
||||||
|
|
||||||
// Create a page
|
|
||||||
const page = await browser.newPage();
|
|
||||||
|
|
||||||
// Go to your site
|
|
||||||
await page.goto('YOUR_SITE');
|
|
||||||
|
|
||||||
// Query for an element handle.
|
|
||||||
const element = await page.waitForSelector('div > .class-name');
|
|
||||||
|
|
||||||
// Do something with element...
|
|
||||||
await element.click(); // Just an example.
|
|
||||||
|
|
||||||
// Dispose of handle
|
|
||||||
await element.dispose();
|
|
||||||
|
|
||||||
// Close browser.
|
|
||||||
await browser.close();
|
|
||||||
})();
|
|
||||||
```
|
|
||||||
|
|
||||||
## CSS
|
|
||||||
|
|
||||||
CSS selectors follow the CSS spec of the browser being automated. We provide some basic type deduction for CSS selectors (such as `HTMLInputElement` for `input`), but any selector that contains no type information (such as `.class-name`) will need to be coerced manually using TypeScript's `as` coercion mechanism.
|
|
||||||
|
|
||||||
### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Automatic
|
|
||||||
const element = await page.waitForSelector('div > input');
|
|
||||||
// Manual
|
|
||||||
const element = (await page.waitForSelector(
|
|
||||||
'div > .class-name-for-input'
|
|
||||||
)) as HTMLInputElement;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Built-in selectors
|
|
||||||
|
|
||||||
Built-in selectors are Puppeteer's own class of selectors for doing things CSS cannot. Every built-in selector starts with a prefix `.../` to assist Puppeteer in distinguishing between CSS selectors and a built-in.
|
|
||||||
|
|
||||||
### Text selectors (`text/`)
|
|
||||||
|
|
||||||
Text selectors will select "minimal" elements containing the given text, even within (open) shadow roots. Here, "minimum" means the deepest elements that contain a given text, but not their parents (which technically will also contain the given text).
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Note we usually need type coercion since the type cannot be deduced, but for text selectors, `instanceof` checks may be better for runtime validation.
|
|
||||||
const element = await page.waitForSelector('text/My name is Jun');
|
|
||||||
```
|
|
||||||
|
|
||||||
### XPath selectors (`xpath/`)
|
|
||||||
|
|
||||||
XPath selectors will use the browser's native [`Document.evaluate`](https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate) to query for elements.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// There is not type deduction for XPaths.
|
|
||||||
const node = await page.waitForSelector('xpath/h2');
|
|
||||||
```
|
|
||||||
|
|
||||||
### ARIA selectors (`aria/`)
|
|
||||||
|
|
||||||
ARIA selectors can be used to find elements with a given ARIA label. These labels are computed using Chrome's internal representation.
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const node = await page.waitForSelector('aria/Button name');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Pierce selectors (`pierce/`)
|
|
||||||
|
|
||||||
Pierce selectors will run the `querySelector*` API on the document and all shadow roots to find an element.
|
|
||||||
|
|
||||||
:::danger
|
|
||||||
|
|
||||||
Selectors will **not** _partially_ pierce through shadow roots. See the examples below.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
Suppose the HTML is
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div>
|
|
||||||
<custom-element>
|
|
||||||
<div></div>
|
|
||||||
</custom-element>
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
Then
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// This will be two elements because of the outer and inner div.
|
|
||||||
expect((await page.$$('pierce/div')).length).toBe(2);
|
|
||||||
|
|
||||||
// Partial piercing doesn't work.
|
|
||||||
expect((await page.$$('pierce/div div')).length).toBe(0);
|
|
||||||
```
|
|
||||||
|
|
||||||
## Custom selectors
|
|
||||||
|
|
||||||
Puppeteer provides users the ability to add their own query selectors to Puppeteer using [Puppeteer.registerCustomQueryHandler](../api/puppeteer.registercustomqueryhandler.md). This is useful for creating custom selectors based on framework objects or other vendor-specific objects.
|
|
@ -1,213 +0,0 @@
|
|||||||
# Query Selectors
|
|
||||||
|
|
||||||
Queries are the primary mechanism for interacting with the DOM on your site. For example, a typical workflow goes like:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// Import puppeteer
|
|
||||||
import puppeteer from 'puppeteer';
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
// Launch the browser
|
|
||||||
const browser = await puppeteer.launch();
|
|
||||||
|
|
||||||
// Create a page
|
|
||||||
const page = await browser.newPage();
|
|
||||||
|
|
||||||
// Go to your site
|
|
||||||
await page.goto('YOUR_SITE');
|
|
||||||
|
|
||||||
// Query for an element handle.
|
|
||||||
const element = await page.waitForSelector('div > .class-name');
|
|
||||||
|
|
||||||
// Do something with element...
|
|
||||||
await element.click(); // Just an example.
|
|
||||||
|
|
||||||
// Dispose of handle
|
|
||||||
await element.dispose();
|
|
||||||
|
|
||||||
// Close browser.
|
|
||||||
await browser.close();
|
|
||||||
})();
|
|
||||||
```
|
|
||||||
|
|
||||||
## `P` Selectors
|
|
||||||
|
|
||||||
Puppeteer uses a superset of the CSS selector syntax for querying. We call this syntax _P selectors_ and it's supercharged with extra capabilities such as deep combinators and text selection.
|
|
||||||
|
|
||||||
:::caution
|
|
||||||
|
|
||||||
Although P selectors look like real CSS selectors (we intentionally designed it this way), they should not be used for actually CSS styling. They are designed only for Puppeteer.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
:::note
|
|
||||||
|
|
||||||
P selectors only work on the first "depth" of selectors; for example, `:is(div >>> a)` will not work.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
### `>>>` and `>>>>` combinators
|
|
||||||
|
|
||||||
The `>>>` and `>>>>` are called _deep descendent_ and _deep_ combinators respectively. Both combinators have the effect of going into shadow hosts with `>>>` going into every shadow host under a node and `>>>>` going into the immediate one (if the node is a shadow host; otherwise, it's a no-op).
|
|
||||||
|
|
||||||
:::note
|
|
||||||
|
|
||||||
A common question is when should `>>>>` be chosen over `>>>` considering the flexibility of `>>>`. A similar question can be asked about `>` and a space; choose `>` if you do not need to query all elements under a given node and a space otherwise. This answer extends to `>>>>` (`>`) and `>>>` (space) naturally.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
Suppose we have the markup
|
|
||||||
|
|
||||||
```html
|
|
||||||
<custom-element>
|
|
||||||
<template shadowrootmode="open">
|
|
||||||
<slot></slot>
|
|
||||||
</template>
|
|
||||||
<custom-element>
|
|
||||||
<template shadowrootmode="open">
|
|
||||||
<slot></slot>
|
|
||||||
</template>
|
|
||||||
<custom-element>
|
|
||||||
<template shadowrootmode="open">
|
|
||||||
<slot></slot>
|
|
||||||
</template>
|
|
||||||
<h2>Light content</h2>
|
|
||||||
</custom-element>
|
|
||||||
</custom-element>
|
|
||||||
</custom-element>
|
|
||||||
```
|
|
||||||
|
|
||||||
> Note: `<template shadowrootmode="open">` is not supported on Firefox.
|
|
||||||
> You can read more about it [here](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template#attributes).
|
|
||||||
|
|
||||||
Then `custom-element >>> h2` will return `h2`, but `custom-element >>>> h2` will return nothing since the inner `h2` is in a deeper shadow root.
|
|
||||||
|
|
||||||
### `P`-elements
|
|
||||||
|
|
||||||
`P` elements are [pseudo-elements](https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements) with a `-p` vendor prefix. It allows you to enhance your selectors with Puppeteer-specific query engines such as XPath, text queries, and ARIA.
|
|
||||||
|
|
||||||
#### Text selectors (`-p-text`)
|
|
||||||
|
|
||||||
Text selectors will select "minimal" elements containing the given text, even within (open) shadow roots. Here, "minimum" means the deepest elements that contain a given text, but not their parents (which technically will also contain the given text).
|
|
||||||
|
|
||||||
##### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const element = await page.waitForSelector('div ::-p-text(My name is Jun)');
|
|
||||||
// You can also use escapes.
|
|
||||||
const element = await page.waitForSelector(
|
|
||||||
':scope >>> ::-p-text(My name is Jun \\(pronounced like "June"\\))'
|
|
||||||
);
|
|
||||||
// or quotes
|
|
||||||
const element = await page.waitForSelector(
|
|
||||||
'div >>>> ::-p-text("My name is Jun (pronounced like \\"June\\")"):hover'
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
#### XPath selectors (`-p-xpath`)
|
|
||||||
|
|
||||||
XPath selectors will use the browser's native [`Document.evaluate`](https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate) to query for elements.
|
|
||||||
|
|
||||||
##### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const element = await page.waitForSelector('::-p-xpath(h2)');
|
|
||||||
```
|
|
||||||
|
|
||||||
#### ARIA selectors (`-p-aria`)
|
|
||||||
|
|
||||||
ARIA selectors can be used to find elements with a given ARIA label. These labels are computed using Chrome's internal representation.
|
|
||||||
|
|
||||||
##### Example
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const node = await page.waitForSelector('::-p-aria(Submit)');
|
|
||||||
const node = await page.waitForSelector(
|
|
||||||
'::-p-aria([name="Click me"][role="button"])'
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Custom selectors
|
|
||||||
|
|
||||||
Puppeteer provides users the ability to add their own query selectors to Puppeteer using [Puppeteer.registerCustomQueryHandler](../api/puppeteer.registercustomqueryhandler.md). This is useful for creating custom selectors based on framework objects or other vendor-specific objects.
|
|
||||||
|
|
||||||
#### Custom Selectors
|
|
||||||
|
|
||||||
You can register a custom query handler that allows you to create custom selectors. For example, define a query handler for `getById` selectors:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
Puppeteer.registerCustomQueryHandler('getById', {
|
|
||||||
queryOne: (elementOrDocument, selector) => {
|
|
||||||
return elementOrDocument.querySelector(`[id="${CSS.escape(selector)}"]`);
|
|
||||||
},
|
|
||||||
// Note: for demonstation perpose only `id` should be page unique
|
|
||||||
queryAll: (elementOrDocument, selector) => {
|
|
||||||
return elementOrDocument.querySelectorAll(`[id="${CSS.escape(selector)}"]`);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
You can now use it as following:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const node = await page.waitForSelector('::-p-getById(elementId)');
|
|
||||||
// OR used in conjunction with other selectors
|
|
||||||
const moreSpecificNode = await page.waitForSelector(
|
|
||||||
'.side-bar ::-p-getById(elementId)'
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Custom framework components selector
|
|
||||||
|
|
||||||
:::caution
|
|
||||||
|
|
||||||
Be careful when relying on internal APIs of libraries or frameworks. They can change at any time.
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
Find Vue components by name by using Vue internals for querying:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
Puppeteer.registerCustomQueryHandler('vue', {
|
|
||||||
queryOne: (element, name) => {
|
|
||||||
const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT);
|
|
||||||
do {
|
|
||||||
const currentNode = walker.currentNode;
|
|
||||||
if (
|
|
||||||
currentNode.__vnode?.ctx?.type?.name.toLowerCase() ===
|
|
||||||
name.toLocaleLowerCase()
|
|
||||||
) {
|
|
||||||
return currentNode;
|
|
||||||
}
|
|
||||||
} while (walker.nextNode());
|
|
||||||
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
Query the Vue component as following:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const element = await page.$('::-p-vue(MyComponent)');
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Web Components
|
|
||||||
|
|
||||||
Web Components create their own tag so you can query them by the tag name:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const element = await page.$('my-web-component');
|
|
||||||
```
|
|
||||||
|
|
||||||
Extend `HTMLElementTagNameMap` to define types for custom tags. This allows Puppeteer to infer the return type for the ElementHandle:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
declare global {
|
|
||||||
interface HTMLElementTagNameMap {
|
|
||||||
'my-web-component': MyWebComponent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
28
docs/guides/screenshots.md
Normal file
28
docs/guides/screenshots.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# Screenshots
|
||||||
|
|
||||||
|
For capturing screenshots use [`Page.screenshot()`](https://pptr.dev/api/puppeteer.screenshot.pdf).
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const browser = await puppeteer.launch();
|
||||||
|
const page = await browser.newPage();
|
||||||
|
await page.goto('https://news.ycombinator.com', {
|
||||||
|
waitUntil: 'networkidle2',
|
||||||
|
});
|
||||||
|
await page.screenshot({
|
||||||
|
path: 'hn.pdf',
|
||||||
|
});
|
||||||
|
|
||||||
|
await browser.close();
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also capture a screenshot of a specific element using [`ElementHandle.screenshot()`](https://pptr.dev/api/puppeteer.elementhandle.screenshot):
|
||||||
|
|
||||||
|
```ts
|
||||||
|
const fileElement = await page.waitForSelector('div');
|
||||||
|
await fileElement.screenshot({
|
||||||
|
path: 'div.png',
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, [`ElementHandle.screenshot()`](https://pptr.dev/api/puppeteer.elementhandle.screenshot) tries to scroll the element into view
|
||||||
|
if it is hidden.
|
14
docs/guides/system-requirements.md
Normal file
14
docs/guides/system-requirements.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# System requirements
|
||||||
|
|
||||||
|
- Node 18+. Puppeteer follows the latest
|
||||||
|
[maintenance LTS](https://github.com/nodejs/Release#release-schedule) version of
|
||||||
|
Node
|
||||||
|
|
||||||
|
- TypeScript 4.7.4+ (If used with TypeScript)
|
||||||
|
|
||||||
|
- Operating systems:
|
||||||
|
|
||||||
|
- Windows, x64 architecture
|
||||||
|
- MacOS, x64 and arm64 architectures
|
||||||
|
- Debian/Ubuntu Linux, with x64 arhicture
|
||||||
|
- Required system packages https://source.chromium.org/chromium/chromium/src/+/main:chrome/installer/linux/debian/dist_package_versions.json
|
25
docs/guides/what-is-puppeteer.md
Normal file
25
docs/guides/what-is-puppeteer.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# What is Puppeteer?
|
||||||
|
|
||||||
|
Puppeteer is a Node.js library which provides a high-level API to control
|
||||||
|
Chrome/Chromium over the
|
||||||
|
[DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/).
|
||||||
|
Puppeteer runs in
|
||||||
|
[headless](https://developer.chrome.com/docs/chromium/new-headless/)
|
||||||
|
mode by default, but can be configured to run in full ("headful")
|
||||||
|
Chrome/Chromium.
|
||||||
|
|
||||||
|
# Features
|
||||||
|
|
||||||
|
Most things that you can do manually in the browser can be done using Puppeteer!
|
||||||
|
Here are a few examples to get you started:
|
||||||
|
|
||||||
|
- Automate form submission, UI testing, keyboard input, etc.
|
||||||
|
- Create an automated testing environment using the latest JavaScript and
|
||||||
|
browser features.
|
||||||
|
- Capture a
|
||||||
|
[timeline trace](https://developer.chrome.com/docs/devtools/performance/reference)
|
||||||
|
of your site to help diagnose performance issues.
|
||||||
|
- [Test Chrome Extensions](https://pptr.dev/guides/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)).
|
261
docs/index.md
261
docs/index.md
@ -1,261 +0,0 @@
|
|||||||
# Puppeteer
|
|
||||||
|
|
||||||
[![build](https://github.com/puppeteer/puppeteer/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/puppeteer/puppeteer/actions/workflows/ci.yml)
|
|
||||||
[![npm puppeteer package](https://img.shields.io/npm/v/puppeteer.svg)](https://npmjs.org/package/puppeteer)
|
|
||||||
|
|
||||||
<img src="https://user-images.githubusercontent.com/10379601/29446482-04f7036a-841f-11e7-9872-91d1fc2ea683.png" height="200" align="right"/>
|
|
||||||
|
|
||||||
#### [Guides](https://pptr.dev/category/guides) | [API](https://pptr.dev/api) | [FAQ](https://pptr.dev/faq) | [Contributing](https://pptr.dev/contributing) | [Troubleshooting](https://pptr.dev/troubleshooting)
|
|
||||||
|
|
||||||
> Puppeteer is a Node.js library which provides a high-level API to control
|
|
||||||
> Chrome/Chromium over the
|
|
||||||
> [DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/).
|
|
||||||
> Puppeteer runs in
|
|
||||||
> [headless](https://developer.chrome.com/docs/chromium/new-headless/)
|
|
||||||
> mode by default, but can be configured to run in full ("headful")
|
|
||||||
> Chrome/Chromium.
|
|
||||||
|
|
||||||
#### 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 automated testing environment using the latest JavaScript and
|
|
||||||
browser features.
|
|
||||||
- Capture a
|
|
||||||
[timeline trace](https://developer.chrome.com/docs/devtools/performance/reference)
|
|
||||||
of your site to help diagnose performance issues.
|
|
||||||
- [Test Chrome Extensions](https://pptr.dev/guides/chrome-extensions).
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
To use Puppeteer in your project, run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm i puppeteer
|
|
||||||
# or using yarn
|
|
||||||
yarn add puppeteer
|
|
||||||
# or using pnpm
|
|
||||||
pnpm i puppeteer
|
|
||||||
```
|
|
||||||
|
|
||||||
When you install Puppeteer, it automatically downloads a recent version of
|
|
||||||
[Chrome for Testing](https://developer.chrome.com/blog/chrome-for-testing/) (~170MB macOS, ~282MB Linux, ~280MB Windows) and a `chrome-headless-shell` binary (starting with Puppeteer v21.6.0) that is [guaranteed to
|
|
||||||
work](https://pptr.dev/faq#q-why-doesnt-puppeteer-vxxx-work-with-chromium-vyyy)
|
|
||||||
with Puppeteer. The browser is downloaded to the `$HOME/.cache/puppeteer` folder
|
|
||||||
by default (starting with Puppeteer v19.0.0). See [configuration](https://pptr.dev/api/puppeteer.configuration) for configuration options and environmental variables to control the download behavior.
|
|
||||||
|
|
||||||
If you deploy a project using Puppeteer to a hosting provider, such as Render or
|
|
||||||
Heroku, you might need to reconfigure the location of the cache to be within
|
|
||||||
your project folder (see an example below) because not all hosting providers
|
|
||||||
include `$HOME/.cache` into the project's deployment.
|
|
||||||
|
|
||||||
For a version of Puppeteer without the browser installation, see
|
|
||||||
[`puppeteer-core`](#puppeteer-core).
|
|
||||||
|
|
||||||
If used with TypeScript, the minimum supported TypeScript version is `4.7.4`.
|
|
||||||
|
|
||||||
#### Configuration
|
|
||||||
|
|
||||||
Puppeteer uses several defaults that can be customized through configuration
|
|
||||||
files.
|
|
||||||
|
|
||||||
For example, to change the default cache directory Puppeteer uses to install
|
|
||||||
browsers, you can add a `.puppeteerrc.cjs` (or `puppeteer.config.cjs`) at the
|
|
||||||
root of your application with the contents
|
|
||||||
|
|
||||||
```js
|
|
||||||
const {join} = require('path');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {import("puppeteer").Configuration}
|
|
||||||
*/
|
|
||||||
module.exports = {
|
|
||||||
// Changes the cache location for Puppeteer.
|
|
||||||
cacheDirectory: join(__dirname, '.cache', 'puppeteer'),
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
After adding the configuration file, you will need to remove and reinstall
|
|
||||||
`puppeteer` for it to take effect.
|
|
||||||
|
|
||||||
See the [configuration guide](https://pptr.dev/guides/configuration) for more
|
|
||||||
information.
|
|
||||||
|
|
||||||
#### `puppeteer-core`
|
|
||||||
|
|
||||||
For every release since v1.7.0 we publish two packages:
|
|
||||||
|
|
||||||
- [`puppeteer`](https://www.npmjs.com/package/puppeteer)
|
|
||||||
- [`puppeteer-core`](https://www.npmjs.com/package/puppeteer-core)
|
|
||||||
|
|
||||||
`puppeteer` is a _product_ for browser automation. When installed, it downloads
|
|
||||||
a version of Chrome, which it then drives using `puppeteer-core`. Being an
|
|
||||||
end-user product, `puppeteer` automates several workflows using reasonable
|
|
||||||
defaults [that can be customized](https://pptr.dev/guides/configuration).
|
|
||||||
|
|
||||||
`puppeteer-core` is a _library_ to help drive anything that supports DevTools
|
|
||||||
protocol. Being a library, `puppeteer-core` is fully driven through its
|
|
||||||
programmatic interface implying no defaults are assumed and `puppeteer-core`
|
|
||||||
will not download Chrome when installed.
|
|
||||||
|
|
||||||
You should use `puppeteer-core` if you are
|
|
||||||
[connecting to a remote browser](https://pptr.dev/api/puppeteer.puppeteer.connect)
|
|
||||||
or [managing browsers yourself](https://pptr.dev/browsers-api/).
|
|
||||||
If you are managing browsers yourself, you will need to call
|
|
||||||
[`puppeteer.launch`](https://pptr.dev/api/puppeteer.puppeteernode.launch) with
|
|
||||||
an explicit
|
|
||||||
[`executablePath`](https://pptr.dev/api/puppeteer.launchoptions)
|
|
||||||
(or [`channel`](https://pptr.dev/api/puppeteer.launchoptions) if it's
|
|
||||||
installed in a standard location).
|
|
||||||
|
|
||||||
When using `puppeteer-core`, remember to change the import:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import puppeteer from 'puppeteer-core';
|
|
||||||
```
|
|
||||||
|
|
||||||
### Usage
|
|
||||||
|
|
||||||
Puppeteer follows the latest
|
|
||||||
[maintenance LTS](https://github.com/nodejs/Release#release-schedule) version of
|
|
||||||
Node.
|
|
||||||
|
|
||||||
Puppeteer will be familiar to people using other browser testing frameworks. You
|
|
||||||
[launch](https://pptr.dev/api/puppeteer.puppeteernode.launch)/[connect](https://pptr.dev/api/puppeteer.puppeteernode.connect)
|
|
||||||
a [browser](https://pptr.dev/api/puppeteer.browser),
|
|
||||||
[create](https://pptr.dev/api/puppeteer.browser.newpage) some
|
|
||||||
[pages](https://pptr.dev/api/puppeteer.page), and then manipulate them with
|
|
||||||
[Puppeteer's API](https://pptr.dev/api).
|
|
||||||
|
|
||||||
For more in-depth usage, check our [guides](https://pptr.dev/category/guides)
|
|
||||||
and [examples](https://github.com/puppeteer/puppeteer/tree/main/examples).
|
|
||||||
|
|
||||||
#### Example
|
|
||||||
|
|
||||||
The following example searches [developer.chrome.com](https://developer.chrome.com/) for blog posts with text "automate beyond recorder", click on the first result and print the full title of the blog post.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
import puppeteer from 'puppeteer';
|
|
||||||
|
|
||||||
(async () => {
|
|
||||||
// Launch the browser and open a new blank page
|
|
||||||
const browser = await puppeteer.launch();
|
|
||||||
const page = await browser.newPage();
|
|
||||||
|
|
||||||
// Navigate the page to a URL
|
|
||||||
await page.goto('https://developer.chrome.com/');
|
|
||||||
|
|
||||||
// Set screen size
|
|
||||||
await page.setViewport({width: 1080, height: 1024});
|
|
||||||
|
|
||||||
// Type into search box
|
|
||||||
await page.type('.devsite-search-field', 'automate beyond recorder');
|
|
||||||
|
|
||||||
// Wait and click on first result
|
|
||||||
const searchResultSelector = '.devsite-result-item-link';
|
|
||||||
await page.waitForSelector(searchResultSelector);
|
|
||||||
await page.click(searchResultSelector);
|
|
||||||
|
|
||||||
// Locate the full title with a unique string
|
|
||||||
const textSelector = await page.waitForSelector(
|
|
||||||
'text/Customize and automate'
|
|
||||||
);
|
|
||||||
const fullTitle = await textSelector?.evaluate(el => el.textContent);
|
|
||||||
|
|
||||||
// Print the full title
|
|
||||||
console.log('The title of this blog post is "%s".', fullTitle);
|
|
||||||
|
|
||||||
await browser.close();
|
|
||||||
})();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Default runtime settings
|
|
||||||
|
|
||||||
**1. Uses Headless mode**
|
|
||||||
|
|
||||||
By default Puppeteer launches Chrome in
|
|
||||||
[the Headless mode](https://developer.chrome.com/docs/chromium/new-headless/).
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const browser = await puppeteer.launch();
|
|
||||||
// Equivalent to
|
|
||||||
const browser = await puppeteer.launch({headless: true});
|
|
||||||
```
|
|
||||||
|
|
||||||
Before v22, Puppeteer launched the [old Headless mode](https://developer.chrome.com/docs/chromium/new-headless/) by default.
|
|
||||||
The old headless mode is now known as
|
|
||||||
[`chrome-headless-shell`](https://developer.chrome.com/blog/chrome-headless-shell)
|
|
||||||
and ships as a separate binary. `chrome-headless-shell` does not match the
|
|
||||||
behavior of the regular Chrome completely but it is currently more performant
|
|
||||||
for automation tasks where the complete Chrome feature set is not needed. If the performance
|
|
||||||
is more important for your use case, switch to `chrome-headless-shell` as following:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const browser = await puppeteer.launch({headless: 'shell'});
|
|
||||||
```
|
|
||||||
|
|
||||||
To launch a "headful" version of Chrome, set the
|
|
||||||
[`headless`](https://pptr.dev/api/puppeteer.browserlaunchargumentoptions) to `false`
|
|
||||||
option when launching a browser:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const browser = await puppeteer.launch({headless: false});
|
|
||||||
```
|
|
||||||
|
|
||||||
**2. Runs a bundled version of Chrome**
|
|
||||||
|
|
||||||
By default, Puppeteer downloads and uses a specific version of Chrome so its
|
|
||||||
API is guaranteed to work out of the box. To use Puppeteer with a different
|
|
||||||
version of Chrome or Chromium, pass in the executable's path when creating a
|
|
||||||
`Browser` instance:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
const browser = await puppeteer.launch({executablePath: '/path/to/Chrome'});
|
|
||||||
```
|
|
||||||
|
|
||||||
You can also use Puppeteer with Firefox. See
|
|
||||||
[status of cross-browser support](https://pptr.dev/faq#q-what-is-the-status-of-cross-browser-support) 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/+/refs/heads/main/docs/chromium_browser_vs_google_chrome.md)
|
|
||||||
describes some differences for Linux users.
|
|
||||||
|
|
||||||
**3. Creates a fresh user profile**
|
|
||||||
|
|
||||||
Puppeteer creates its own browser user profile which it **cleans up on every
|
|
||||||
run**.
|
|
||||||
|
|
||||||
#### Using Docker
|
|
||||||
|
|
||||||
See our [Docker guide](https://pptr.dev/guides/docker).
|
|
||||||
|
|
||||||
#### Using Chrome Extensions
|
|
||||||
|
|
||||||
See our [Chrome extensions guide](https://pptr.dev/guides/chrome-extensions).
|
|
||||||
|
|
||||||
## Resources
|
|
||||||
|
|
||||||
- [API Documentation](https://pptr.dev/api)
|
|
||||||
- [Guides](https://pptr.dev/category/guides)
|
|
||||||
- [Examples](https://github.com/puppeteer/puppeteer/tree/main/examples)
|
|
||||||
- [Community list of Puppeteer resources](https://github.com/transitive-bullshit/awesome-puppeteer)
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Check out our [contributing guide](https://pptr.dev/contributing) to get an
|
|
||||||
overview of Puppeteer development.
|
|
||||||
|
|
||||||
## FAQ
|
|
||||||
|
|
||||||
Our [FAQ](https://pptr.dev/faq) has migrated to
|
|
||||||
[our site](https://pptr.dev/faq).
|
|
@ -83,6 +83,22 @@ const config = {
|
|||||||
from: '/guides',
|
from: '/guides',
|
||||||
to: '/category/guides',
|
to: '/category/guides',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
from: '/guides/query-selectors',
|
||||||
|
to: '/guides/page-interactions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: '/guides/query-selectors-legacy',
|
||||||
|
to: '/guides/page-interactions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: '/guides/locators',
|
||||||
|
to: '/guides/page-interactions',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: '/guides/evaluate-javascript',
|
||||||
|
to: '/guides/javascript-execution',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
from: '/chromium-support',
|
from: '/chromium-support',
|
||||||
to: '/supported-browsers',
|
to: '/supported-browsers',
|
||||||
@ -260,8 +276,8 @@ const config = {
|
|||||||
items: [
|
items: [
|
||||||
...[
|
...[
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'docSidebar',
|
||||||
docId: 'index',
|
sidebarId: 'docs',
|
||||||
label: 'Docs',
|
label: 'Docs',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -6,25 +6,54 @@
|
|||||||
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
||||||
module.exports = {
|
module.exports = {
|
||||||
docs: [
|
docs: [
|
||||||
'index',
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Introduction',
|
||||||
|
link: {
|
||||||
|
type: 'generated-index',
|
||||||
|
title: 'Introduction',
|
||||||
|
keywords: ['introduction'],
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
'guides/what-is-puppeteer',
|
||||||
|
'guides/installation',
|
||||||
|
'guides/getting-started',
|
||||||
|
'guides/system-requirements',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Core concepts',
|
||||||
|
link: {
|
||||||
|
type: 'generated-index',
|
||||||
|
title: 'Core concepts',
|
||||||
|
keywords: ['core-concepts'],
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
'guides/browser-management',
|
||||||
|
'guides/page-interactions',
|
||||||
|
'guides/javascript-execution',
|
||||||
|
'guides/network-logging',
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'category',
|
type: 'category',
|
||||||
label: 'Guides',
|
label: 'Guides',
|
||||||
link: {
|
link: {
|
||||||
type: 'generated-index',
|
type: 'generated-index',
|
||||||
title: 'Puppeteer Guides',
|
title: 'Guides',
|
||||||
keywords: ['guides'],
|
keywords: ['guides'],
|
||||||
},
|
},
|
||||||
collapsed: false,
|
|
||||||
items: [
|
items: [
|
||||||
'guides/configuration',
|
'guides/configuration',
|
||||||
'guides/query-selectors',
|
|
||||||
'guides/locators',
|
|
||||||
'guides/evaluate-javascript',
|
|
||||||
'guides/docker',
|
|
||||||
'guides/request-interception',
|
|
||||||
'guides/chrome-extensions',
|
|
||||||
'guides/debugging',
|
'guides/debugging',
|
||||||
|
'guides/network-interception',
|
||||||
|
'guides/headless-modes',
|
||||||
|
'guides/screenshots',
|
||||||
|
'guides/pdf-generation',
|
||||||
|
'guides/chrome-extensions',
|
||||||
|
'guides/cookies',
|
||||||
|
'guides/files',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -35,19 +64,31 @@ module.exports = {
|
|||||||
title: 'Puppeteer Integrations',
|
title: 'Puppeteer Integrations',
|
||||||
keywords: ['integrations'],
|
keywords: ['integrations'],
|
||||||
},
|
},
|
||||||
collapsed: false,
|
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
type: 'doc',
|
type: 'doc',
|
||||||
label: 'Angular Schematics',
|
label: 'Angular',
|
||||||
id: 'integrations/ng-schematics',
|
id: 'guides/ng-schematics',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'doc',
|
||||||
|
label: 'Docker',
|
||||||
|
id: 'guides/docker',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'category',
|
||||||
|
label: 'Community',
|
||||||
|
link: {
|
||||||
|
type: 'generated-index',
|
||||||
|
title: 'Community',
|
||||||
|
keywords: ['community'],
|
||||||
|
},
|
||||||
|
items: ['contributing', 'troubleshooting', 'guides/links'],
|
||||||
|
},
|
||||||
'webdriver-bidi',
|
'webdriver-bidi',
|
||||||
'supported-browsers',
|
'supported-browsers',
|
||||||
'troubleshooting',
|
|
||||||
'contributing',
|
|
||||||
'faq',
|
'faq',
|
||||||
],
|
],
|
||||||
api: [
|
api: [
|
||||||
|
Loading…
Reference in New Issue
Block a user