docs: generate all docs off docs-src (#4858)

This commit is contained in:
Pavel Feldman 2020-12-30 18:04:51 -08:00 committed by GitHub
parent 75198f044d
commit ba291372e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 7150 additions and 2035 deletions

56
docs-src/actionability.md Normal file
View file

@ -0,0 +1,56 @@
# Actionability
Playwright does a range of actionability checks on the elements before performing certain actions. These checks ensure that action behaves as expected, for example Playwright does not click on a disabled button.
Playwright waits until all the relevant actionability checks pass before performing an action. This means that action will fail with `TimeoutError` if checks do not pass within the specified `timeout`.
Some actions like `page.click()` support `{force: true}` option that disable non-essential actionability checks, for example passing `force` to `click()` method will not check that the target element actually receives click events.
| Actions | Performed checks |
| ------ | ------- |
| `check()`<br>`click()`<br>`dblclick()`<br>`tap()`<br>`uncheck()` | [Visible]<br>[Stable]<br>[Enabled]<br>[Receiving Events]<br>[Attached] |
| `hover()` | [Visible]<br>[Stable]<br>[Receiving Events]<br>[Attached] |
| `fill()` | [Visible]<br>[Enabled]<br>[Editable]<br>[Attached] |
| `dispatchEvent()`<br>`focus()`<br>`press()`<br>`setInputFiles()`<br>`selectOption()`<br>`type()` | [Attached] |
| `scrollIntoViewIfNeeded()`<br>`screenshot()` | [Visible]<br>[Stable]<br>[Attached] |
| `selectText()` | [Visible]<br>[Attached] |
| `getAttribute()`<br>`innerText()`<br>`innerHTML()`<br>`textContent()` | [Attached] |
### Visible
Element is considered visible when it has non-empty bounding box and does not have `visibility:hidden` computed style. Note that elements of zero size or with `display:none` are not considered visible.
### Stable
Element is considered stable when it has maintained the same bounding box for at least two consecutive animation frames.
### Enabled
Element is considered enabled when it is not a `<button>`, `<select>` or `<input>` with a `disabled` property set.
### Editable
Element is considered editable when it does not have `readonly` property set.
### Receiving events
Element is considered receiving pointer events when it is the hit target of the pointer event at the action point. For example, when clicking at the point `(10;10)`, Playwright checks whether some other element (usually an overlay) will instead capture the click at `(10;10)`.
### Attached
Element is considered attached when it is [connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected) to a Document or a ShadowRoot.
Attached check differs between selector-based and handle-based actions, like `page.click(selector, options)` as opposite to `elementHandle.click(options)`:
- For selector-based actions, Playwright first waits for an element matching `selector` to be attached to the DOM, and then checks that element is still attached before performing the action. If element was detached, the action is retried from the start.
- For handle-based actions, Playwright throws if the element is not attached.
For example, consider a scenario where Playwright will click `Sign Up` button regardless of when the `page.click()` call was made:
- page is checking that user name is unique and `Sign Up` button is disabled;
- after checking with the server, the disabled `Sign Up` button is replaced with another one that is now enabled.
[Visible]: #visible "Visible"
[Stable]: #stable "Stable"
[Enabled]: #enabled "Enabled"
[Editable]: #editable "Editable"
[Receiving Events]: #receiving-events "Receiving Events"
[Attached]: #attached "Attached"

View file

@ -1,6 +1,6 @@
### EvaluationArgument
Playwright evaluation methods like [page.evaluate(pageFunction[, arg])](#pageevaluatepagefunction-arg) take a single optional argument. This argument can be a mix of [Serializable] values and [JSHandle] or [ElementHandle] instances. Handles are automatically converted to the value they represent.
Playwright evaluation methods like [`method: Page.evaluate`] take a single optional argument. This argument can be a mix of [Serializable] values and [JSHandle] or [ElementHandle] instances. Handles are automatically converted to the value they represent.
See examples for various scenarios:
@ -80,9 +80,9 @@ $ node playwright-script.js
### Working with selectors
Selector describes an element in the page. It can be used to obtain `ElementHandle` (see [page.$()](#pageselector) for example) or shortcut element operations to avoid intermediate handle (see [page.click()](#pageclickselector-options) for example).
Selector describes an element in the page. It can be used to obtain `ElementHandle` (see [`method: Page.$`] for example) or shortcut element operations to avoid intermediate handle (see [`method: Page.click`] for example).
Selector has the following format: `engine=body [>> engine=body]*`. Here `engine` is one of the supported [selector engines](selectors.md) (e.g. `css` or `xpath`), and `body` is a selector body in the format of the particular engine. When multiple `engine=body` clauses are present (separated by `>>`), next one is queried relative to the previous one's result.
Selector has the following format: `engine=body [>> engine=body]*`. Here `engine` is one of the supported [selector engines](./selectors.md) (e.g. `css` or `xpath`), and `body` is a selector body in the format of the particular engine. When multiple `engine=body` clauses are present (separated by `>>`), next one is queried relative to the previous one's result.
Playwright also supports the following CSS extensions:
* `:text("string")` - Matches elements that contain specific text node. Learn more about [text selector](./selectors.md#css-extension-text).

121
docs-src/assertions.md Normal file
View file

@ -0,0 +1,121 @@
# Assertions
The Playwright API can be used to read element contents and properties for test assertions. These values are fetched from the browser page and asserted in
Node.js.
The examples in this guide use the built-in [`assert` module](https://nodejs.org/api/assert.html), but they can be used with any assertion library (like [Expect](https://www.npmjs.com/package/expect) or [Chai](https://www.npmjs.com/package/chai)). See [Test runners](test-runners.md) for more info.
<!-- GEN:toc-top-level -->
<!-- GEN:stop -->
<br/>
## Common patterns
Playwright provides convenience APIs for common assertion tasks, like finding the
text content of an element. These APIs require a [selector](./selectors.md) to locate
the element.
```js
// Assert text content
const content = await page.textContent('nav:first-child');
assert(content === 'home');
// Assert inner text
const text = await page.innerText('.selected');
assert(text === 'value');
// Assert inner HTML
const html = await page.innerHTML('div.result');
assert(html === '<p>Result</p>')
// Assert `checked` attribute
const checked = await page.getAttribute('input', 'checked');
assert(checked);
```
#### API reference
- [`method: Page.textContent`]
- [`method: Page.innerText`]
- [`method: Page.innerHTML`]
- [`method: Page.getAttribute`]
- [`method: Frame.textContent`]
- [`method: Frame.innerText`]
- [`method: Frame.innerHTML`]
- [`method: Frame.getAttribute`]
<br/>
## Element Handles
[ElementHandle] objects represent in-page DOM
elements. They can be used to assert for multiple properties of the element.
It is recommended to fetch the `ElementHandle` object with
[`method: Page.waitForSelector`] or [`method: Frame.waitForSelector`]. These
APIs wait for the element to be visible and then return an `ElementHandle`.
```js
// Get the element handle
const elementHandle = page.waitForSelector('#box');
// Assert bounding box for the element
const boundingBox = await elementHandle.boundingBox();
assert(boundingBox.width === 100);
// Assert attribute for the element
const classNames = await elementHandle.getAttribute('class');
assert(classNames.includes('highlighted'));
```
#### API reference
- [`method: ElementHandle.textContent`]
- [`method: ElementHandle.innerText`]
- [`method: ElementHandle.innerHTML`]
- [`method: ElementHandle.getAttribute`]
- [`method: ElementHandle.boundingBox`]
<br/>
## Custom assertions
With Playwright, you can also write custom JavaScript to run in the context of
the browser. This is useful in situations where you want to assert for values
that are not covered by the convenience APIs above.
The following APIs do not auto-wait for the element. It is recommended to use
[`method: Page.waitForSelector`] or
[`method: Frame.waitForSelector`].
```js
// Assert local storage value
const userId = page.evaluate(() => window.localStorage.getItem('userId'));
assert(userId);
// Assert value for input element
await page.waitForSelector('#search');
const value = await page.$eval('#search', el => el.value);
assert(value === 'query');
// Assert computed style
const fontSize = await page.$eval('div', el => window.getComputedStyle(el).fontSize);
assert(fontSize === '16px');
// Assert list length
const length = await page.$$eval('li.selected', (items) => items.length);
assert(length === 3);
```
#### API reference
- [`method: Page.evaluate`]
- [`method: Page.$eval`]
- [`method: Page.$$eval`]
- [`method: Frame.evaluate`]
- [`method: Frame.$eval`]
- [`method: Frame.$$eval`]
- [`method: ElementHandle.$eval`]
- [`method: ElementHandle.$$eval`]
- [EvaluationArgument]

166
docs-src/auth.md Normal file
View file

@ -0,0 +1,166 @@
# Authentication
Playwright can be used to automate scenarios that require authentication.
Tests written with Playwright execute in isolated clean-slate environments called
[browser contexts](./core-concepts.md#browser-contexts). This isolation model
improves reproducibility and prevents cascading test failures. New browser
contexts can load existing authentication state. This eliminates the need to
login in every context and speeds up test execution.
> Note: This guide covers cookie/token-based authentication (logging in via the
app UI). For [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication)
use [`browser.newContext`](./network.md#http-authentication).
<!-- GEN:toc -->
<!-- GEN:stop -->
## Automate logging in
The Playwright API can automate interaction with a login form. See
[Input guide](./input.md) for more details.
The following example automates login on GitHub. Once these steps are executed,
the browser context will be authenticated.
```js
const page = await context.newPage();
await page.goto('https://github.com/login');
// Interact with login form
await page.click('text=Login');
await page.fill('input[name="login"]', USERNAME);
await page.fill('input[name="password"]', PASSWORD);
await page.click('text=Submit');
// Verify app is logged in
```
These steps can be executed for every browser context. However, redoing login
for every test can slow down test execution. To prevent that, we will reuse
existing authentication state in new browser contexts.
## Reuse authentication state
Web apps use cookie-based or token-based authentication, where authenticated
state is stored as [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)
or in [local storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage).
The Playwright API can be used to retrieve this state from authenticated contexts
and then load it into new contexts.
Cookies and local storage state can be used across different browsers. They depend
on your application's authentication model: some apps might require both cookies
and local storage.
The following code snippets retrieve state from an authenticated page/context and
load them into a new context.
### Cookies
```js
// Get cookies and store as an env variable
const cookies = await context.cookies();
process.env.COOKIES = JSON.stringify(cookies);
// Set cookies in a new context
const deserializedCookies = JSON.parse(process.env.COOKIES)
await context.addCookies(deserializedCookies);
```
### Local storage
Local storage ([`window.localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage))
is specific to a particular domain.
```js
// Get local storage and store as env variable
const localStorage = await page.evaluate(() => JSON.stringify(window.localStorage));
process.env.LOCAL_STORAGE = localStorage;
// Set local storage in a new context
const localStorage = process.env.LOCAL_STORAGE;
await context.addInitScript(storage => {
if (window.location.hostname === 'example.com') {
const entries = JSON.parse(storage);
Object.keys(entries).forEach(key => {
window.localStorage.setItem(key, entries[key]);
});
}
}, localStorage);
```
### Session storage
Session storage ([`window.sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage))
is specific to a particular domain.
```js
// Get session storage and store as env variable
const sessionStorage = await page.evaluate(() => JSON.stringify(sessionStorage));
process.env.SESSION_STORAGE = sessionStorage;
// Set session storage in a new context
const sessionStorage = process.env.SESSION_STORAGE;
await context.addInitScript(storage => {
if (window.location.hostname === 'example.com') {
const entries = JSON.parse(storage);
Object.keys(entries).forEach(key => {
window.sessionStorage.setItem(key, entries[key]);
});
}
}, sessionStorage);
```
### Lifecycle
Logging in via the UI and then reusing authentication state can be combined to
implement **login once and run multiple scenarios**. The lifecycle looks like:
1. Run tests (for example, with `npm run test`).
1. Login via UI and retrieve authentication state.
* In Jest, this can be executed in [`globalSetup`](https://jestjs.io/docs/en/configuration#globalsetup-string).
1. In each test, load authentication state in `beforeEach` or `beforeAll` step.
This approach will also **work in CI environments**, since it does not rely
on any external state.
### Example
[This example script](examples/authentication.js) logs in on GitHub.com with
Chromium, and then reuses the logged in cookie state in WebKit.
### API reference
- [BrowserContext]
- [`method: BrowserContext.cookies`]
- [`method: BrowserContext.addCookies`]
- [`method: Page.evaluate`]
- [`method: BrowserContext.addInitScript`]
## Multi-factor authentication
Accounts with multi-factor authentication (MFA) cannot be fully automated, and need
manual intervention. Persistent authentication can be used to partially automate
MFA scenarios.
### Persistent authentication
Web browsers use a directory on disk to store user history, cookies, IndexedDB
and other local state. This disk location is called the [User data directory](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md).
Note that persistent authentication is not suited for CI environments since it
relies on a disk location. User data directories are specific to browser types
and cannot be shared across browser types.
User data directories can be used with the `launchPersistentContext` API.
```js
const { chromium } = require('playwright');
const userDataDir = '/path/to/directory';
const context = await chromium.launchPersistentContext(userDataDir, { headless: false });
// Execute login steps manually in the browser window
```
### Lifecycle
1. Create a user data directory on disk
2. Launch a persistent context with the user data directory and login the MFA account.
3. Reuse user data directory to run automation scenarios.
### API reference
- [BrowserContext]
- [`method: BrowserType.launchPersistentContext`]

272
docs-src/ci.md Normal file
View file

@ -0,0 +1,272 @@
# Continuous Integration
Playwright tests can be executed in CI environments. We have created sample
configurations for common CI providers.
<!-- GEN:toc -->
<!-- GEN:stop -->
## Introduction
3 steps to get your tests running on CI:
1. **Ensure CI agent can run browsers**: Use [our Docker image](docker/README.md)
in Linux agents. Windows and macOS agents do not require any additional dependencies.
1. **Install Playwright**: In most projects, this would be done with `npm ci`
(or `npm install`). Playwright would install the relevant browsers automatically.
1. **Run your tests**: Use `npm test` or equivalent to execute your tests.
## CI configurations
### GitHub Actions
The [Playwright GitHub Action](https://github.com/microsoft/playwright-github-action) can be used to run Playwright tests on GitHub Actions.
```yml
steps:
- uses: microsoft/playwright-github-action@v1
- name: Run your tests
run: npm test
```
We run [our tests](/.github/workflows/tests.yml) on GitHub Actions, across a matrix of 3 platforms (Windows, Linux, macOS) and 3 browsers (Chromium, Firefox, WebKit).
### Docker
We have a [pre-built Docker image](docker/README.md) which can either be used directly, or as a reference to update your existing Docker definitions.
Suggested configuration
1. By default, Docker runs a container with a `/dev/shm` shared memory space 64MB.
This is [typically too small](https://github.com/c0b/chrome-in-docker/issues/1) for Chromium
and will cause Chromium to crash when rendering large pages. To fix, run the container with
`docker run --shm-size=1gb` to increase the size of `/dev/shm`. Since Chromium 65, this is no
longer necessary. Instead, launch the browser with the `--disable-dev-shm-usage` flag:
```js
const browser = await playwright.chromium.launch({
args: ['--disable-dev-shm-usage']
});
```
This will write shared memory files into `/tmp` instead of `/dev/shm`. See
[crbug.com/736452](https://bugs.chromium.org/p/chromium/issues/detail?id=736452) for more details.
1. Using `--ipc=host` is also recommended when using Chromium—without it Chromium can run out of memory
and crash. Learn more about this option in [Docker docs](https://docs.docker.com/engine/reference/run/#ipc-settings---ipc).
1. Seeing other weird errors when launching Chromium? Try running your container
with `docker run --cap-add=SYS_ADMIN` when developing locally.
1. [dumb-init](https://github.com/Yelp/dumb-init) is worth checking out if you're
experiencing a lot of zombies Chromium processes sticking around. There's special
treatment for processes with PID=1, which makes it hard to terminate Chromium
properly in some cases (e.g. in Docker).
### Azure Pipelines
For Windows or macOS agents, no additional configuration required, just install Playwright and run your tests.
For Linux agents, you can use [our Docker container](docker/README.md) with Azure Pipelines support for [running containerized jobs](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/container-phases?view=azure-devops). Alternatively, you can refer to the [Dockerfile](docker/README.md) to see additional dependencies that need to be installed on a Ubuntu agent.
```yml
pool:
vmImage: 'ubuntu-18.04'
container: mcr.microsoft.com/playwright:bionic
steps:
- script: npm install
- script: npm run test
```
### Travis CI
We run our tests on Travis CI over a Linux agent (Ubuntu 18.04).
Suggested configuration
1. [User namespace cloning](http://man7.org/linux/man-pages/man7/user_namespaces.7.html)
should be enabled to support proper sandboxing
1. [xvfb](https://en.wikipedia.org/wiki/Xvfb) should be launched in order to run
Chromium in non-headless mode (e.g. to test Chrome Extensions)
1. If your project does not have `package-lock.json`, Travis would be auto-caching
`node_modules` directory. If you run `npm install` (instead of `npm ci`), it is
possible that the browser binaries are not downloaded. Fix this with [these steps](#exception-node_modules-are-cached) outlined below.
To sum up, your `.travis.yml` might look like this:
```yml
language: node_js
dist: bionic
addons:
apt:
packages:
# These are required to run webkit
- libwoff1
- libopus0
- libwebp6
- libwebpdemux2
- libenchant1c2a
- libgudev-1.0-0
- libsecret-1-0
- libhyphen0
- libgdk-pixbuf2.0-0
- libegl1
- libgles2
- libevent-2.1-6
- libnotify4
- libxslt1.1
- libvpx5
# gstreamer and plugins to support video playback in WebKit.
- gstreamer1.0-gl
- gstreamer1.0-plugins-base
- gstreamer1.0-plugins-good
- gstreamer1.0-plugins-bad
# This is required to run chromium
- libgbm1
# this is needed for running headful tests
- xvfb
# allow headful tests
before_install:
# Enable user namespace cloning
- "sysctl kernel.unprivileged_userns_clone=1"
# Launch XVFB
- "export DISPLAY=:99.0"
- "sh -e /etc/init.d/xvfb start"
```
### CircleCI
We run our tests on CircleCI, with our [pre-built Docker image](docker/README.md). Running Playwright smoothly on CircleCI requires the following steps:
1. Use the pre-built [Docker image](docker/README.md) in your config like so:
```yaml
docker:
- image: mcr.microsoft.com/playwright:bionic
environment:
NODE_ENV: development # Needed if playwright is in `devDependencies`
```
1. If youre using Playwright through Jest, then you may encounter an error spawning child processes:
```
[00:00.0] jest args: --e2e --spec --max-workers=36
Error: spawn ENOMEM
at ChildProcess.spawn (internal/child_process.js:394:11)
```
This is likely caused by Jest autodetecting the number of processes on the entire machine (`36`) rather than the number allowed to your container (`2`). To fix this, set `jest --maxWorkers=2` in your test command.
### Jenkins
Jenkins supports Docker agents for pipelines. Use the [Playwright Docker image](docker/README.md)
to run tests on Jenkins.
```groovy
pipeline {
agent { docker { image 'mcr.microsoft.com/playwright:bionic' } }
stages {
stage('e2e-tests') {
steps {
sh 'npm install'
sh 'npm run test'
}
}
}
}
```
### Bitbucket Pipelines
Bitbucket Pipelines can use public [Docker images as build environments](https://confluence.atlassian.com/bitbucket/use-docker-images-as-build-environments-792298897.html). To run Playwright tests on Bitbucket, use our public Docker image ([see Dockerfile](docker/README.md)).
```yml
image: mcr.microsoft.com/playwright:bionic
```
While the Docker image supports sandboxing for Chromium, it does not work in the Bitbucket Pipelines environment. To launch Chromium on Bitbucket Pipelines, use the `chromiumSandbox: false` launch argument.
```js
const { chromium } = require('playwright');
const browser = await chromium.launch({ chromiumSandbox: false });
```
### GitLab CI
To run Playwright tests on GitLab, use our public Docker image ([see Dockerfile](docker/README.md)).
```yml
stages:
- test
tests:
stage: test
image: mcr.microsoft.com/playwright:bionic
script:
- npm install # This should install playwright
- npm run test
```
## Caching browsers
By default, Playwright downloads browser binaries when the Playwright NPM package
is installed. The NPM packages have a `postinstall` hook that downloads the browser
binaries. This behavior can be [customized with environment variables](./installation.md).
Caching browsers on CI is **strictly optional**: The `postinstall` hooks should
execute and download the browser binaries on every run.
#### Exception: `node_modules` are cached
Most CI providers cache the [npm-cache](https://docs.npmjs.com/cli-commands/cache.html)
directory (located at `$HOME/.npm`). If your CI pipelines caches the `node_modules`
directory and you run `npm install` (instead of `npm ci`), the default configuration
**will not work**. This is because the `npm install` step will find the Playwright NPM
package on disk and not execute the `postinstall` step.
> Travis CI automatically caches `node_modules` if your repo does not have a
`package-lock.json` file.
This behavior can be fixed with one of the following approaches:
1. Move to caching `$HOME/.npm` or the npm-cache directory. (This is the default
behavior in most CI providers.)
1. Set `PLAYWRIGHT_BROWSERS_PATH=0` as the environment variable before running
`npm install`. This will download the browser binaries in the `node_modules`
directory and cache them with the package code. See [installation docs](./installation.md).
1. Use `npm ci` (instead of `npm install`) which forces a clean install: by
removing the existing `node_modules` directory. See [npm docs](https://docs.npmjs.com/cli/ci.html).
1. Cache the browser binaries, with the steps below.
#### Directories to cache
With the default behavior, Playwright downloads the browser binaries in the following
directories:
- `%USERPROFILE%\AppData\Local\ms-playwright` on Windows
- `~/Library/Caches/ms-playwright` on MacOS
- `~/.cache/ms-playwright` on Linux
To cache the browser downloads between CI runs, cache this location in your CI
configuration, against a hash of the Playwright version.
## Debugging browser launches
Playwright supports the `DEBUG` environment variable to output debug logs during execution. Setting it to `pw:browser*` is helpful while debugging `Error: Failed to launch browser` errors.
```
DEBUG=pw:browser* npm run test
```
## Running headful
By default, Playwright launches browsers in headless mode. This can be changed by passing a flag when the browser is launched.
```js
// Works across chromium, firefox and webkit
const { chromium } = require('playwright');
const browser = await chromium.launch({ headless: false });
```
On Linux agents, headful execution requires [Xvfb](https://en.wikipedia.org/wiki/Xvfb) to be installed. Our [Docker image](docker/README.md) and GitHub Action have Xvfb pre-installed. To run browsers in headful mode with Xvfb, add `xvfb-run` before the Node.js command.
```
xvfb-run node index.js
```

143
docs-src/cli.md Normal file
View file

@ -0,0 +1,143 @@
# Playwright CLI
Playwright comes with the command line tools that run via `npx` or as a part of the `npm` scripts.
<!-- GEN:toc -->
<!-- GEN:stop -->
## Usage
```sh
$ npx playwright --help
```
Running from `package.json` script
```json
{
"scripts": {
"help": "playwright --help"
}
}
```
## Generate code
```sh
$ npx playwright codegen wikipedia.org
```
Run `codegen` and perform actions in the browser. Playwright CLI will generate JavaScript code for the user interactions. `codegen` will attempt to generate resilient text-based selectors.
<img src="https://user-images.githubusercontent.com/284612/92536033-7e7ebe00-f1ed-11ea-9e1a-7cbd912e3391.gif">
## Open pages
With `open`, you can use Playwright bundled browsers to browse web pages. Playwright provides cross-platform WebKit builds that can be used to reproduce Safari rendering across Windows, Linux and macOS.
```sh
# Open page in Chromium
npx playwright open example.com
```
```sh
# Open page in WebKit
npx playwright wk example.com
```
### Emulate devices
`open` can emulate mobile and tablet devices ([see all devices](https://github.com/microsoft/playwright/blob/master/src/server/deviceDescriptors.ts)).
```sh
# Emulate iPhone 11.
npx playwright --device="iPhone 11" open wikipedia.org
```
### Emulate color scheme and viewport size
```sh
# Emulate screen size and color scheme.
npx playwright --viewport-size=800,600 --color-scheme=dark open twitter.com
```
### Emulate geolocation, language and timezone
```sh
# Emulate timezone, language & location
# Once page opens, click the "my location" button to see geolocation in action
npx playwright --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --lang="it-IT" open maps.google.com
```
## Inspect selectors
During `open` or `codegen`, you can use following API inside the developer tools console of any browser.
<img src="https://user-images.githubusercontent.com/284612/92536317-37dd9380-f1ee-11ea-875d-daf1b206dd56.png">
#### playwright.$(selector)
Query Playwright selector, using the actual Playwright query engine, for example:
```js
> playwright.$('.auth-form >> text=Log in');
<button>Log in</button>
```
#### playwright.$$(selector)
Same as `playwright.$`, but returns all matching elements.
```js
> playwright.$$('li >> text=John')
> [<li>, <li>, <li>, <li>]
```
#### playwright.inspect(selector)
Reveal element in the Elements panel (if DevTools of the respective browser supports it).
```js
> playwright.inspect('text=Log in')
```
#### playwright.selector(element)
Generates selector for the given element.
```js
> playwright.selector($0)
"div[id="glow-ingress-block"] >> text=/.*Hello.*/"
```
## Take screenshot
```sh
# See command help
$ npx playwright screenshot --help
```
```sh
# Wait 3 seconds before capturing a screenshot after page loads ('load' event fires)
npx playwright \
--device="iPhone 11" \
--color-scheme=dark \
screenshot \
--wait-for-timeout=3000 \
twitter.com twitter-iphone.png
```
```sh
# Capture a full page screenshot
npx playwright screenshot --full-page en.wikipedia.org wiki-full.png
```
## Generate PDF
PDF generation only works in Headless Chromium.
```sh
# See command help
$ npx playwright pdf https://en.wikipedia.org/wiki/PDF wiki.pdf
```
## Known limitations
Opening WebKit Web Inspector will disconnect Playwright from the browser. In such cases, code generation will stop.

350
docs-src/core-concepts.md Normal file
View file

@ -0,0 +1,350 @@
# Core concepts
Playwright provides a set of APIs to automate Chromium, Firefox and WebKit
browsers. By using the Playwright API, you can write JavaScript code to create
new browser pages, navigate to URLs and then interact with elements on a page.
Along with a test runner Playwright can be used to automate user interactions to
validate and test web applications. The Playwright API enables this through
the following primitives.
<!-- GEN:toc-top-level -->
<!-- GEN:stop -->
<br/>
## Browser
A [Browser] refers to an instance of Chromium, Firefox
or WebKit. Playwright scripts generally start with launching a browser instance
and end with closing the browser. Browser instances can be launched in headless
(without a GUI) or headful mode.
```js
const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'.
const browser = await chromium.launch({ headless: false });
await browser.close();
```
Launching a browser instance can be expensive, and Playwright is designed to
maximize what a single instance can do through multiple browser contexts.
#### API reference
- [Browser]
<br/>
## Browser contexts
A [BrowserContext] is an isolated incognito-alike
session within a browser instance. Browser contexts are fast and cheap to create.
Browser contexts can be used to parallelize isolated test executions.
```js
const browser = await chromium.launch();
const context = await browser.newContext();
```
Browser contexts can also be used to emulate multi-page scenarios involving
mobile devices, permissions, locale and color scheme.
```js
const { devices } = require('playwright');
const iPhone = devices['iPhone 11 Pro'];
const context = await browser.newContext({
...iPhone,
permissions: ['geolocation'],
geolocation: { latitude: 52.52, longitude: 13.39},
colorScheme: 'dark',
locale: 'de-DE'
});
```
#### API reference
- [BrowserContext]
- [`method: Browser.newContext`]
<br/>
## Pages and frames
A Browser context can have multiple pages. A [Page]
refers to a single tab or a popup window within a browser context. It should be used to navigate to URLs and interact with the page content.
```js
// Create a page.
const page = await context.newPage();
// Navigate explicitly, similar to entering a URL in the browser.
await page.goto('http://example.com');
// Fill an input.
await page.fill('#search', 'query');
// Navigate implicitly by clicking a link.
await page.click('#submit');
// Expect a new url.
console.log(page.url());
// Page can navigate from the script - this will be picked up by Playwright.
window.location.href = 'https://example.com';
```
> Read more on [page navigation and loading](./navigations.md).
A page can have one or more [Frame] objects attached to
it. Each page has a main frame and page-level interactions (like `click`) are
assumed to operate in the main frame.
A page can have additional frames attached with the `iframe` HTML tag. These
frames can be accessed for interactions inside the frame.
```js
// Get frame using the frame's name attribute
const frame = page.frame('frame-login');
// Get frame using frame's URL
const frame = page.frame({ url: /.*domain.*/ });
// Get frame using any other selector
const frameElementHandle = await page.$('.frame-class');
const frame = await frameElementHandle.contentFrame();
// Interact with the frame
await frame.fill('#username-input', 'John');
```
#### API reference
- [Page]
- [Frame]
- [`method: Page.frame`]
<br/>
## Selectors
Playwright can search for elements using CSS selectors, XPath selectors, HTML attributes like `id`, `data-test-id` and even text content.
You can explicitly specify the selector engine you are using or let Playwright detect it.
All selector engines except for XPath pierce shadow DOM by default. If you want to enforce regular DOM selection, you can use the `*:light` versions of the selectors. You don't typically need to though.
Learn more about selectors and selector engines [here](./selectors.md).
Some examples below:
```js
// Using data-test-id= selector engine
await page.click('data-test-id=foo');
```
```js
// CSS and XPath selector engines are automatically detected
await page.click('div');
await page.click('//html/body/div');
```
```js
// Find node by text substring
await page.click('text=Hello w');
```
```js
// Explicit CSS and XPath notation
await page.click('css=div');
await page.click('xpath=//html/body/div');
```
```js
// Only search light DOM, outside WebComponent shadow DOM:
await page.click('css:light=div');
```
Selectors using the same or different engines can be combined using the `>>` separator. For example,
```js
// Click an element with text 'Sign Up' inside of a #free-month-promo.
await page.click('#free-month-promo >> text=Sign Up');
```
```js
// Capture textContent of a section that contains an element with text 'Selectors'.
const sectionText = await page.$eval('*css=section >> text=Selectors', e => e.textContent);
```
<br/>
## Auto-waiting
Actions like `click` and `fill` auto-wait for the element to be visible and [actionable](./actionability.md). For example, click will:
- wait for an element with the given selector to appear in the DOM
- wait for it to become visible: have non-empty bounding box and no `visibility:hidden`
- wait for it to stop moving: for example, wait until css transition finishes
- scroll the element into view
- wait for it to receive pointer events at the action point: for example, wait until element becomes non-obscured by other elements
- retry if the element is detached during any of the above checks
```js
// Playwright waits for #search element to be in the DOM
await page.fill('#search', 'query');
```
```js
// Playwright waits for element to stop animating
// and accept clicks.
await page.click('#search');
```
You can explicitly wait for an element to appear in the DOM or to become visible:
```js
// Wait for #search to appear in the DOM.
await page.waitForSelector('#search', { state: 'attached' });
// Wait for #promo to become visible, for example with `visibility:visible`.
await page.waitForSelector('#promo');
```
... or to become hidden or detached
```js
// Wait for #details to become hidden, for example with `display:none`.
await page.waitForSelector('#details', { state: 'hidden' });
// Wait for #promo to be removed from the DOM.
await page.waitForSelector('#promo', { state: 'detached' });
```
#### API reference
- [`method: Page.click`]
- [`method: Page.fill`]
- [`method: Page.waitForSelector`]
<br/>
## Execution contexts: Node.js and Browser
Playwright scripts run in your Node.js environment. Your page scripts run in the browser page environment. Those environments don't intersect, they are running in different virtual machines in different processes and even potentially on different computers.
The [`method: Page.evaluate`] API can run a JavaScript function in the context
of the web page and bring results back to the Node.js environment. Browser globals like
`window` and `document` can be used in `evaluate`.
```js
const href = await page.evaluate(() => document.location.href);
```
If the result is a Promise or if the function is asynchronous evaluate will automatically wait until it's resolved:
```js
const status = await page.evaluate(async () => {
const response = await fetch(location.href);
return response.status;
});
```
### Evaluation
Functions passed inside [`method: Page.evaluate`] can accept parameters. These parameters are
serialized and sent into your web page over the wire. You can pass primitive types, JSON-alike objects and remote object handles received from the page.
Right:
```js
const data = { text: 'some data', value: 1 };
// Pass |data| as a parameter.
const result = await page.evaluate(data => {
window.myApp.use(data);
}, data);
```
Wrong:
```js
const data = { text: 'some data', value: 1 };
const result = await page.evaluate(() => {
// There is no |data| in the web page.
window.myApp.use(data);
});
```
#### API reference
- [`method: Page.evaluate`]
- [`method: Frame.evaluate`]
- [EvaluationArgument]
<br/>
## Object & Element handles
Playwright can create Node-side handles to the page DOM elements or any other objects inside the page. These handles live in the Node.js process, whereas the actual objects reside in browser.
There are two types of handles:
- [JSHandle] to reference any JavaScript objects in the page
- [ElementHandle] to reference DOM elements in the page
Note that since any DOM element in the page is also a JavaScript object,
Playwright's [ElementHandle] extends [JSHandle].
### Handles Lifecycle
- Handles can be acquired using the page methods [`method: Page.evaluateHandle`], [`method: Page.$`] or [`method: Page.$$`] or
their frame counterparts [`method: Frame.evaluateHandle`], [`method: Frame.$`] or [`method: Frame.$$`].
- Once created, handles will retain object from [garbage collection](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management).
- Handles will be **automatically disposed** once the page or frame they belong to navigates or closes.
- Handles can be **manually disposed** using [`method: JSHandle.dispose`] method.
### Example: ElementHandle
```js
// The first parameter of the elementHandle.evaluate callback is the element handle points to.
const ulElementHandle = await page.$('ul');
await ulElementHandle.evaluate(ulElement => getComputedStyle(ulElement).getPropertyValue('display'));
```
Handles can also be passed as arguments to [`method: Page.evaluate`] function:
```js
// In the page API, you can pass handle as a parameter.
const ulElementHandle = await page.$('ul');
await page.evaluate(uiElement => getComputedStyle(uiElement).getPropertyValue('display'), uiElement);
```
### Example: JSHandle
```js
// Create a new array in the page, write a reference to it in
// window.myArray and get a handle to it.
const myArrayHandle = await page.evaluateHandle(() => {
window.myArray = [1];
return myArray;
});
// Get current length of the array using the handle.
const length = await page.evaluate(
(arg) => arg.myArray.length,
{ myArray: myArrayHandle }
);
// Add one more element to the array using the handle
await page.evaluate((arg) => arg.myArray.push(arg.newElement), {
myArray: myArrayHandle,
newElement: 2
});
// Get current length of the array using window.myArray reference.
const newLength = await page.evaluate(() => window.myArray.length);
// Release the object when it's no longer needed.
await myArrayHandle.dispose();
```
#### API reference
- [JSHandle]
- [ElementHandle]
- [`method: Page.evaluateHandle`]
- [`method: Page.$`]
- [`method: Page.$$`]
- [`method: JSHandle.evaluate`]

126
docs-src/debug.md Normal file
View file

@ -0,0 +1,126 @@
# Debugging tools
Playwright scripts work with existing debugging tools, like Node.js debuggers
and browser developer tools. Playwright also introduces new debugging features
for browser automation.
<!-- GEN:toc -->
<!-- GEN:stop -->
## Run in headful mode
Playwright runs browsers in headless mode by default. To change this behavior,
use `headless: false` as a launch option. You can also use the `slowMo` option
to slow down execution and follow along while debugging.
```js
await chromium.launch({ headless: false, slowMo: 100 }); // or firefox, webkit
```
## Visual Studio Code debugger
The VS Code debugger can be used to pause and resume execution of Playwright
scripts with breakpoints. The debugger can be configured in two ways.
### Use launch config
Setup [`launch.json` configuration](https://code.visualstudio.com/docs/nodejs/nodejs-debugging)
for your Node.js project. Once configured launch the scripts with F5 and use
breakpoints.
### Use the new JavaScript debugger
VS Code 1.46+ introduces the new JavaScript debugger behind a feature flag. The
new debugger does not require a `launch.json` configuration. To use this:
1. Enable the preview debugger
* Open JSON settings and add `"debug.javascript.usePreview": true`
* Open settings UI and enable the `Debug JavaScript: Use Preview` setting
1. Set a breakpoint in VS Code
* Use the `debugger` keyword or set a breakpoint in the VS Code UI
1. Run your Node.js script from the terminal
## Browser Developer Tools
You can use browser developer tools in Chromium, Firefox and WebKit while running
a Playwright script. Developer tools help to:
* Inspect the DOM tree and **find element selectors**
* **See console logs** during execution (or learn how to [read logs via API](./verification.md#console-logs))
* Check **network activity** and other developer tools features
<a href="https://user-images.githubusercontent.com/284612/77234134-5f21a500-6b69-11ea-92ec-1c146e1333ec.png"><img src="https://user-images.githubusercontent.com/284612/77234134-5f21a500-6b69-11ea-92ec-1c146e1333ec.png" width="500" alt="Chromium Developer Tools"></a>
> **For WebKit**: Note that launching WebKit Inspector during the execution will
prevent the Playwright script from executing any further.
### API for Chromium
In Chromium, you can also open developer tools through a launch option.
```js
await chromium.launch({ devtools: true });
```
## Run in Debug Mode
Set the `PWDEBUG` environment variable to run your scripts in debug mode. This
configures the browser for debugging.
```sh
# Linux/macOS
$ PWDEBUG=1 npm run test
# Windows
$ set PWDEBUG=1
$ npm run test
```
### Defaults
With PWDEBUG, the following defaults are configured for you:
* **Run in headful**: With PWDEBUG, browsers always launch in headful mode
* **Disables timeout**: PWDEBUG sets timeout to 0 (= no timeout)
* **Preserve DevTools preferences**: When used with `devtools: true`, PWDEBUG
preserves the docked/undocked state of Chrome DevTools
### Debugging Selectors
PWDEBUG configures a `playwright` object in the browser to highlight
[Playwright selectors](./selectors.md). This can be used to verify text or
composite selectors. To use this:
1. Setup a breakpoint to pause the execution
1. Open the console panel in browser developer tools
1. Use the `playwright` API
* `playwright.$(selector)`: Highlight the first occurrence of the selector. This reflects
how `page.$` would see the page.
* `playwright.$$(selector)`: Highlight all occurrences of the selector. This reflects
how `page.$$` would see the page.
* `playwright.inspect(selector)`: Inspect the selector in the Elements panel.
* `playwright.clear()`: Clear existing highlights.
<a href="https://user-images.githubusercontent.com/284612/86857345-299abc00-c073-11ea-9e31-02923a9f0d4b.png"><img src="https://user-images.githubusercontent.com/284612/86857345-299abc00-c073-11ea-9e31-02923a9f0d4b.png" width="500" alt="Highlight selectors"></a>
### Evaluate Source Maps
PWDEBUG also enables source maps for [`page.evaluate` executions](core-concepts.md#evaluation).
This improves the debugging experience for JavaScript executions in the page context.
<a href="https://user-images.githubusercontent.com/284612/86857568-a6c63100-c073-11ea-82a4-bfd531a4ec87.png"><img src="https://user-images.githubusercontent.com/284612/86857568-a6c63100-c073-11ea-82a4-bfd531a4ec87.png" width="500" alt="Highlight selectors"></a>
## Verbose API logs
Playwright supports verbose logging with the `DEBUG` environment variable.
```sh
# Linux/macOS
$ DEBUG=pw:api npm run test
# Windows
$ set DEBUG=pw:api
$ npm run test
```

179
docs-src/emulation.md Normal file
View file

@ -0,0 +1,179 @@
# Device and environment emulation
Playwright allows overriding various parameters of the device where the browser is running:
- viewport size, device scale factor, touch support
- locale, timezone
- color scheme
- geolocation
Most of these parameters are configured during the browser context construction, but some of them such as viewport size can be changed for individual pages.
<!-- GEN:toc-top-level -->
<!-- GEN:stop -->
<br/>
## Devices
Playwright comes with a registry of device parameters for selected mobile devices. It can be used to simulate browser behavior on a mobile device:
```js
const { chromium, devices } = require('playwright');
const browser = await chromium.launch();
const pixel2 = devices['Pixel 2'];
const context = await browser.newContext({
...pixel2,
});
```
All pages created in the context above will share the same device parameters.
#### API reference
- [`property: Playwright.devices`]
- [`method: Browser.newContext`]
<br/>
## User agent
All pages created in the context above will share the user agent specified:
```js
const context = await browser.newContext({
userAgent: 'My user agent'
});
```
#### API reference
- [`method: Browser.newContext`]
<br/>
## Viewport
Create a context with custom viewport size:
```js
// Create context with given viewport
const context = await browser.newContext({
viewport: { width: 1280, height: 1024 }
});
// Resize viewport for individual page
await page.setViewportSize({ width: 1600, height: 1200 });
// Emulate high-DPI
const context = await browser.newContext({
viewport: { width: 2560, height: 1440 },
deviceScaleFactor: 2,
});
```
#### API reference
- [`method: Browser.newContext`]
- [`method: Page.setViewportSize`]
<br/>
## Locale & timezone
```js
// Emulate locale and time
const context = await browser.newContext({
locale: 'de-DE',
timezoneId: 'Europe/Berlin',
});
```
#### API reference
- [`method: Browser.newContext`]
<br/>
## Permissions
Allow all pages in the context to show system notifications:
```js
const context = await browser.newContext({
permissions: ['notifications'],
});
```
Grant all pages in the existing context access to current location:
```js
await context.grantPermissions(['geolocation']);
```
Grant notifications access from a specific domain:
```js
await context.grantPermissions(['notifications'], {origin: 'https://skype.com'} );
```
Revoke all permissions:
```js
await context.clearPermissions();
```
#### API reference
- [`method: Browser.newContext`]
- [`method: BrowserContext.grantPermissions`]
- [`method: BrowserContext.clearPermissions`]
<br/>
## Geolocation
Create a context with `"geolocation"` permissions granted:
```js
const context = await browser.newContext({
geolocation: { longitude: 48.858455, latitude: 2.294474 },
permissions: ['geolocation']
});
```
Change the location later:
```js
await context.setGeolocation({ longitude: 29.979097, latitude: 31.134256 });
```
**Note** you can only change geolocation for all pages in the context.
#### API reference
- [`method: Browser.newContext`]
- [`method: BrowserContext.setGeolocation`]
<br/>
## Color scheme and media
Create a context with dark or light mode. Pages created in this context will
follow this color scheme preference.
```js
// Create context with dark mode
const context = await browser.newContext({
colorScheme: 'dark' // or 'light'
});
// Create page with dark mode
const page = await browser.newPage({
colorScheme: 'dark' // or 'light'
});
// Change color scheme for the page
await page.emulateMedia({ colorScheme: 'dark' });
// Change media for page
await page.emulateMedia({ media: 'print' });
```
#### API reference
- [`method: Browser.newContext`]
- [`method: Page.emulateMedia`]

44
docs-src/extensibility.md Normal file
View file

@ -0,0 +1,44 @@
# Extensibility
<!-- GEN:toc-top-level -->
<!-- GEN:stop -->
## Custom selector engines
Playwright supports custom selector engines, registered with [`method: Selectors.register`].
Selector engine should have the following properties:
- `create` function to create a relative selector from `root` (root is either a `Document`, `ShadowRoot` or `Element`) to a `target` element.
- `query` function to query first element matching `selector` relative to the `root`.
- `queryAll` function to query all elements matching `selector` relative to the `root`.
By default the engine is run directly in the frame's JavaScript context and, for example, can call an application-defined function. To isolate the engine from any JavaScript in the frame, but leave access to the DOM, register the engine with `{contentScript: true}` option. Content script engine is safer because it is protected from any tampering with the global objects, for example altering `Node.prototype` methods. All built-in selector engines run as content scripts. Note that running as a content script is not guaranteed when the engine is used together with other custom engines.
An example of registering selector engine that queries elements based on a tag name:
```js
// Must be a function that evaluates to a selector engine instance.
const createTagNameEngine = () => ({
// Returns the first element matching given selector in the root's subtree.
query(root, selector) {
return root.querySelector(selector);
},
// Returns all elements matching given selector in the root's subtree.
queryAll(root, selector) {
return Array.from(root.querySelectorAll(selector));
}
});
// Register the engine. Selectors will be prefixed with "tag=".
await selectors.register('tag', createTagNameEngine);
// Now we can use 'tag=' selectors.
const button = await page.$('tag=button');
// We can combine it with other selector engines using `>>` combinator.
await page.click('tag=div >> span >> "Click me"');
// We can use it in any methods supporting selectors.
const buttonCount = await page.$$eval('tag=button', buttons => buttons.length);
```

277
docs-src/input.md Normal file
View file

@ -0,0 +1,277 @@
# Input
<!-- GEN:toc-top-level -->
<!-- GEN:stop -->
<br/>
## Text input
This is the easiest way to fill out the form fields. It focuses the element and triggers an `input` event with the entered text. It works for `<input>`, `<textarea>`, `[contenteditable]` and `<label>` associated with an input or textarea.
```js
// Text input
await page.fill('#name', 'Peter');
// Date input
await page.fill('#date', '2020-02-02');
// Time input
await page.fill('#time', '13-15');
// Local datetime input
await page.fill('#local', '2020-03-02T05:15');
// Input through label
await page.fill('text=First Name', 'Peter');
```
#### API reference
- [`method: Page.fill`]
- [`method: Frame.fill`]
- [`method: ElementHandle.fill`]
<br/>
## Checkboxes and radio buttons
This is the easiest way to check and uncheck a checkbox or a radio button. This method can be used with `input[type=checkbox]`, `input[type=radio]`, `[role=checkbox]` or `label` associated with checkbox or radio button.
```js
// Check the checkbox
await page.check('#agree');
// Uncheck by input <label>.
await page.uncheck('#subscribe-label');
// Select the radio button
await page.check('text=XL');
```
#### API reference
- [`method: Page.check`]
- [`method: Page.uncheck`]
- [`method: Frame.check`]
- [`method: Frame.uncheck`]
- [`method: ElementHandle.check`]
- [`method: ElementHandle.uncheck`]
<br/>
## Select options
Selects one or multiple options in the `<select>` element.
You can specify option `value`, `label` or `elementHandle` to select. Multiple options can be selected.
```js
// Single selection matching the value
await page.selectOption('select#colors', 'blue');
// Single selection matching the label
await page.selectOption('select#colors', { label: 'Blue' });
// Multiple selected items
await page.selectOption('select#colors', ['red', 'green', 'blue']);
// Select the option via element handle
const option = await page.$('#best-option');
await page.selectOption('select#colors', option);
```
#### API reference
- [`method: Page.selectOption`]
- [`method: Frame.selectOption`]
- [`method: ElementHandle.selectOption`]
<br/>
## Mouse click
Performs a simple human click.
```js
// Generic click
await page.click('button#submit');
// Double click
await page.dblclick('#item');
// Right click
await page.click('#item', { button: 'right' });
// Shift + click
await page.click('#item', { modifiers: ['Shift'] });
// Hover over element
await page.hover('#item');
// Click the top left corner
await page.click('#item', { position: { x: 0, y: 0} });
```
Under the hood, this and other pointer-related methods:
- wait for element with given selector to be in DOM
- wait for it to become displayed, i.e. not empty, no `display:none`, no `visibility:hidden`
- wait for it to stop moving, for example, until css transition finishes
- scroll the element into view
- wait for it to receive pointer events at the action point, for example, waits until element becomes non-obscured by other elements
- retry if the element is detached during any of the above checks
#### Forcing the click
Sometimes, apps use non-trivial logic where hovering the element overlays it with another element that intercepts the click. This behavior is indistinguishable from a bug where element gets covered and the click is dispatched elsewhere. If you know this is taking place, you can bypass the [actionability](./actionability.md) checks and force the click:
```js
await page.click('button#submit', { force: true });
```
#### Programmatic click
If you are not interested in testing your app under the real conditions and want to simulate the click by any means possible, you can trigger the [`HTMLElement.click()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click) behavior via simply dispatching a click event on the element:
```js
await page.dispatchEvent('button#submit', 'click');
```
#### API reference
- [`method: Page.click`]
- [`method: Frame.click`]
- [`method: ElementHandle.click`]
- [`method: Page.dblclick`]
- [`method: Frame.dblclick`]
- [`method: ElementHandle.dblclick`]
- [`method: Page.hover`]
- [`method: Frame.hover`]
- [`method: ElementHandle.hover`]
- [`method: Page.dispatchEvent`]
- [`method: Frame.dispatchEvent`]
- [`method: ElementHandle.dispatchEvent`]
<br/>
## Type characters
Type into the field character by character, as if it was a user with a real keyboard.
```js
// Type character by character
await page.type('#area', 'Hello World!');
```
This method will emit all the necessary keyboard events, with all the `keydown`, `keyup`, `keypress` events in place. You can even specify the optional `delay` between the key presses to simulate real user behavior.
> **NOTE** that most of the time, [`page.fill`](#text-input) will just work. You only need to type characters if there is special keyboard handling on the page.
#### API reference
- [`method: Page.type`]
- [`method: Frame.type`]
- [`method: ElementHandle.type`]
- [`method: Keyboard.type`]
<br/>
## Keys and shortcuts
```js
// Hit Enter
await page.press('#submit', 'Enter');
// Dispatch Control+Right
await page.press('#name', 'Control+ArrowRight');
// Press $ sign on keyboard
await page.press('#value', '$');
```
This method focuses the selected element and produces a single keystroke. It accepts the logical key names that are emitted in the [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) property of the keyboard events:
```
Backquote, Minus, Equal, Backslash, Backspace, Tab, Delete, Escape,
ArrowDown, End, Enter, Home, Insert, PageDown, PageUp, ArrowRight,
ArrowUp, F1 - F12, Digit0 - Digit9, KeyA - KeyZ, etc.
```
- You can alternatively specify a single character you'd like to produce such as `"a"` or `"#"`.
- Following modification shortcuts are also supported: `Shift, Control, Alt, Meta`.
Simple version produces a single character. This character is case-sensitive, so `"a"` and `"A"` will produce different results.
```js
// <input id=name>
await page.press('#name', 'Shift+A');
// <input id=name>
await page.press('#name', 'Shift+ArrowLeft');
```
Shortcuts such as `"Control+o"` or `"Control+Shift+T"` are supported as well. When specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.
Note that you still need to specify the capital `A` in `Shift-A` to produce the capital character. `Shift-a` produces a lower-case one as if you had the `CapsLock` toggled.
#### API reference
- [`method: Page.press`]
- [`method: Frame.press`]
- [`method: ElementHandle.press`]
- [`method: Keyboard.press`]
<br/>
## Upload files
```js
// Select one file
await page.setInputFiles('input#upload', 'myfile.pdf');
// Select multiple files
await page.setInputFiles('input#upload', ['file1.txt', 'file2.txt']);
// Remove all the selected files
await page.setInputFiles('input#upload', []);
// Upload buffer from memory
await page.setInputFiles('input#upload', {
name: 'file.txt',
mimeType: 'text/plain',
buffer: Buffer.from('this is test')
});
```
You can select input files for upload using the `page.setInputFiles` method. It expects first argument to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) with the type `"file"`. Multiple files can be passed in the array. If some of the file paths are relative, they are resolved relative to the [current working directory](https://nodejs.org/api/process.html#process_process_cwd). Empty array clears the selected files.
#### Example
[This script](examples/upload.js) uploads a file to an `input` element that accepts file uploads.
#### API reference
- [`method: Page.setInputFiles`]
- [`method: Frame.setInputFiles`]
- [`method: ElementHandle.setInputFiles`]
<br/>
## Focus element
For the dynamic pages that handle focus events, you can focus the given element.
```js
await page.focus('input#name');
```
#### API reference
- [`method: Page.focus`]
- [`method: Frame.focus`]
- [`method: ElementHandle.focus`]
<br/>

142
docs-src/installation.md Normal file
View file

@ -0,0 +1,142 @@
# Advanced installation
<!-- GEN:toc -->
<!-- GEN:stop -->
<br>
## Managing browser binaries
Each version of Playwright needs specific versions of browser binaries to operate. By default Playwright downloads Chromium, WebKit and Firefox browsers into the OS-specific cache folders:
- `%USERPROFILE%\AppData\Local\ms-playwright` on Windows
- `~/Library/Caches/ms-playwright` on MacOS
- `~/.cache/ms-playwright` on Linux
```sh
npm i -D playwright
```
These browsers will take few hundreds of megabytes of the disk space when installed:
```sh
du -hs ./Library/Caches/ms-playwright/*
281M chromium-XXXXXX
187M firefox-XXXX
180M webkit-XXXX
```
You can override default behavior using environment variables. When installing Playwright, ask it to download browsers into a specific location:
```sh
# Linux/macOS
$ PLAYWRIGHT_BROWSERS_PATH=$HOME/pw-browsers npm i -D playwright
# Windows
$ set PLAYWRIGHT_BROWSERS_PATH=%USERPROFILE%\pw-browsers
$ npm i -D playwright
```
When running Playwright scripts, ask it to search for browsers in a shared location:
```sh
# Linux/macOS
$ PLAYWRIGHT_BROWSERS_PATH=$HOME/pw-browsers node playwright-script.js
# Windows
$ set PLAYWRIGHT_BROWSERS_PATH=%USERPROFILE%\pw-browsers
$ node playwright-script.js
```
Or you can opt into the hermetic install and place binaries under the `node_modules/` folder:
```sh
# Linux/macOS
$ PLAYWRIGHT_BROWSERS_PATH=0 npm i -D playwright
# Windows
$ set PLAYWRIGHT_BROWSERS_PATH=0
$ npm i -D playwright
```
Playwright keeps track of packages that need those browsers and will garbage collect them as you update Playwright to the newer versions.
<br>
> **NOTE** Developers can opt-in in this mode via exporting `PLAYWRIGHT_BROWSERS_PATH=$HOME/pw-browsers` in their `.bashrc`.
<br>
## Download from artifact repository
By default, Playwright downloads browsers from Microsoft and Google public CDNs.
Sometimes companies maintain an internal artifact repository to host browser
binaries. In this case, Playwright can be configured to download from a custom
location using the `PLAYWRIGHT_DOWNLOAD_HOST` env variable.
```sh
# Linux/macOS
$ PLAYWRIGHT_DOWNLOAD_HOST=192.168.1.78 npm i -D playwright
# Windows
$ set PLAYWRIGHT_DOWNLOAD_HOST=192.168.1.78
$ npm i -D playwright
```
It is also possible to use a per-browser download hosts using `PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST`, `PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST` and `PLAYWRIGHT_WEBKIT_DOWNLOAD_HOST` env variables that
take precedence over `PLAYWRIGHT_DOWNLOAD_HOST`.
```sh
# Linux/macOS
$ PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=192.168.1.1 PLAYWRIGHT_DOWNLOAD_HOST=192.168.1.78 npm i -D playwright
```
<br>
## Skip browser downloads
In certain cases, it is desired to avoid browser downloads altogether because
browser binaries are managed separately.
This can be done by setting `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` variable before installation.
```sh
# Linux/macOS
$ PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 npm i -D playwright
# Windows
$ set PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
$ npm i -D playwright
```
<br>
## Download single browser binary
Playwright ships three packages that bundle only a single browser:
- [`playwright-chromium`](https://www.npmjs.com/package/playwright-chromium)
- [`playwright-webkit`](https://www.npmjs.com/package/playwright-webkit)
- [`playwright-firefox`](https://www.npmjs.com/package/playwright-firefox)
> **NOTE** All configuration environment variables also apply to these packages.
Using these packages is as easy as using a regular Playwright:
Install a specific package
```sh
$ npm i -D playwright-webkit
```
Require package
```js
// Notice a proper package name in require
const { webkit } = require('playwright-webkit');
(async () => {
const browser = await webkit.launch();
// ...
})();
```

106
docs-src/intro.md Normal file
View file

@ -0,0 +1,106 @@
# Getting Started
<!-- GEN:toc-top-level -->
<!-- GEN:stop -->
## Installation
Use npm or Yarn to install Playwright in your Node.js project. See [system requirements](#system-requirements).
```sh
npm i -D playwright
```
This single command downloads the Playwright NPM package and browser binaries for Chromium, Firefox and WebKit. To modify this behavior see [installation parameters](./installation.md).
## Usage
Once installed, you can `require` Playwright in a Node.js script, and launch any of the 3 browsers (`chromium`, `firefox` and `webkit`).
```js
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
// Create pages, interact with UI elements, assert values
await browser.close();
})();
```
Playwright APIs are asynchronous and return Promise objects. Our code examples use [the async/await pattern](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await) to ease readability. The code is wrapped in an unnamed async arrow function which is invoking itself.
```js
(async () => { // Start of async arrow function
// Function code
// ...
})(); // End of the function and () to invoke itself
```
## First script
In our first script, we will navigate to `whatsmyuseragent.org` and take a screenshot in WebKit.
```js
const { webkit } = require('playwright');
(async () => {
const browser = await webkit.launch();
const page = await browser.newPage();
await page.goto('http://whatsmyuseragent.org/');
await page.screenshot({ path: `example.png` });
await browser.close();
})();
```
By default, Playwright runs the browsers in headless mode. To see the browser UI, pass the `headless: false` flag while launching the browser. You can also use `slowMo` to slow down execution.
```js
firefox.launch({ headless: false, slowMo: 50 });
```
## Record scripts
[Playwright CLI](./cli.md) can be used to record user interactions and generate JavaScript code.
```sh
npx playwright codegen wikipedia.org
```
## TypeScript support
Playwright includes built-in support for TypeScript. Type definitions will be imported automatically. It is recommended to use type-checking to improve the IDE experience.
### In JavaScript
Add the following to the top of your JavaScript file to get type-checking in VS Code or WebStorm.
```js
//@ts-check
// ...
```
Alternatively, you can use JSDoc to set types for variables.
```js
/** @type {import('playwright').Page} */
let page;
```
### In TypeScript
TypeScript support will work out-of-the-box. Types can also be imported explicitly.
```ts
let page: import('playwright').Page;
```
## System requirements
Playwright requires Node.js version 10.17 or above. The browser binaries for Chromium,
Firefox and WebKit work across the 3 platforms (Windows, macOS, Linux):
* **Windows**: Works with Windows and Windows Subsystem for Linux (WSL).
* **macOS**: Requires 10.14 or above.
* **Linux**: Depending on your Linux distribution, you might need to install additional
dependencies to run the browsers.
* Firefox requires Ubuntu 18.04+
* For Ubuntu 18.04, the additional dependencies are defined in [our Docker image](docker/Dockerfile.bionic),
which is based on Ubuntu.

51
docs-src/languages.md Normal file
View file

@ -0,0 +1,51 @@
# Supported languages
The Playwright API is available in multiple languages.
<!-- GEN:toc -->
<!-- GEN:stop -->
## JavaScript and TypeScript
Playwright for JavaScript and TypeScript is generally available.
```
npm i -D playwright
```
* [Playwright on NPM](https://www.npmjs.com/package/playwright)
* [GitHub repo](https://github.com/microsoft/playwright)
## Python
Playwright for Python is available in preview.
```
pip install playwright
```
* [Playwright on PyPI](https://pypi.org/project/playwright/)
* [GitHub repo](https://github.com/microsoft/playwright-python)
* [Pytest integration](https://github.com/microsoft/playwright-pytest)
## C#
Playwright for C# is available in preview.
```
dotnet add package PlaywrightSharp
```
* [Playwright on NuGet](https://www.nuget.org/packages/PlaywrightSharp/)
* [GitHub repo](https://github.com/microsoft/playwright-sharp)
## Go
Playwright for Go is available in preview.
```
go get github.com/mxschmitt/playwright-go
```
* [GitHub repo](https://github.com/mxschmitt/playwright-go)
* [Documentation](https://pkg.go.dev/github.com/mxschmitt/playwright-go?tab=doc)

38
docs-src/mobile.md Normal file
View file

@ -0,0 +1,38 @@
# Experimental support for Chrome for Android
You can try Playwright against Chrome for Android today. This support is experimental. Support for devices is tracked in the issue [#1122](https://github.com/microsoft/playwright/issues/1122).
## Requirements
- [Playwright 1.6](https://www.npmjs.com/package/playwright) or newer
- [ADB daemon](https://developer.android.com/studio/command-line/adb) running and authenticated with your device.
- [`Chrome 87`](https://play.google.com/store/apps/details?id=com.android.chrome) or newer installed on the device
- "Enable command line on non-rooted devices" enabled in `chrome://flags`.
> Playwright will be looking for ADB daemon on the default port `5037`. It will use the first device available. Typically running `adb devices` is all you need to do.
## How to run
```js
const { _clank } = require('playwright');
(async () => {
const context = await _clank.launchPersistentContext('', {
viewport: null
});
const [page] = context.pages();
await page.goto('https://webkit.org/');
console.log(await page.evaluate(() => window.location.href));
await page.screenshot({ path: 'example.png' });
await context.close();
})();
```
> [Clank](https://chromium.googlesource.com/chromium/src/+/master/docs/memory/android_dev_tips.md) is a code name for Chrome for Android.
## Known limitations
- Raw USB operation is not yet supported, so you need ADB.
- Only `launchPersistentContext` works, launching ephemeral contexts is not supported.
- Passing `viewport: null` is necessary to make sure resolution is not emulated.
- Device needs to be awake to produce screenshots. Enabling "Stay awake" developer mode will help.
- We didn't run all the tests against the device, so not everything works.

121
docs-src/multi-pages.md Normal file
View file

@ -0,0 +1,121 @@
# Multi-page scenarios
Playwright can automate scenarios that span multiple browser contexts or multiple
tabs in a browser window.
<!-- GEN:toc-top-level -->
<!-- GEN:stop -->
## Multiple contexts
[Browser contexts](core-concepts.md#browser-contexts) are isolated environments
on a single browser instance. Playwright can create multiple browser contexts
within a single scenario. This is useful when you want to test for
multi-user functionality, like chat.
```js
const { chromium } = require('playwright');
// Create a Chromium browser instance
const browser = await chromium.launch();
// Create two isolated browser contexts
const userContext = await browser.newContext();
const adminContext = await browser.newContext();
// Load user and admin cookies
await userContext.addCookies(userCookies);
await adminContext.addCookies(adminCookies);
```
#### API reference
- [BrowserContext]
- [`method: Browser.newContext`]
- [`method: BrowserContext.addCookies`]
## Multiple pages
Each browser context can host multiple pages (tabs).
* Each page behaves like a focused, active page. Bringing the page to front
is not required.
* Pages inside a context respect context-level emulation, like viewport sizes,
custom network routes or browser locale.
```js
// Create two pages
const pageOne = await context.newPage();
const pageTwo = await context.newPage();
// Get pages of a brower context
const allPages = context.pages();
```
#### API reference
- [Page]
- [`method: BrowserContext.newPage`]
- [`method: BrowserContext.pages`]
## Handling new pages
The `page` event on browser contexts can be used to get new pages that are
created in the context. This can be used to handle new pages opened by
`target="_blank"` links.
```js
// Get page after a specific action (e.g. clicking a link)
const [newPage] = await Promise.all([
context.waitForEvent('page'),
page.click('a[target="_blank"]') // Opens a new tab
])
await newPage.waitForLoadState();
console.log(await newPage.title());
```
If the action that triggers the new page is unknown, the following pattern can be used.
```js
// Get all new pages (including popups) in the context
context.on('page', async page => {
await page.waitForLoadState();
await page.title();
})
```
#### API reference
- [`event: BrowserContext.page`]
## Handling popups
If the page opens a pop-up, you can get a reference to it by listening to the
`popup` event on the page.
This event is emitted in addition to the `browserContext.on('page')` event, but
only for popups relevant to this page.
```js
// Get popup after a specific action (e.g., click)
const [popup] = await Promise.all([
page.waitForEvent('popup'),
page.click('#open')
]);
await popup.waitForLoadState();
await popup.title();
```
If the action that triggers the popup is unknown, the following pattern can be used.
```js
// Get all popups when they open
page.on('popup', async popup => {
await popup.waitForLoadState();
await popup.title();
})
```
#### API reference
- [`event: Page.popup`]

155
docs-src/navigations.md Normal file
View file

@ -0,0 +1,155 @@
# Navigations
Playwright can navigate to URLs and handle navigations caused by page interactions. This guide covers common scenarios to wait for page navigations and loading to complete.
<!-- GEN:toc-top-level -->
<!-- GEN:stop -->
## Navigation lifecycle
Playwright splits the process of showing a new document in a page into **navigation** and **loading**.
**Navigations** can be initiated by changing the page URL or by interacting with the page (e.g., clicking a link). Navigation ends when response headers have been parsed and session history is updated. The navigation intent may be canceled, for example, on hitting an unresolved DNS address or transformed into a file download. Only after the navigation succeeds, page starts **loading** the document.
**Loading** covers getting the remaining response body over the network, parsing, executing the scripts and firing load events:
- [`method: Page.url`] is set to the new url
- document content is loaded over network and parsed
- [`event: Page.domcontentloaded`] event is fired
- page executes some scripts and loads resources like stylesheets and images
- [`event: Page.load`] event is fired
- page executes dynamically loaded scripts
- `networkidle` is fired when no new network requests are made for 500 ms
## Scenarios initiated by browser UI
Navigations can be initiated by changing the URL bar, reloading the page or going back or forward in session history.
### Auto-wait
Navigating to a URL auto-waits for the page to fire the `load` event. If the page does a client-side redirect before `load`, `page.goto` will auto-wait for the redirected page to fire the `load` event.
```js
// Navigate the page
await page.goto('https://example.com');
```
### Custom wait
Override the default behavior to wait until a specific event, like `networkidle`.
```js
// Navigate and wait until network is idle
await page.goto('https://example.com', { waitUntil: 'networkidle' });
```
### Wait for element
In lazy-loaded pages, it can be useful to wait until an element is visible with [`method: Page.waitForSelector`]. Alternatively, page interactions like [`method: Page.click`] auto-wait for elements.
```js
// Navigate and wait for element
await page.goto('https://example.com');
await page.waitForSelector('text=Example Domain');
// Navigate and click element
// Click will auto-wait for the element
await page.goto('https://example.com');
await page.click('text=Example Domain');
```
#### API reference
- [`method: Page.goto`]
- [`method: Page.reload`]
- [`method: Page.goBack`]
- [`method: Page.goForward`]
## Scenarios initiated by page interaction
In the scenarios below, `page.click` initiates a navigation and then waits for the navigation to complete.
### Auto-wait
By default, `page.click` will wait for the navigation step to complete. This can be combined with a page interaction on the navigated page which would auto-wait for an element.
```js
// Click will auto-wait for navigation to complete
await page.click('text=Login');
// Fill will auto-wait for element on navigated page
await page.fill('#username', 'John Doe');
```
### Custom wait
`page.click` can be combined with [`method: Page.waitForLoadState`] to wait for a loading event.
```js
await page.click('button'); // Click triggers navigation
await page.waitForLoadState('networkidle'); // This resolves after 'networkidle'
```
### Wait for element
In lazy-loaded pages, it can be useful to wait until an element is visible with [`method: Page.waitForSelector`]. Alternatively, page interactions like [`method: Page.click`] auto-wait for elements.
```js
// Click triggers navigation
await page.click('text=Login');
// Click will auto-wait for the element
await page.waitForSelector('#username', 'John Doe');
// Click triggers navigation
await page.click('text=Login');
// Fill will auto-wait for element
await page.fill('#username', 'John Doe');
```
### Asynchronous navigation
Clicking an element could trigger asychronous processing before initiating the navigation. In these cases, it is recommended to explicitly call [`method: Page.waitForNavigation`]. For example:
* Navigation is triggered from a `setTimeout`
* Page waits for network requests before navigation
```js
await Promise.all([
page.click('a'), // Triggers a navigation after a timeout
page.waitForNavigation(), // Waits for the next navigation
]);
```
The `Promise.all` pattern prevents a race condition between `page.click` and `page.waitForNavigation` when navigation happens quickly.
### Multiple navigations
Clicking an element could trigger multiple navigations. In these cases, it is recommended to explicitly [`method: Page.waitForNavigation`] to a specific url. For example:
* Client-side redirects issued after the `load` event
* Multiple pushes to history state
```js
await Promise.all([
page.waitForNavigation({ url: '**/login' }),
page.click('a'), // Triggers a navigation with a script redirect
]);
```
The `Promise.all` pattern prevents a race condition between `page.click` and `page.waitForNavigation` when navigation happens quickly.
### Loading a popup
When popup is opened, explicitly calling [`method: Page.waitForLoadState`] ensures that popup is loaded to the desired state.
```js
const [ popup ] = await Promise.all([
page.waitForEvent('popup'),
page.click('a[target="_blank"]'), // Opens popup
]);
await popup.waitForLoadState('load');
```
#### API reference
- [`method: Page.click`]
- [`method: Page.waitForLoadState`]
- [`method: Page.waitForSelector`]
- [`method: Page.waitForNavigation`]
- [`method: Page.waitForFunction`]
## Advanced patterns
For pages that have complicated loading patterns, [`method: Page.waitForFunction`] is a powerful and extensible approach to define a custom wait criteria.
```js
await page.goto('http://example.com');
await page.waitForFunction(() => window.amILoadedYet());
// Ready to take a screenshot, according to the page itself.
await page.screenshot();
```
#### API reference
- [`method: Page.waitForFunction`]

189
docs-src/network.md Normal file
View file

@ -0,0 +1,189 @@
# Network
Playwright provides APIs to **monitor** and **modify** network traffic, both HTTP and HTTPS.
Any requests that page does, including [XHRs](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) and
[fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) requests, can be tracked, modified and handled.
<!-- GEN:toc-top-level -->
<!-- GEN:stop -->
<br/>
## HTTP Authentication
```js
const context = await browser.newContext({
httpCredentials: {
username: 'bill',
password: 'pa55w0rd',
},
});
const page = await context.newPage();
await page.goto('https://example.com');
```
#### API reference
- [`method: Browser.newContext`]
<br/>
## Handle file downloads
```js
const [ download ] = await Promise.all([
page.waitForEvent('download'), // <-- start waiting for the download
page.click('button#delayed-download') // <-- perform the action that directly or indirectly initiates it.
]);
const path = await download.path();
```
For every attachment downloaded by the page, [`event: Page.download`] event is emitted. If you create a browser context with the `acceptDownloads: true`, all these attachments are going to be downloaded into a temporary folder. You can obtain the download url, file system path and payload stream using the [Download] object from the event.
#### Variations
If you have no idea what initiates the download, you can still handle the event:
```js
page.on('download', download => download.path().then(console.log));
```
Note that handling the event forks the control flow and makes script harder to follow. Your scenario might end while you are downloading a file since your main control flow is not awaiting for this operation to resolve.
#### API reference
- [Download]
- [`event: Page.download`]
- [`method: Page.waitForEvent`]
<br/>
## Network events
You can monitor all the requests and responses:
```js
const { chromium, webkit, firefox } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
// Subscribe to 'request' and 'response' events.
page.on('request', request =>
console.log('>>', request.method(), request.url()));
page.on('response', response =>
console.log('<<', response.status(), response.url()));
await page.goto('https://example.com');
await browser.close();
})();
```
Or wait for a network response after the button click:
```js
// Use a glob URL pattern
const [response] = await Promise.all([
page.waitForResponse('**/api/fetch_data'),
page.click('button#update'),
]);
```
#### Variations
```js
// Use a RegExp
const [response] = await Promise.all([
page.waitForResponse(/\.jpeg$/),
page.click('button#update'),
]);
// Use a predicate taking a Response object
const [response] = await Promise.all([
page.waitForResponse(response => response.url().includes(token)),
page.click('button#update'),
]);
```
#### API reference
- [Request]
- [Response]
- [`event: Page.request`]
- [`event: Page.response`]
- [`method: Page.waitForRequest`]
- [`method: Page.waitForResponse`]
<br/>
## Handle requests
```js
await page.route('**/api/fetch_data', route => route.fulfill({
status: 200,
body: testData,
}));
await page.goto('https://example.com');
```
You can mock API endpoints via handling the network quests in your Playwright script.
#### Variations
```js
// Set up route on the entire browser context.
// It will apply to popup windows and opened links.
await browserContext.route('**/api/login', route => route.fulfill({
status: 200,
body: 'accept',
}));
await page.goto('https://example.com');
```
#### API reference
- [`method: BrowserContext.route`]
- [`method: BrowserContext.unroute`]
- [`method: Page.route`]
- [`method: Page.unroute`]
- [Route]
<br/>
## Modify requests
```js
// Delete header
await page.route('**/*', route => {
const headers = route.request().headers();
delete headers['X-Secret'];
route.continue({headers});
});
// Continue requests as POST.
await page.route('**/*', route => route.continue({method: 'POST'}));
```
You can continue requests with modifications. Example above removes an HTTP header from the outgoing requests.
## Abort requests
```js
await page.route('**/*.{png,jpg,jpeg}', route => route.abort());
// Abort based on the request type
await page.route('**/*', route => {
return route.request().resourceType() === 'image' ?
route.abort() : route.continue();
});
```
#### API reference
- [`method: Page.route`]
- [`method: BrowserContext.route`]
- [`method: Route.abort`]
<br/>

53
docs-src/pom.md Normal file
View file

@ -0,0 +1,53 @@
# Page Object Models
Large test suites can be structured to optimize ease of authoring and maintenance.
Page object models are one such approach to structure your test suite.
<!-- GEN:toc-top-level -->
<!-- GEN:stop -->
## Introduction
A page object represents a part of your web application. An e-commerce web
application might have a home page, a listings page and a checkout page. Each of
them can be represented by page object models.
Page objects **simplify authoring**. They create a higher-level API which suits
your application.
Page objects **simplify maintenance**. They capture element selectors in one place
and create reusable code to avoid repetition.
## Implementation
Page object models wrap over a Playwright [Page].
```js
// models/Search.js
class SearchPage {
constructor(page) {
this.page = page;
}
async navigate() {
await this.page.goto('https://bing.com');
}
async search(text) {
await this.page.fill('[aria-label="Enter your search term"]', text);
await this.page.keyboard.press('Enter');
}
}
module.exports = { SearchPage };
```
Page objects can then be used inside a test.
```js
// search.spec.js
const { SearchPage } = require('./models/Search');
// In the test
const page = await browser.newPage();
const searchPage = new SearchPage(page);
await searchPage.navigate();
await searchPage.search('search query');
```
### API reference
- [Page]

263
docs-src/selectors.md Normal file
View file

@ -0,0 +1,263 @@
# Element selectors
Selectors query elements on the web page for interactions, like [`method: Page.click`], and to obtain `ElementHandle` through [`method: Page.$`]. Built-in selectors auto-pierce [shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
<!-- GEN:toc-top-level -->
<!-- GEN:stop -->
## Syntax
Selectors are defined by selector engine name and selector body, `engine=body`.
* `engine` refers to one of the [supported engines](#selector-engines)
* Built-in selector engines: [css], [text], [xpath] and [id selectors][id]
* Learn more about [custom selector engines](./extensibility.md)
* `body` refers to the query string for the respective engine
* For `text`, body is the text content
* For `css`, body is a [css selector](https://developer.mozilla.org/en/docs/Web/CSS/CSS_Selectors)
Body format is assumed to ignore leading and trailing white spaces, so that extra whitespace can be added for readability.
### Short-forms
For convenience, common selectors have short-forms:
- Selector starting with `//` or `..` is assumed to be `xpath=selector`
- Example: `page.click('//html')` is converted to `page.click('xpath=//html')`.
- Selector starting and ending with a quote (either `"` or `'`) is assumed to be `text=selector`
- Example: `page.click('"foo"')` is converted to `page.click('text="foo"')`.
- Otherwise, selector is assumed to be `css=selector`
- Example: `page.click('div')` is converted to `page.click('css=div')`.
### Chaining selectors
Selectors defined as `engine=body` or in short-form can be combined with the `>>` token, e.g. `selector1 >> selector2 >> selectors3`. When selectors are chained, next one is queried relative to the previous one's result.
For example,
```
css=article >> css=.bar > .baz >> css=span[attr=value]
```
is equivalent to
```js
document
.querySelector('article')
.querySelector('.bar > .baz')
.querySelector('span[attr=value]')
```
If a selector needs to include `>>` in the body, it should be escaped inside a string to not be confused with chaining separator, e.g. `text="some >> text"`.
### Intermediate matches
By default, chained selectors resolve to an element queried by the last selector. A selector can be prefixed with `*` to capture elements that are queried by an intermediate selector.
For example, `css=article >> text=Hello` captures the element with the text `Hello`, and `*css=article >> text=Hello` (note the `*`) captures the `article` element that contains some element with the text `Hello`.
## Best practices
The choice of selectors determines the resiliency of automation scripts. To reduce the maintenance burden, we recommend prioritizing user-facing attributes and explicit contracts.
### Prioritize user-facing attributes
Attributes like text content, input placeholder, accessibility roles and labels are user-facing attributes that change rarely. These attributes are not impacted by DOM structure changes.
The following examples use the built-in [text] and [css] selector engines.
```js
// queries "Login" text selector
await page.click('text="Login"');
await page.click('"Login"'); // short-form
// queries "Search GitHub" placeholder attribute
await page.fill('css=[placeholder="Search GitHub"]');
await page.fill('[placeholder="Search GitHub"]'); // short-form
// queries "Close" accessibility label
await page.click('css=[aria-label="Close"]');
await page.click('[aria-label="Close"]'); // short-form
// combine role and text queries
await page.click('css=nav >> text=Login');
```
### Define explicit contract
When user-facing attributes change frequently, it is recommended to use explicit test ids, like `data-test-id`. These `data-*` attributes are supported by the [css] and [id selectors][id].
```html
<button data-test-id="directions">Itinéraire</button>
```
```js
// queries data-test-id attribute with css
await page.click('css=[data-test-id=directions]');
await page.click('[data-test-id=directions]'); // short-form
// queries data-test-id with id
await page.click('data-test-id=directions');
```
### Avoid selectors tied to implementation
[xpath] and [css] can be tied to the DOM structure or implementation. These selectors can break when the DOM structure changes.
```js
// avoid long css or xpath chains
await page.click('#tsf > div:nth-child(2) > div.A8SBwf > div.RNNXgb > div > div.a4bIc > input');
await page.click('//*[@id="tsf"]/div[2]/div[1]/div[1]/div/div[2]/input');
```
## Examples
```js
// queries 'div' css selector
const handle = await page.$('css=div');
// queries '//html/body/div' xpath selector
const handle = await page.$('xpath=//html/body/div');
// queries '"foo"' text selector
const handle = await page.$('text="foo"');
// queries 'span' css selector inside the result of '//html/body/div' xpath selector
const handle = await page.$('xpath=//html/body/div >> css=span');
// converted to 'css=div'
const handle = await page.$('div');
// converted to 'xpath=//html/body/div'
const handle = await page.$('//html/body/div');
// converted to 'text="foo"'
const handle = await page.$('"foo"');
// queries 'span' css selector inside the div handle
const handle = await divHandle.$('css=span');
```
## Selector engines
### css and css:light
`css` is a default engine - any malformed selector not starting with `//` nor starting and ending with a quote is assumed to be a css selector. For example, Playwright converts `page.$('span > button')` to `page.$('css=span > button')`.
Playwright augments standard CSS selectors in two ways, see below for more details:
* `css` engine pierces open shadow DOM by default.
* Playwright adds a few custom pseudo-classes like `:visible`.
#### Shadow piercing
`css:light` engine is equivalent to [`Document.querySelector`](https://developer.mozilla.org/en/docs/Web/API/Document/querySelector) and behaves according to the CSS spec. However, it does not pierce shadow roots, which may be inconvenient when working with [Shadow DOM and Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM). For that reason, `css` engine pierces shadow roots. More specifically, any [Descendant combinator](https://developer.mozilla.org/en-US/docs/Web/CSS/Descendant_combinator) or [Child combinator](https://developer.mozilla.org/en-US/docs/Web/CSS/Child_combinator) pierces an arbitrary number of open shadow roots, including the implicit descendant combinator at the start of the selector.
`css` engine first searches for elements in the light dom in the iteration order, and then recursively inside open shadow roots in the iteration order. It does not search inside closed shadow roots or iframes.
```html
<article>
<div>In the light dom</div>
<div slot='myslot'>In the light dom, but goes into the shadow slot</div>
<open mode shadow root>
<div class='in-the-shadow'>
<span class='content'>
In the shadow dom
<open mode shadow root>
<li id='target'>Deep in the shadow</li>
</open mode shadow root>
</span>
</div>
<slot name='myslot'></slot>
</open mode shadow root>
</article>
```
Note that `<open mode shadow root>` is not an html element, but rather a shadow root created with `element.attachShadow({mode: 'open'})`.
- Both `"css=article div"` and `"css:light=article div"` match the first `<div>In the light dom</div>`.
- Both `"css=article > div"` and `"css:light=article > div"` match two `div` elements that are direct children of the `article`.
- `"css=article .in-the-shadow"` matches the `<div class='in-the-shadow'>`, piercing the shadow root, while `"css:light=article .in-the-shadow"` does not match anything.
- `"css:light=article div > span"` does not match anything, because both light-dom `div` elements do not contain a `span`.
- `"css=article div > span"` matches the `<span class='content'>`, piercing the shadow root.
- `"css=article > .in-the-shadow"` does not match anything, because `<div class='in-the-shadow'>` is not a direct child of `article`
- `"css:light=article > .in-the-shadow"` does not match anything.
- `"css=article li#target"` matches the `<li id='target'>Deep in the shadow</li>`, piercing two shadow roots.
#### CSS extension: visible
The `:visible` pseudo-class matches elements that are visible as defined in the [actionability](./actionability.md#visible) guide. For example, `input` matches all the inputs on the page, while `input:visible` matches only visible inputs. This is useful to distinguish elements that are very similar but differ in visibility.
```js
// Clicks the first button.
await page.click('button');
// Clicks the first visible button. If there are some invisible buttons, this click will just ignore them.
await page.click('button:visible');
```
Use `:visible` with caution, because it has two major drawbacks:
* When elements change their visibility dynamically, `:visible` will give upredictable results based on the timing.
* `:visible` forces a layout and may lead to querying being slow, especially when used with `page.waitForSelector(selector[, options])` method.
#### CSS extension: text
The `:text` pseudo-class matches elements that have a text node child with specific text. It is similar to the [text engine](#text-and-textlight). There are a few variations that support different arguments:
* `:text("substring")` - Matches when element's text contains "substring" somewhere. Matching is case-insensitive. Matching also normalizes whitespace, for example it turns multiple spaces into one, trusn line breaks into spaces and ignores leading and trailing whitespace.
* `:text-is("string")` - Matches when element's text equals the "string". Matching is case-insensitive and normalizes whitespace.
* `button:text("Sign in")` - Text selector may be combined with regular CSS.
* `:text-matches("[+-]?\\d+")` - Matches text against a regular expression. Note that special characters like back-slash `\`, quotes `"`, square brackets `[]` and more should be escaped. Learn more about [regular expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp).
* `:text-matches("value", "i")` - Matches text against a regular expression with specified flags.
```js
// Click a button with text "Sign in".
await page.click('button:text("Sign in")');
```
#### CSS extension: light
`css` engine [pierces shadow](#shadow-piercing) by default. It is possible to disable this behavior by wrapping a selector in `:light` pseudo-class: `:light(section > button.class)` matches in light DOM only.
```js
await page.click(':light(.article > .header)');
```
<!--
#### CSS extension: proximity
Playwright provides a few proximity selectors based on the page layout. These can be combined with regular CSS for better results, for example `input:right-of(:text("Password"))` matches an input field that is to the right of text "Password".
Note that Playwright uses some heuristics to determine whether one element should be considered to the left/right/above/below/near/within another. Therefore, using proximity selectors may produce unpredictable results. For example, selector could stop matching when element moves by one pixel.
* `:right-of(css > selector)` - Matches elements that are to the right of any element matching the inner selector.
* `:left-of(css > selector)` - Matches elements that are to the left of any element matching the inner selector.
* `:above(css > selector)` - Matches elements that are above any of the elements matching the inner selector.
* `:below(css > selector)` - Matches elements that are below any of the elements matching the inner selector.
* `:near(css > selector)` - Matches elements that are near any of the elements matching the inner selector.
* `:within(css > selector)` - Matches elements that are within any of the elements matching the inner selector.
```js
// Fill an input to the right of "Username".
await page.fill('input:right-of(:text("Username"))');
// Click a button near the promo card.
await page.click('button:near(.promo-card)');
```
-->
### xpath
XPath engine is equivalent to [`Document.evaluate`](https://developer.mozilla.org/en/docs/Web/API/Document/evaluate). Example: `xpath=//html/body`.
Malformed selector starting with `//` or `..` is assumed to be an xpath selector. For example, Playwright converts `page.$('//html/body')` to `page.$('xpath=//html/body')`.
Note that `xpath` does not pierce shadow roots.
### text and text:light
Text engine finds an element that contains a text node with the passed text. For example, `page.click('text=Login')` clicks on a login button, and `page.waitForSelector('"lazy loaded text")` waits for the `"lazy loaded text"` to appear in the page.
- By default, the match is case-insensitive, ignores leading/trailing whitespace and searches for a substring. This means `text= Login` matches `<button>Button loGIN (click me)</button>`.
- Text body can be escaped with single or double quotes for precise matching, insisting on exact match, including specified whitespace and case. This means `text="Login "` will only match `<button>Login </button>` with exactly one space after "Login". Quoted text follows the usual escaping rules, e.g. use `\"` to escape double quote in a double-quoted string: `text="foo\"bar"`.
- Text body can also be a JavaScript-like regex wrapped in `/` symbols. This means `text=/^\\s*Login$/i` will match `<button> loGIN</button>` with any number of spaces before "Login" and no spaces after.
- Input elements of the type `button` and `submit` are rendered with their value as text, and text engine finds them. For example, `text=Login` matches `<input type=button value="Login">`.
Malformed selector starting and ending with a quote (either `"` or `'`) is assumed to be a text selector. For example, Playwright converts `page.click('"Login"')` to `page.click('text="Login"')`.
`text` engine open pierces shadow roots similarly to `css`, while `text:light` does not. Text engine first searches for elements in the light dom in the iteration order, and then recursively inside open shadow roots in the iteration order. It does not search inside closed shadow roots or iframes.
### id, data-testid, data-test-id, data-test and their :light counterparts
Attribute engines are selecting based on the corresponding attribute value. For example: `data-test-id=foo` is equivalent to `css=[data-test-id="foo"]`, and `id:light=foo` is equivalent to `css:light=[id="foo"]`.
[css]: #css-and-csslight
[text]: #text-and-textlight
[xpath]: #xpath
[id]: #id-data-testid-data-test-id-data-test-and-their-light-counterparts

57
docs-src/showcase.md Normal file
View file

@ -0,0 +1,57 @@
# Community Showcase
## Users
* [VS Code](https://github.com/microsoft/vscode): Playwright is used to run cross-browser tests on their web builds
* [TypeScript](https://github.com/microsoft/TypeScript): Playwright is used test typescript.js across browsers
* [Elastic APM JS agent](https://github.com/elastic/apm-agent-rum-js): Playwright is used to run benchmark tests across browsers
* [Blockstack](https://github.com/blockstack/ux): Playwright is used to run cross-browser UI tests
* [Instakittens React admin](https://github.com/fredericbonnet/instakittens-react-admin): Playwright is used to run end-to-end test scenarios written with Cucumber
* [xterm.js](https://github.com/xtermjs/xterm.js): Playwright is used to run cross-browser integration tests
* [Accessibility Insights for Web](https://github.com/microsoft/accessibility-insights-web): Playwright is used with Jest and axe-core to run end-to-end functional and accessibility tests of a WebExtension-based browser extension
## Tools
* [CodeceptJS](https://github.com/Codeception/CodeceptJS): Write scenario-driven Playwright tests with synchronous code
* [dom-to-playwright](https://github.com/Xiphe/dom-to-playwright) to copy a JSDOM snapshot into a Playwright page.
* [expected-condition-playwright](https://github.com/elaichenkov/expected-condition-playwright): Supplies a set of common expected conditions that can wait for certain states and conditions
* [Headless Testing](https://headlesstesting.com/support/start/playwright.html): Run Playwright tests on browsers in the cloud
* [Lumberjack](https://github.com/JakePartusch/lumberjack): Automated accessibility scanner to run checks on your entire website
* [mockiavelli](https://github.com/HLTech/mockiavelli) Request mocking library for Playwright to test SPA in isolation from backend APIs.
* [Moon](https://github.com/aerokube/moon): Run Playwright tests in parallel in Kubernetes cluster (free up to 4 parallel sessions)
* [playwright-test](https://github.com/hugomrdias/playwright-test) Run unit tests and benchmarks in browsers with Node's seamless experience.
* [playwright-video](https://github.com/qawolf/playwright-video): Capture a video while executing a Playwright script
* [QA Wolf](https://github.com/qawolf/qawolf): Record and create Playwright tests and then run them in CI
* [Root Cause OSS](https://github.com/testimio/root-cause): Capture screenshots on every step and display in a viewer with logs for easy troubleshooting.
* [test-real-styles](https://github.com/Xiphe/test-real-styles): Utility to test real styling of virtual DOM elements in a browser
* [Testim Playground](https://www.testim.io/playground/): Record Playwright UI tests as code
* [Try Playwright](https://try.playwright.tech/): Interactive playground for Playwright to run examples directly from your browser
* [Applitools](https://applitools.com): Add AI-powered visual assertions and run your Playwright tests on all browser, device, and viewport combinations in parallel, without requiring any setup.
## Frameworks
* [jest-playwright](https://github.com/mmarkelov/jest-playwright): Jest preset to run Playwright tests with Jest
* [query-selector-shadow-dom](https://github.com/Georgegriff/query-selector-shadow-dom): Custom selector engine to pierce shadow DOM roots
* [Playwright Sharp](https://github.com/kblok/playwright-sharp): Work in progress port of Playwright to .NET
* [playwright-fluent](https://github.com/hdorgeval/playwright-fluent): Fluent API around Playwright
* [robotframework-browser](https://robotframework-browser.org/) Robotframework library that uses Playwright to achieve good development ergonomics.
## Examples
* [e2e Boilerplates](https://github.com/e2e-boilerplate?utf8=%E2%9C%93&q=playwright): Project boilerplates for using Playwright with TypeScript, Cucumber, Jest, and other libraries
* [react-app-playwright](https://github.com/KyleADay/react-app-playwright): Using Playwright with a create-react-app project
* [playwright-react-typescript-jest-example](https://github.com/azemetre/playwright-react-typescript-jest-example): Using Playwright + Jest with a custom webpack configuration for React + Typescript project
* [playwright-mocha](https://github.com/roggerfe/playwright-mocha): Using Playwright with Mocha and Chai
* [playwright-cljs](https://github.com/apeckham/playwright-cljs): Playwright examples in ClojureScript
* [playwright-azure-functions](https://github.com/arjun27/playwright-azure-functions): Playwright setup on Azure Functions
* [playwright-aws-lambda](https://github.com/austinkelleher/playwright-aws-lambda): Playwright setup on AWS Lambda
* [playwright-jest-circus-allure](https://github.com/d-shch/playwright-jest-circus-allure): Example how to use allure-report and jest-circus with playwright
* [Heroku Playwright Example](https://github.com/mxschmitt/heroku-playwright-example): Example using Playwright on Heroku
## Guides
* [theheadless.dev](https://theheadless.dev): Practical guides and runnable examples on Playwright (and Puppeteer)
## Contributing
Did we miss something in this list? Send us a PR!

112
docs-src/test-runners.md Normal file
View file

@ -0,0 +1,112 @@
# Test Runners
With a few lines of code, you can hook up Playwright to your favorite JavaScript test runner.
<!-- GEN:toc -->
<!-- GEN:stop -->
<br>
<br>
## Jest / Jasmine
For Jest, [jest-playwright](https://github.com/playwright-community/jest-playwright) can be used. However for a light-weight solution, requiring playwright directly works fine. Jest shares it's syntax with Jasmine, so this applies to Jasmine as well.
```js
const {chromium} = require('playwright');
const expect = require('expect');
let browser;
let page;
beforeAll(async () => {
browser = await chromium.launch();
});
afterAll(async () => {
await browser.close();
});
beforeEach(async () => {
page = await browser.newPage();
});
afterEach(async () => {
await page.close();
});
it('should work', async () => {
await page.goto('https://www.example.com/');
expect(await page.title()).toBe('Example Domain');
});
```
<br>
## AVA
Tests run concurrently in AVA, so a single page variable cannot be shared between tests. Instead, create new pages with a macro function.
```js
const {chromium} = require('playwright');
const test = require('ava').default;
const browserPromise = chromium.launch();
async function pageMacro(t, callback) {
const browser = await browserPromise;
const page = await browser.newPage();
try {
await callback(t, page);
} finally {
await page.close();
}
}
test('should work', pageMacro, async (t, page) => {
await page.goto('https://www.example.com/');
t.is(await page.title(), 'Example Domain');
});
```
<br>
## Mocha
Mocha looks very similar to the Jest/Jasmine setup, and functions in the same way.
```js
const {chromium} = require('playwright');
const assert = require('assert');
let browser;
before(async() => {
browser = await chromium.launch();
});
after(async () => {
await browser.close();
});
let page;
beforeEach(async() => {
page = await browser.newPage();
});
afterEach(async () => {
await page.close();
});
it('should work', async () => {
await page.goto('https://www.example.com/');
assert.equal(await page.title(), 'Example Domain');
});
```
<br>
## Multiple Browsers
These simple examples can be extended to support multiple browsers using an environment variable.
```js
const {chromium, webkit, firefox} = require('playwright');
const browserName = process.env.BROWSER || 'webkit';
let browser;
beforeAll(async() => {
browser = await {chromium, webkit, firefox}[browserName].launch();
});
```
Then set `BROWSER=firefox` to run your tests with firefox, or any other browser.

View file

@ -0,0 +1,33 @@
# Troubleshooting
<!-- GEN:toc -->
<!-- GEN:stop -->
## Browser dependencies
Playwright does self-inspection every time it runs to make sure the browsers can be launched successfully. If there are missing
dependencies, playwright will print instructions to acquire them.
We also provide [Ubuntu 18.04 dockerfile](docker/Dockerfile.bionic) and [Ubuntu 20.04 dockerfile](docker/Dockerfile.focal) with the list of Debian dependencies.
## Code transpilation issues
If you are using a JavaScript transpiler like babel or TypeScript, calling `evaluate()` with an async function might not work. This is because while `playwright` uses `Function.prototype.toString()` to serialize functions while transpilers could be changing the output code in such a way it's incompatible with `playwright`.
Some workarounds to this problem would be to instruct the transpiler not to mess up with the code, for example, configure TypeScript to use latest ECMAScript version (`"target": "es2018"`). Another workaround could be using string templates instead of functions:
```js
await page.evaluate(`(async() => {
console.log('1');
})()`);
```
## Node.js requirements
### ReferenceError: URL is not defined
Playwright requires Node.js 10 or higher. Node.js 8 is not supported, and will cause you to receive this error.
# Please file an issue
Playwright is a new project, and we are watching the issues very closely. As we solve common issues, this document will grow to include the common answers.

153
docs-src/verification.md Normal file
View file

@ -0,0 +1,153 @@
# Verification
<!-- GEN:toc-top-level -->
<!-- GEN:stop -->
<br/>
## Videos
Playwright can record videos for all pages in a [browser context](core-concepts.md#browser-contexts). Videos are saved upon context closure, so make sure to await `browserContext.close()`.
```js
// With browser.newContext()
const context = await browser.newContext({ recordVideo: { dir: 'videos/' } });
// Make sure to await close, so that videos are saved.
await context.close();
// With browser.newPage()
const page = await browser.newPage({ recordVideo: { dir: 'videos/' } });
// Make sure to await close, so that videos are saved.
await page.close();
// [Optional] Specify video size; defaults to viewport size
const context = await browser.newContext({
recordVideo: {
dir: 'videos/',
size: { width: 800, height: 600 },
}
});
```
#### API reference
- [BrowserContext]
- [`method: Browser.newContext`]
- [`method: Browser.newPage`]
- [`method: BrowserContext.close`]
## Screenshots
```js
// Save to file
await page.screenshot({ path: 'screenshot.png' });
// Capture full page
await page.screenshot({ path: 'screenshot.png', fullPage: true });
// Capture into buffer
const buffer = await page.screenshot();
console.log(buffer.toString('base64'));
// Capture given element
const elementHandle = await page.$('.header');
await elementHandle.screenshot({ path: 'screenshot.png' });
```
#### API reference
- [`method: Page.screenshot`]
- [`method: ElementHandle.screenshot`]
<br/>
## Console logs
Console messages logged in the page can be brought into the Node.js context.
```js
// Listen for all console logs
page.on('console', msg => console.log(msg.text()))
// Listen for all console events and handle errors
page.on('console', msg => {
if (msg.type() === 'error')
console.log(`Error text: "${msg.text()}"`);
});
// Get the next console log
const [msg] = await Promise.all([
page.waitForEvent('console'),
// Issue console.log inside the page
page.evaluate(() => {
console.log('hello', 42, { foo: 'bar' });
}),
]);
// Deconstruct console log arguments
await msg.args[0].jsonValue() // hello
await msg.args[1].jsonValue() // 42
```
#### API reference
- [ConsoleMessage]
- [Page]
- [`event: Page.console`]
<br/>
## Page errors
Listen for uncaught exceptions in the page with the `pagerror` event.
```js
// Log all uncaught errors to the terminal
page.on('pageerror', exception => {
console.log(`Uncaught exception: "${exception}"`);
});
// Navigate to a page with an exception.
await page.goto('data:text/html,<script>throw new Error("Test")</script>');
```
#### API reference
- [Page]
- [`event: Page.pageerror`]
<br/>
## Page events
#### `"requestfailed"`
```js
page.on('requestfailed', request => {
console.log(request.url() + ' ' + request.failure().errorText);
});
```
#### `"dialog"` - handle alert, confirm, prompt
```js
page.on('dialog', dialog => {
dialog.accept();
});
```
#### `"popup"` - handle popup windows
```js
const [popup] = await Promise.all([
page.waitForEvent('popup'),
page.click('#open')
]);
```
#### API reference
- [Page]
- [`event: Page.requestfailed`]
- [`event: Page.dialog`]
- [`event: Page.popup`]

View file

@ -0,0 +1,52 @@
# Why Playwright?
Playwright enables fast, reliable and capable automation across all modern browsers. This guide covers those key differentiators to help you decide on the right tool for your automated tests.
<!-- GEN:toc-top-level -->
<!-- GEN:stop -->
## Support for all browsers
* **Test on Chromium, Firefox and WebKit**. Playwright has full API coverage for all modern browsers, including Google Chrome and Microsoft Edge (with [Chromium](https://www.chromium.org/)), Apple Safari (with [WebKit](https://webkit.org/)) and Mozilla Firefox.
* **Cross-platform WebKit testing**. With Playwright, test how your app behaves in Apple Safari with WebKit builds for Windows, Linux and macOS. Test locally and on CI.
* **Test for mobile**. Use [device emulation](./emulation.md) to test your responsive web apps in mobile web browsers.
* **Headless and headful**. Playwright supports headless (without browser UI) and headful (with browser UI) modes for all browsers and all platforms. Headful is great for debugging, and headless is faster and suited for CI/cloud executions.
## Fast and reliable execution
* **Auto-wait APIs**. Playwright interactions [auto-wait for elements](./actionability.md) to be ready. This improves reliability and simplifies test authoring.
* **Timeout-free automation**. Playwright receives browser signals, like network requests, page navigations and page load events to eliminate the need for sleep timeouts that cause flakiness.
* **Lean parallelization with browser contexts**. Reuse a single browser instance for multiple parallelized, isolated execution environments with [browser contexts](core-concepts.md).
* **Resilient element selectors**. Playwright can rely on user-facing strings, like text content and accessibility labels to [select elements](./selectors.md). These strings are more resilient than selectors tightly-coupled to the DOM structure.
## Powerful automation capabilities
* **Multiple domains, pages and frames**. Playwright is an out-of-process automation driver that is not limited by the scope of in-page JavaScript execution and can automate scenarios with [multiple pages](multi-pages.md).
* **Powerful network control**. Playwright introduces context-wide [network interception](./network.md) to stub and mock network requests.
* **Modern web features**. Playwright supports web components through [shadow-piercing selectors](./selectors.md), [geolocation, permissions](./emulation.md), web workers and other modern web APIs.
* **Capabilities to cover all scenarios**. Support for [file downloads](./network.md) and [uploads](./input.md), out-of-process iframes, native [input events](./input.md), and even [dark mode](./emulation.md).
## Integrates with your workflow
* **One-line installation**. Running `npm i playwright` auto-downloads browser dependencies for your team to be onboarded quickly.
* **TypeScript support**. Playwright ships with built-in types for auto-completion and other benefits.
* **Debugging tools**. Playwright works with the [editor debugger and browser developer tools](./debug.md) to pause execution and inspect the web page.
* **Language bindings**. Playwright is also available in [Python](https://github.com/microsoft/playwright-python) and [C#](https://github.com/microsoft/playwright-sharp). Learn about [supported languages](./languages.md).
* **Deploy tests to CI**. First-party [Docker image](docker/README.md) and [GitHub Actions](https://github.com/microsoft/playwright-github-action) to deploy tests to [your preferred CI/CD provider](./ci.md).
## Limitations
* **Legacy Edge and IE11 support**. Playwright does not support legacy Microsoft Edge or IE11 ([deprecation notice](https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666)). The new Microsoft Edge (on Chromium) is supported.
* **Java language bindings**: The Playwright API cannot be used in Java or Ruby today. This is a temporary limitation as Playwright is built to support bindings for any language.
* **Test on real mobile devices**: Playwright uses desktop browsers to emulate mobile devices. If you are interested in running on real mobile devices, please [upvote this issue](https://github.com/microsoft/playwright/issues/1122).

View file

@ -1,10 +1,18 @@
<!-- THIS FILE IS NOW GENERATED -->
# Actionability
Playwright does a range of actionability checks on the elements before performing certain actions. These checks ensure that action behaves as expected, for example Playwright does not click on a disabled button.
Playwright does a range of actionability checks on the elements before
performing certain actions. These checks ensure that action behaves as expected,
for example Playwright does not click on a disabled button.
Playwright waits until all the relevant actionability checks pass before performing an action. This means that action will fail with `TimeoutError` if checks do not pass within the specified `timeout`.
Playwright waits until all the relevant actionability checks pass before
performing an action. This means that action will fail with `TimeoutError` if
checks do not pass within the specified `timeout`.
Some actions like `page.click()` support `{force: true}` option that disable non-essential actionability checks, for example passing `force` to `click()` method will not check that the target element actually receives click events.
Some actions like `page.click()` support `{force: true}` option that disable
non-essential actionability checks, for example passing `force` to `click()`
method will not check that the target element actually receives click events.
| Actions | Performed checks |
| ------ | ------- |
@ -18,15 +26,19 @@ Some actions like `page.click()` support `{force: true}` option that disable non
### Visible
Element is considered visible when it has non-empty bounding box and does not have `visibility:hidden` computed style. Note that elements of zero size or with `display:none` are not considered visible.
Element is considered visible when it has non-empty bounding box and does not
have `visibility:hidden` computed style. Note that elements of zero size or with
`display:none` are not considered visible.
### Stable
Element is considered stable when it has maintained the same bounding box for at least two consecutive animation frames.
Element is considered stable when it has maintained the same bounding box for at
least two consecutive animation frames.
### Enabled
Element is considered enabled when it is not a `<button>`, `<select>` or `<input>` with a `disabled` property set.
Element is considered enabled when it is not a `<button>`, `<select>` or
`<input>` with a `disabled` property set.
### Editable
@ -34,19 +46,30 @@ Element is considered editable when it does not have `readonly` property set.
### Receiving events
Element is considered receiving pointer events when it is the hit target of the pointer event at the action point. For example, when clicking at the point `(10;10)`, Playwright checks whether some other element (usually an overlay) will instead capture the click at `(10;10)`.
Element is considered receiving pointer events when it is the hit target of the
pointer event at the action point. For example, when clicking at the point
`(10;10)`, Playwright checks whether some other element (usually an overlay)
will instead capture the click at `(10;10)`.
### Attached
Element is considered attached when it is [connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected) to a Document or a ShadowRoot.
Element is considered attached when it is
[connected](https://developer.mozilla.org/en-US/docs/Web/API/Node/isConnected)
to a Document or a ShadowRoot.
Attached check differs between selector-based and handle-based actions, like `page.click(selector, options)` as opposite to `elementHandle.click(options)`:
- For selector-based actions, Playwright first waits for an element matching `selector` to be attached to the DOM, and then checks that element is still attached before performing the action. If element was detached, the action is retried from the start.
Attached check differs between selector-based and handle-based actions, like
`page.click(selector, options)` as opposite to `elementHandle.click(options)`:
- For selector-based actions, Playwright first waits for an element matching
`selector` to be attached to the DOM, and then checks that element is still
attached before performing the action. If element was detached, the action
is retried from the start.
- For handle-based actions, Playwright throws if the element is not attached.
For example, consider a scenario where Playwright will click `Sign Up` button regardless of when the `page.click()` call was made:
For example, consider a scenario where Playwright will click `Sign Up` button
regardless of when the `page.click()` call was made:
- page is checking that user name is unique and `Sign Up` button is disabled;
- after checking with the server, the disabled `Sign Up` button is replaced with another one that is now enabled.
- after checking with the server, the disabled `Sign Up` button is replaced
with another one that is now enabled.
[Visible]: #visible "Visible"
[Stable]: #stable "Stable"
@ -54,3 +77,60 @@ For example, consider a scenario where Playwright will click `Sign Up` button re
[Editable]: #editable "Editable"
[Receiving Events]: #receiving-events "Receiving Events"
[Attached]: #attached "Attached"
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

File diff suppressed because it is too large Load diff

View file

@ -1,9 +1,16 @@
<!-- THIS FILE IS NOW GENERATED -->
# Assertions
The Playwright API can be used to read element contents and properties for test assertions. These values are fetched from the browser page and asserted in
The Playwright API can be used to read element contents and properties for test
assertions. These values are fetched from the browser page and asserted in
Node.js.
The examples in this guide use the built-in [`assert` module](https://nodejs.org/api/assert.html), but they can be used with any assertion library (like [Expect](https://www.npmjs.com/package/expect) or [Chai](https://www.npmjs.com/package/chai)). See [Test runners](test-runners.md) for more info.
The examples in this guide use the built-in
[`assert` module](https://nodejs.org/api/assert.html), but they can be used with
any assertion library (like [Expect](https://www.npmjs.com/package/expect) or
[Chai](https://www.npmjs.com/package/chai)). See [Test runners](test-runners.md)
for more info.
<!-- GEN:toc-top-level -->
- [Common patterns](#common-patterns)
@ -15,9 +22,9 @@ The examples in this guide use the built-in [`assert` module](https://nodejs.org
## Common patterns
Playwright provides convenience APIs for common assertion tasks, like finding the
text content of an element. These APIs require a [selector](selectors.md) to locate
the element.
Playwright provides convenience APIs for common assertion tasks, like finding
the text content of an element. These APIs require a [selector](./selectors.md)
to locate the element.
```js
// Assert text content
@ -38,27 +45,28 @@ assert(checked);
```
#### API reference
- [page.textContent(selector[, options])](api.md#pagetextcontentselector-options)
- [page.innerText(selector[, options])](api.md#pageinnertextselector-options)
- [page.innerHTML(selector[, options])](api.md#pageinnerhtmlselector-options)
- [page.getAttribute(selector, name[, options])](api.md#pagegetattributeselector-name-options)
- [frame.textContent(selector[, options])](api.md#frametextcontentselector-options)
- [frame.innerText(selector[, options])](api.md#frameinnertextselector-options)
- [frame.innerHTML(selector[, options])](api.md#frameinnerhtmlselector-options)
- [frame.getAttribute(selector, name[, options])](api.md#framegetattributeselector-name-options)
- [page.textContent(selector[, options])](./api.md#pagetextcontentselector-options)
- [page.innerText(selector[, options])](./api.md#pageinnertextselector-options)
- [page.innerHTML(selector[, options])](./api.md#pageinnerhtmlselector-options)
- [page.getAttribute(selector, name[, options])](./api.md#pagegetattributeselector-name-options)
- [frame.textContent(selector[, options])](./api.md#frametextcontentselector-options)
- [frame.innerText(selector[, options])](./api.md#frameinnertextselector-options)
- [frame.innerHTML(selector[, options])](./api.md#frameinnerhtmlselector-options)
- [frame.getAttribute(selector, name[, options])](./api.md#framegetattributeselector-name-options)
<br/>
## Element Handles
[ElementHandle](api.md#class-elementhandle) objects represent in-page DOM
elements. They can be used to assert for multiple properties of the element.
[ElementHandle] objects represent in-page DOM elements. They can be used to
assert for multiple properties of the element.
It is recommended to fetch the `ElementHandle` object with
[`page.waitForSelector`](api.md#pagewaitforselectorselector-options) or
[`frame.waitForSelector`](api.md#framewaitforselectorselector-options). These
APIs wait for the element to be visible and then return an `ElementHandle`.
[page.waitForSelector(selector[, options])](./api.md#pagewaitforselectorselector-options)
or
[frame.waitForSelector(selector[, options])](./api.md#framewaitforselectorselector-options).
These APIs wait for the element to be visible and then return an
`ElementHandle`.
```js
// Get the element handle
@ -74,12 +82,11 @@ assert(classNames.includes('highlighted'));
```
#### API reference
- [elementHandle.textContent()](api.md#elementhandletextcontent)
- [elementHandle.innerText()](api.md#elementhandleinnertext)
- [elementHandle.innerHTML()](api.md#elementhandleinnerhtml)
- [elementHandle.getAttribute(name)](api.md#elementhandlegetattributename)
- [elementHandle.boundingBox()](api.md#elementhandleboundingbox)
- [elementHandle.textContent()](./api.md#elementhandletextcontent)
- [elementHandle.innerText()](./api.md#elementhandleinnertext)
- [elementHandle.innerHTML()](./api.md#elementhandleinnerhtml)
- [elementHandle.getAttribute(name)](./api.md#elementhandlegetattributename)
- [elementHandle.boundingBox()](./api.md#elementhandleboundingbox)
<br/>
@ -90,8 +97,9 @@ the browser. This is useful in situations where you want to assert for values
that are not covered by the convenience APIs above.
The following APIs do not auto-wait for the element. It is recommended to use
[`page.waitForSelector`](api.md#pagewaitforselectorselector-options) or
[`frame.waitForSelector`](api.md#framewaitforselectorselector-options).
[page.waitForSelector(selector[, options])](./api.md#pagewaitforselectorselector-options)
or
[frame.waitForSelector(selector[, options])](./api.md#framewaitforselectorselector-options).
```js
// Assert local storage value
@ -113,13 +121,69 @@ assert(length === 3);
```
#### API reference
- [page.evaluate(pageFunction[, arg])](api.md#pageevaluatepagefunction-arg)
- [page.$eval(selector, pageFunction[, arg])](api.md#pageevalselector-pagefunction-arg)
- [page.$$eval(selector, pageFunction[, arg])](api.md#pageevalselector-pagefunction-arg-1)
- [frame.evaluate(pageFunction[, arg])](api.md#frameevaluatepagefunction-arg)
- [frame.$eval(selector, pageFunction[, arg])](api.md#frameevalselector-pagefunction-arg)
- [frame.$$eval(selector, pageFunction[, arg])](api.md#frameevalselector-pagefunction-arg-1)
- [elementHandle.$eval(selector, pageFunction[, arg])](api.md#elementhandleevalselector-pagefunction-arg)
- [elementHandle.$$eval(selector, pageFunction[, arg])](api.md#elementhandleevalselector-pagefunction-arg-1)
- Evaluation argument [examples](api.md#evaluationargument)
- [page.evaluate(pageFunction[, arg])](./api.md#pageevaluatepagefunction-arg)
- [page.$eval(selector, pageFunction[, arg])](./api.md#pageevalselector-pagefunction-arg)
- [page.$$eval(selector, pageFunction[, arg])](./api.md#pageevalselector-pagefunction-arg-1)
- [frame.evaluate(pageFunction[, arg])](./api.md#frameevaluatepagefunction-arg)
- [frame.$eval(selector, pageFunction[, arg])](./api.md#frameevalselector-pagefunction-arg)
- [frame.$$eval(selector, pageFunction[, arg])](./api.md#frameevalselector-pagefunction-arg-1)
- [elementHandle.$eval(selector, pageFunction[, arg])](./api.md#elementhandleevalselector-pagefunction-arg)
- [elementHandle.$$eval(selector, pageFunction[, arg])](./api.md#elementhandleevalselector-pagefunction-arg-1)
- [EvaluationArgument]
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,14 +1,18 @@
<!-- THIS FILE IS NOW GENERATED -->
# Authentication
Playwright can be used to automate scenarios that require authentication.
Tests written with Playwright execute in isolated clean-slate environments called
[browser contexts](./core-concepts.md#browser-contexts). This isolation model
improves reproducibility and prevents cascading test failures. New browser
Tests written with Playwright execute in isolated clean-slate environments
called [browser contexts](./core-concepts.md#browser-contexts). This isolation
model improves reproducibility and prevents cascading test failures. New browser
contexts can load existing authentication state. This eliminates the need to
login in every context and speeds up test execution.
> Note: This guide covers cookie/token-based authentication (logging in via the
app UI). For [HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication)
app UI). For
[HTTP authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication)
use [`browser.newContext`](./network.md#http-authentication).
<!-- GEN:toc -->
@ -53,17 +57,18 @@ existing authentication state in new browser contexts.
## Reuse authentication state
Web apps use cookie-based or token-based authentication, where authenticated
state is stored as [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies)
or in [local storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage).
The Playwright API can be used to retrieve this state from authenticated contexts
state is stored as
[cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies) or in
[local storage](https://developer.mozilla.org/en-US/docs/Web/API/Storage). The
Playwright API can be used to retrieve this state from authenticated contexts
and then load it into new contexts.
Cookies and local storage state can be used across different browsers. They depend
on your application's authentication model: some apps might require both cookies
and local storage.
Cookies and local storage state can be used across different browsers. They
depend on your application's authentication model: some apps might require both
cookies and local storage.
The following code snippets retrieve state from an authenticated page/context and
load them into a new context.
The following code snippets retrieve state from an authenticated page/context
and load them into a new context.
### Cookies
@ -78,7 +83,9 @@ await context.addCookies(deserializedCookies);
```
### Local storage
Local storage ([`window.localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage))
Local storage
([`window.localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage))
is specific to a particular domain.
```js
@ -99,7 +106,9 @@ await context.addInitScript(storage => {
```
### Session storage
Session storage ([`window.sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage))
Session storage
([`window.sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage))
is specific to a particular domain.
```js
@ -123,14 +132,15 @@ await context.addInitScript(storage => {
Logging in via the UI and then reusing authentication state can be combined to
implement **login once and run multiple scenarios**. The lifecycle looks like:
1. Run tests (for example, with `npm run test`).
2. Login via UI and retrieve authentication state.
* In Jest, this can be executed in [`globalSetup`](https://jestjs.io/docs/en/configuration#globalsetup-string).
3. In each test, load authentication state in `beforeEach` or `beforeAll` step.
1. Login via UI and retrieve authentication state.
* In Jest, this can be executed in
[`globalSetup`](https://jestjs.io/docs/en/configuration#globalsetup-string).
1. In each test, load authentication state in `beforeEach` or `beforeAll`
step.
This approach will also **work in CI environments**, since it does not rely
on any external state.
This approach will also **work in CI environments**, since it does not rely on
any external state.
### Example
@ -138,20 +148,23 @@ on any external state.
Chromium, and then reuses the logged in cookie state in WebKit.
### API reference
- [class `BrowserContext`](./api.md#class-browsercontext)
- [`browserContext.cookies`](./api.md#browsercontextcookiesurls)
- [`browserContext.addCookies`](./api.md#browsercontextaddcookiescookies)
- [`page.evaluate`](./api.md#pageevaluatepagefunction-arg)
- [`browserContext.addInitScript`](./api.md#browsercontextaddinitscriptscript-arg)
- [BrowserContext]
- [browserContext.cookies([urls])](./api.md#browsercontextcookiesurls)
- [browserContext.addCookies(cookies)](./api.md#browsercontextaddcookiescookies)
- [page.evaluate(pageFunction[, arg])](./api.md#pageevaluatepagefunction-arg)
- [browserContext.addInitScript(script[, arg])](./api.md#browsercontextaddinitscriptscript-arg)
## Multi-factor authentication
Accounts with multi-factor authentication (MFA) cannot be fully automated, and need
manual intervention. Persistent authentication can be used to partially automate
MFA scenarios.
Accounts with multi-factor authentication (MFA) cannot be fully automated, and
need manual intervention. Persistent authentication can be used to partially
automate MFA scenarios.
### Persistent authentication
Web browsers use a directory on disk to store user history, cookies, IndexedDB
and other local state. This disk location is called the [User data directory](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md).
and other local state. This disk location is called the
[User data directory](https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md).
Note that persistent authentication is not suited for CI environments since it
relies on a disk location. User data directories are specific to browser types
@ -168,11 +181,67 @@ const context = await chromium.launchPersistentContext(userDataDir, { headless:
```
### Lifecycle
1. Create a user data directory on disk
2. Launch a persistent context with the user data directory and login the MFA account.
3. Reuse user data directory to run automation scenarios.
1. Create a user data directory on disk 2. Launch a persistent context with
the user data directory and login the MFA account. 3. Reuse user data
directory to run automation scenarios.
### API reference
- [class `BrowserContext`](./api.md#class-browsercontext)
- [`browserType.launchPersistentContext`](./api.md#browsertypelaunchpersistentcontextuserdatadir-options)
- [BrowserContext]
- [browserType.launchPersistentContext(userDataDir[, options])](./api.md#browsertypelaunchpersistentcontextuserdatadir-options)
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,3 +1,5 @@
<!-- THIS FILE IS NOW GENERATED -->
# Continuous Integration
Playwright tests can be executed in CI environments. We have created sample
@ -24,18 +26,21 @@ configurations for common CI providers.
## Introduction
3 steps to get your tests running on CI:
1. **Ensure CI agent can run browsers**: Use [our Docker image](docker/README.md)
in Linux agents. Windows and macOS agents do not require any additional dependencies.
1. **Ensure CI agent can run browsers**: Use
[our Docker image](docker/README.md) in Linux agents. Windows and macOS
agents do not require any additional dependencies.
1. **Install Playwright**: In most projects, this would be done with `npm ci`
(or `npm install`). Playwright would install the relevant browsers automatically.
(or `npm install`). Playwright would install the relevant browsers
automatically.
1. **Run your tests**: Use `npm test` or equivalent to execute your tests.
## CI configurations
### GitHub Actions
The [Playwright GitHub Action](https://github.com/microsoft/playwright-github-action) can be used to run Playwright tests on GitHub Actions.
The
[Playwright GitHub Action](https://github.com/microsoft/playwright-github-action)
can be used to run Playwright tests on GitHub Actions.
```yml
steps:
@ -44,18 +49,23 @@ steps:
run: npm test
```
We run [our tests](/.github/workflows/tests.yml) on GitHub Actions, across a matrix of 3 platforms (Windows, Linux, macOS) and 3 browsers (Chromium, Firefox, WebKit).
We run [our tests](/.github/workflows/tests.yml) on GitHub Actions, across a
matrix of 3 platforms (Windows, Linux, macOS) and 3 browsers (Chromium, Firefox,
WebKit).
### Docker
We have a [pre-built Docker image](docker/README.md) which can either be used directly, or as a reference to update your existing Docker definitions.
We have a [pre-built Docker image](docker/README.md) which can either be used
directly, or as a reference to update your existing Docker definitions.
Suggested configuration
1. By default, Docker runs a container with a `/dev/shm` shared memory space 64MB.
This is [typically too small](https://github.com/c0b/chrome-in-docker/issues/1) for Chromium
and will cause Chromium to crash when rendering large pages. To fix, run the container with
`docker run --shm-size=1gb` to increase the size of `/dev/shm`. Since Chromium 65, this is no
longer necessary. Instead, launch the browser with the `--disable-dev-shm-usage` flag:
1. By default, Docker runs a container with a `/dev/shm` shared memory space
64MB. This is
[typically too small](https://github.com/c0b/chrome-in-docker/issues/1)
for Chromium and will cause Chromium to crash when rendering large pages.
To fix, run the container with `docker run --shm-size=1gb` to increase the
size of `/dev/shm`. Since Chromium 65, this is no longer necessary.
Instead, launch the browser with the `--disable-dev-shm-usage` flag:
```js
const browser = await playwright.chromium.launch({
@ -64,21 +74,28 @@ Suggested configuration
```
This will write shared memory files into `/tmp` instead of `/dev/shm`. See
[crbug.com/736452](https://bugs.chromium.org/p/chromium/issues/detail?id=736452) for more details.
1. Using `--ipc=host` is also recommended when using Chromium—without it Chromium can run out of memory
and crash. Learn more about this option in [Docker docs](https://docs.docker.com/engine/reference/run/#ipc-settings---ipc).
1. Seeing other weird errors when launching Chromium? Try running your container
with `docker run --cap-add=SYS_ADMIN` when developing locally.
1. [dumb-init](https://github.com/Yelp/dumb-init) is worth checking out if you're
experiencing a lot of zombies Chromium processes sticking around. There's special
treatment for processes with PID=1, which makes it hard to terminate Chromium
properly in some cases (e.g. in Docker).
[crbug.com/736452](https://bugs.chromium.org/p/chromium/issues/detail?id=736452)
for more details.
1. Using `--ipc=host` is also recommended when using Chromium—without it
Chromium can run out of memory and crash. Learn more about this option in
[Docker docs](https://docs.docker.com/engine/reference/run/#ipc-settings---ipc).
1. Seeing other weird errors when launching Chromium? Try running your
container with `docker run --cap-add=SYS_ADMIN` when developing locally.
1. [dumb-init](https://github.com/Yelp/dumb-init) is worth checking out if
you're experiencing a lot of zombies Chromium processes sticking around.
There's special treatment for processes with PID=1, which makes it hard to
terminate Chromium properly in some cases (e.g. in Docker).
### Azure Pipelines
For Windows or macOS agents, no additional configuration required, just install Playwright and run your tests.
For Windows or macOS agents, no additional configuration required, just install
Playwright and run your tests.
For Linux agents, you can use [our Docker container](docker/README.md) with Azure Pipelines support for [running containerized jobs](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/container-phases?view=azure-devops). Alternatively, you can refer to the [Dockerfile](docker/README.md) to see additional dependencies that need to be installed on a Ubuntu agent.
For Linux agents, you can use [our Docker container](docker/README.md) with
Azure Pipelines support for
[running containerized jobs](https://docs.microsoft.com/en-us/azure/devops/pipelines/process/container-phases?view=azure-devops).
Alternatively, you can refer to the [Dockerfile](docker/README.md) to see
additional dependencies that need to be installed on a Ubuntu agent.
```yml
pool:
@ -98,11 +115,13 @@ We run our tests on Travis CI over a Linux agent (Ubuntu 18.04).
Suggested configuration
1. [User namespace cloning](http://man7.org/linux/man-pages/man7/user_namespaces.7.html)
should be enabled to support proper sandboxing
1. [xvfb](https://en.wikipedia.org/wiki/Xvfb) should be launched in order to run
Chromium in non-headless mode (e.g. to test Chrome Extensions)
1. If your project does not have `package-lock.json`, Travis would be auto-caching
`node_modules` directory. If you run `npm install` (instead of `npm ci`), it is
possible that the browser binaries are not downloaded. Fix this with [these steps](#exception-node_modules-are-cached) outlined below.
1. [xvfb](https://en.wikipedia.org/wiki/Xvfb) should be launched in order to
run Chromium in non-headless mode (e.g. to test Chrome Extensions)
1. If your project does not have `package-lock.json`, Travis would be
auto-caching `node_modules` directory. If you run `npm install` (instead
of `npm ci`), it is possible that the browser binaries are not downloaded.
Fix this with [these steps](#exception-node_modules-are-cached) outlined
below.
To sum up, your `.travis.yml` might look like this:
@ -149,8 +168,9 @@ before_install:
### CircleCI
We run our tests on CircleCI, with our [pre-built Docker image](docker/README.md). Running Playwright smoothly on CircleCI requires the following steps:
We run our tests on CircleCI, with our
[pre-built Docker image](docker/README.md). Running Playwright smoothly on
CircleCI requires the following steps:
1. Use the pre-built [Docker image](docker/README.md) in your config like so:
```yaml
@ -160,7 +180,8 @@ We run our tests on CircleCI, with our [pre-built Docker image](docker/README.md
NODE_ENV: development # Needed if playwright is in `devDependencies`
```
1. If youre using Playwright through Jest, then you may encounter an error spawning child processes:
1. If youre using Playwright through Jest, then you may encounter an error
spawning child processes:
```
[00:00.0] jest args: --e2e --spec --max-workers=36
@ -168,12 +189,14 @@ We run our tests on CircleCI, with our [pre-built Docker image](docker/README.md
at ChildProcess.spawn (internal/child_process.js:394:11)
```
This is likely caused by Jest autodetecting the number of processes on the entire machine (`36`) rather than the number allowed to your container (`2`). To fix this, set `jest --maxWorkers=2` in your test command.
This is likely caused by Jest autodetecting the number of processes on the
entire machine (`36`) rather than the number allowed to your container
(`2`). To fix this, set `jest --maxWorkers=2` in your test command.
### Jenkins
Jenkins supports Docker agents for pipelines. Use the [Playwright Docker image](docker/README.md)
to run tests on Jenkins.
Jenkins supports Docker agents for pipelines. Use the
[Playwright Docker image](docker/README.md) to run tests on Jenkins.
```groovy
pipeline {
@ -191,13 +214,18 @@ pipeline {
### Bitbucket Pipelines
Bitbucket Pipelines can use public [Docker images as build environments](https://confluence.atlassian.com/bitbucket/use-docker-images-as-build-environments-792298897.html). To run Playwright tests on Bitbucket, use our public Docker image ([see Dockerfile](docker/README.md)).
Bitbucket Pipelines can use public
[Docker images as build environments](https://confluence.atlassian.com/bitbucket/use-docker-images-as-build-environments-792298897.html).
To run Playwright tests on Bitbucket, use our public Docker image
([see Dockerfile](docker/README.md)).
```yml
image: mcr.microsoft.com/playwright:bionic
```
While the Docker image supports sandboxing for Chromium, it does not work in the Bitbucket Pipelines environment. To launch Chromium on Bitbucket Pipelines, use the `chromiumSandbox: false` launch argument.
While the Docker image supports sandboxing for Chromium, it does not work in the
Bitbucket Pipelines environment. To launch Chromium on Bitbucket Pipelines, use
the `chromiumSandbox: false` launch argument.
```js
const { chromium } = require('playwright');
@ -206,7 +234,8 @@ const browser = await chromium.launch({ chromiumSandbox: false });
### GitLab CI
To run Playwright tests on GitLab, use our public Docker image ([see Dockerfile](docker/README.md)).
To run Playwright tests on GitLab, use our public Docker image
([see Dockerfile](docker/README.md)).
```yml
stages:
@ -222,39 +251,43 @@ tests:
## Caching browsers
By default, Playwright downloads browser binaries when the Playwright NPM package
is installed. The NPM packages have a `postinstall` hook that downloads the browser
binaries. This behavior can be [customized with environment variables](installation.md).
By default, Playwright downloads browser binaries when the Playwright NPM
package is installed. The NPM packages have a `postinstall` hook that downloads
the browser binaries. This behavior can be
[customized with environment variables](./installation.md).
Caching browsers on CI is **strictly optional**: The `postinstall` hooks should
execute and download the browser binaries on every run.
#### Exception: `node_modules` are cached
Most CI providers cache the [npm-cache](https://docs.npmjs.com/cli-commands/cache.html)
directory (located at `$HOME/.npm`). If your CI pipelines caches the `node_modules`
directory and you run `npm install` (instead of `npm ci`), the default configuration
**will not work**. This is because the `npm install` step will find the Playwright NPM
package on disk and not execute the `postinstall` step.
Most CI providers cache the
[npm-cache](https://docs.npmjs.com/cli-commands/cache.html) directory (located
at `$HOME/.npm`). If your CI pipelines caches the `node_modules` directory and
you run `npm install` (instead of `npm ci`), the default configuration
**will not work**. This is because the `npm install` step will find the
Playwright NPM package on disk and not execute the `postinstall` step.
> Travis CI automatically caches `node_modules` if your repo does not have a
`package-lock.json` file.
`package-lock.json` file.
This behavior can be fixed with one of the following approaches:
1. Move to caching `$HOME/.npm` or the npm-cache directory. (This is the default
behavior in most CI providers.)
1. Set `PLAYWRIGHT_BROWSERS_PATH=0` as the environment variable before running
`npm install`. This will download the browser binaries in the `node_modules`
directory and cache them with the package code. See [installation docs](installation.md).
1. Move to caching `$HOME/.npm` or the npm-cache directory. (This is the
default behavior in most CI providers.)
1. Set `PLAYWRIGHT_BROWSERS_PATH=0` as the environment variable before
running `npm install`. This will download the browser binaries in the
`node_modules` directory and cache them with the package code. See
[installation docs](./installation.md).
1. Use `npm ci` (instead of `npm install`) which forces a clean install: by
removing the existing `node_modules` directory. See [npm docs](https://docs.npmjs.com/cli/ci.html).
removing the existing `node_modules` directory. See
[npm docs](https://docs.npmjs.com/cli/ci.html).
1. Cache the browser binaries, with the steps below.
#### Directories to cache
With the default behavior, Playwright downloads the browser binaries in the following
directories:
With the default behavior, Playwright downloads the browser binaries in the
following directories:
- `%USERPROFILE%\AppData\Local\ms-playwright` on Windows
- `~/Library/Caches/ms-playwright` on MacOS
- `~/.cache/ms-playwright` on Linux
@ -264,7 +297,9 @@ configuration, against a hash of the Playwright version.
## Debugging browser launches
Playwright supports the `DEBUG` environment variable to output debug logs during execution. Setting it to `pw:browser*` is helpful while debugging `Error: Failed to launch browser` errors.
Playwright supports the `DEBUG` environment variable to output debug logs during
execution. Setting it to `pw:browser*` is helpful while debugging `Error: Failed
to launch browser` errors.
```
DEBUG=pw:browser* npm run test
@ -272,7 +307,8 @@ DEBUG=pw:browser* npm run test
## Running headful
By default, Playwright launches browsers in headless mode. This can be changed by passing a flag when the browser is launched.
By default, Playwright launches browsers in headless mode. This can be changed
by passing a flag when the browser is launched.
```js
// Works across chromium, firefox and webkit
@ -280,8 +316,70 @@ const { chromium } = require('playwright');
const browser = await chromium.launch({ headless: false });
```
On Linux agents, headful execution requires [Xvfb](https://en.wikipedia.org/wiki/Xvfb) to be installed. Our [Docker image](docker/README.md) and GitHub Action have Xvfb pre-installed. To run browsers in headful mode with Xvfb, add `xvfb-run` before the Node.js command.
On Linux agents, headful execution requires
[Xvfb](https://en.wikipedia.org/wiki/Xvfb) to be installed. Our
[Docker image](docker/README.md) and GitHub Action have Xvfb pre-installed. To
run browsers in headful mode with Xvfb, add `xvfb-run` before the Node.js
command.
```
xvfb-run node index.js
```
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,6 +1,9 @@
<!-- THIS FILE IS NOW GENERATED -->
# Playwright CLI
Playwright comes with the command line tools that run via `npx` or as a part of the `npm` scripts.
Playwright comes with the command line tools that run via `npx` or as a part of
the `npm` scripts.
<!-- GEN:toc -->
- [Usage](#usage)
@ -26,6 +29,7 @@ $ npx playwright --help
```
Running from `package.json` script
```json
{
"scripts": {
@ -40,13 +44,18 @@ Running from `package.json` script
$ npx playwright codegen wikipedia.org
```
Run `codegen` and perform actions in the browser. Playwright CLI will generate JavaScript code for the user interactions. `codegen` will attempt to generate resilient text-based selectors.
Run `codegen` and perform actions in the browser. Playwright CLI will generate
JavaScript code for the user interactions. `codegen` will attempt to generate
resilient text-based selectors.
<img src="https://user-images.githubusercontent.com/284612/92536033-7e7ebe00-f1ed-11ea-9e1a-7cbd912e3391.gif">
<img
src="https://user-images.githubusercontent.com/284612/92536033-7e7ebe00-f1ed-11ea-9e1a-7cbd912e3391.gif">
## Open pages
With `open`, you can use Playwright bundled browsers to browse web pages. Playwright provides cross-platform WebKit builds that can be used to reproduce Safari rendering across Windows, Linux and macOS.
With `open`, you can use Playwright bundled browsers to browse web pages.
Playwright provides cross-platform WebKit builds that can be used to reproduce
Safari rendering across Windows, Linux and macOS.
```sh
# Open page in Chromium
@ -59,7 +68,9 @@ npx playwright wk example.com
```
### Emulate devices
`open` can emulate mobile and tablet devices ([see all devices](https://github.com/microsoft/playwright/blob/master/src/server/deviceDescriptors.ts)).
`open` can emulate mobile and tablet devices
([see all devices](https://github.com/microsoft/playwright/blob/master/src/server/deviceDescriptors.ts)).
```sh
# Emulate iPhone 11.
@ -67,12 +78,14 @@ npx playwright --device="iPhone 11" open wikipedia.org
```
### Emulate color scheme and viewport size
```sh
# Emulate screen size and color scheme.
npx playwright --viewport-size=800,600 --color-scheme=dark open twitter.com
```
### Emulate geolocation, language and timezone
```sh
# Emulate timezone, language & location
# Once page opens, click the "my location" button to see geolocation in action
@ -80,13 +93,17 @@ npx playwright --timezone="Europe/Rome" --geolocation="41.890221,12.492348" --la
```
## Inspect selectors
During `open` or `codegen`, you can use following API inside the developer tools console of any browser.
<img src="https://user-images.githubusercontent.com/284612/92536317-37dd9380-f1ee-11ea-875d-daf1b206dd56.png">
During `open` or `codegen`, you can use following API inside the developer tools
console of any browser.
<img
src="https://user-images.githubusercontent.com/284612/92536317-37dd9380-f1ee-11ea-875d-daf1b206dd56.png">
#### playwright.$(selector)
Query Playwright selector, using the actual Playwright query engine, for example:
Query Playwright selector, using the actual Playwright query engine, for
example:
```js
> playwright.$('.auth-form >> text=Log in');
@ -106,7 +123,8 @@ Same as `playwright.$`, but returns all matching elements.
#### playwright.inspect(selector)
Reveal element in the Elements panel (if DevTools of the respective browser supports it).
Reveal element in the Elements panel (if DevTools of the respective browser
supports it).
```js
> playwright.inspect('text=Log in')
@ -154,4 +172,63 @@ $ npx playwright pdf https://en.wikipedia.org/wiki/PDF wiki.pdf
```
## Known limitations
Opening WebKit Web Inspector will disconnect Playwright from the browser. In such cases, code generation will stop.
Opening WebKit Web Inspector will disconnect Playwright from the browser. In
such cases, code generation will stop.
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,3 +1,5 @@
<!-- THIS FILE IS NOW GENERATED -->
# Core concepts
Playwright provides a set of APIs to automate Chromium, Firefox and WebKit
@ -5,8 +7,8 @@ browsers. By using the Playwright API, you can write JavaScript code to create
new browser pages, navigate to URLs and then interact with elements on a page.
Along with a test runner Playwright can be used to automate user interactions to
validate and test web applications. The Playwright API enables this through
the following primitives.
validate and test web applications. The Playwright API enables this through the
following primitives.
<!-- GEN:toc-top-level -->
- [Browser](#browser)
@ -22,10 +24,10 @@ the following primitives.
## Browser
A [`Browser`](api.md#class-browser) refers to an instance of Chromium, Firefox
or WebKit. Playwright scripts generally start with launching a browser instance
and end with closing the browser. Browser instances can be launched in headless
(without a GUI) or headful mode.
A [Browser] refers to an instance of Chromium, Firefox or WebKit. Playwright
scripts generally start with launching a browser instance and end with closing
the browser. Browser instances can be launched in headless (without a GUI) or
headful mode.
```js
const { chromium } = require('playwright'); // Or 'firefox' or 'webkit'.
@ -38,16 +40,15 @@ Launching a browser instance can be expensive, and Playwright is designed to
maximize what a single instance can do through multiple browser contexts.
#### API reference
- [class `Browser`](./api.md#class-browser)
- [Browser]
<br/>
## Browser contexts
A [`BrowserContext`](api.md#class-browsercontext) is an isolated incognito-alike
session within a browser instance. Browser contexts are fast and cheap to create.
Browser contexts can be used to parallelize isolated test executions.
A [BrowserContext] is an isolated incognito-alike session within a browser
instance. Browser contexts are fast and cheap to create. Browser contexts can be
used to parallelize isolated test executions.
```js
const browser = await chromium.launch();
@ -71,16 +72,16 @@ const context = await browser.newContext({
```
#### API reference
- [class `BrowserContext`](./api.md#class-browsercontext)
- [BrowserContext]
- [browser.newContext([options])](./api.md#browsernewcontextoptions)
<br/>
## Pages and frames
A Browser context can have multiple pages. A [`Page`](api.md#class-page)
refers to a single tab or a popup window within a browser context. It should be used to navigate to URLs and interact with the page content.
A Browser context can have multiple pages. A [Page] refers to a single tab or a
popup window within a browser context. It should be used to navigate to URLs and
interact with the page content.
```js
// Create a page.
@ -100,11 +101,11 @@ console.log(page.url());
window.location.href = 'https://example.com';
```
> Read more on [page navigation and loading](navigations.md).
> Read more on [page navigation and loading](./navigations.md).
A page can have one or more [Frame](api.md#class-frame) objects attached to
it. Each page has a main frame and page-level interactions (like `click`) are
assumed to operate in the main frame.
A page can have one or more [Frame] objects attached to it. Each page has a main
frame and page-level interactions (like `click`) are assumed to operate in the
main frame.
A page can have additional frames attached with the `iframe` HTML tag. These
frames can be accessed for interactions inside the frame.
@ -125,20 +126,23 @@ await frame.fill('#username-input', 'John');
```
#### API reference
- [class `Page`](./api.md#class-page)
- [class `Frame`](./api.md#class-frame)
- [page.frame()](./api.md#pageframeframeselector)
- [Page]
- [Frame]
- [page.frame(frameSelector)](./api.md#pageframeframeselector)
<br/>
## Selectors
Playwright can search for elements using CSS selectors, XPath selectors, HTML attributes like `id`, `data-test-id` and even text content.
Playwright can search for elements using CSS selectors, XPath selectors, HTML
attributes like `id`, `data-test-id` and even text content.
You can explicitly specify the selector engine you are using or let Playwright detect it.
You can explicitly specify the selector engine you are using or let Playwright
detect it.
All selector engines except for XPath pierce shadow DOM by default. If you want to enforce regular DOM selection, you can use the `*:light` versions of the selectors. You don't typically need to though.
All selector engines except for XPath pierce shadow DOM by default. If you want
to enforce regular DOM selection, you can use the `*:light` versions of the
selectors. You don't typically need to though.
Learn more about selectors and selector engines [here](./selectors.md).
@ -171,7 +175,8 @@ await page.click('xpath=//html/body/div');
await page.click('css:light=div');
```
Selectors using the same or different engines can be combined using the `>>` separator. For example,
Selectors using the same or different engines can be combined using the `>>`
separator. For example,
```js
// Click an element with text 'Sign Up' inside of a #free-month-promo.
@ -187,26 +192,30 @@ const sectionText = await page.$eval('*css=section >> text=Selectors', e => e.te
## Auto-waiting
Actions like `click` and `fill` auto-wait for the element to be visible and [actionable](./actionability.md). For example, click will:
Actions like `click` and `fill` auto-wait for the element to be visible and
[actionable](./actionability.md). For example, click will:
- wait for an element with the given selector to appear in the DOM
- wait for it to become visible: have non-empty bounding box and no `visibility:hidden`
- wait for it to become visible: have non-empty bounding box and no
`visibility:hidden`
- wait for it to stop moving: for example, wait until css transition finishes
- scroll the element into view
- wait for it to receive pointer events at the action point: for example, wait until element becomes non-obscured by other elements
- wait for it to receive pointer events at the action point: for example, wait
until element becomes non-obscured by other elements
- retry if the element is detached during any of the above checks
```js
// Playwright waits for #search element to be in the DOM
await page.fill('#search', 'query');
```
```js
// Playwright waits for element to stop animating
// and accept clicks.
await page.click('#search');
```
You can explicitly wait for an element to appear in the DOM or to become visible:
You can explicitly wait for an element to appear in the DOM or to become
visible:
```js
// Wait for #search to appear in the DOM.
@ -225,26 +234,31 @@ await page.waitForSelector('#promo', { state: 'detached' });
```
#### API reference
- [page.click()](./api.md#pageclickselector-options)
- [page.fill()](./api.md#pagefillselector-value-options)
- [page.waitForSelector()](./api.md#pagewaitforselectorselector-options)
- [page.click(selector[, options])](./api.md#pageclickselector-options)
- [page.fill(selector, value[, options])](./api.md#pagefillselector-value-options)
- [page.waitForSelector(selector[, options])](./api.md#pagewaitforselectorselector-options)
<br/>
## Execution contexts: Node.js and Browser
Playwright scripts run in your Node.js environment. Your page scripts run in the browser page environment. Those environments don't intersect, they are running in different virtual machines in different processes and even potentially on different computers.
Playwright scripts run in your Node.js environment. Your page scripts run in the
browser page environment. Those environments don't intersect, they are running
in different virtual machines in different processes and even potentially on
different computers.
The [`page.evaluate`](https://github.com/microsoft/playwright/blob/master/docs/api.md#pageevaluatepagefunction-arg) API can run a JavaScript function in the context
of the web page and bring results back to the Node.js environment. Browser globals like
`window` and `document` can be used in `evaluate`.
The [page.evaluate(pageFunction[, arg])](./api.md#pageevaluatepagefunction-arg)
API can run a JavaScript function in the context of the web page and bring
results back to the Node.js environment. Browser globals like `window` and
`document` can be used in `evaluate`.
```js
const href = await page.evaluate(() => document.location.href);
```
If the result is a Promise or if the function is asynchronous evaluate will automatically wait until it's resolved:
If the result is a Promise or if the function is asynchronous evaluate will
automatically wait until it's resolved:
```js
const status = await page.evaluate(async () => {
const response = await fetch(location.href);
@ -254,8 +268,11 @@ const status = await page.evaluate(async () => {
### Evaluation
Functions passed inside `page.evaluate` can accept parameters. These parameters are
serialized and sent into your web page over the wire. You can pass primitive types, JSON-alike objects and remote object handles received from the page.
Functions passed inside
[page.evaluate(pageFunction[, arg])](./api.md#pageevaluatepagefunction-arg) can
accept parameters. These parameters are serialized and sent into your web page
over the wire. You can pass primitive types, JSON-alike objects and remote
object handles received from the page.
Right:
@ -278,31 +295,39 @@ const result = await page.evaluate(() => {
```
#### API reference
- [`page.evaluate()`](api.md#pageevaluatepagefunction-arg)
- [`frame.evaluate()`](api.md#frameevaluatepagefunction-arg)
- Evaluation argument [examples](api.md#evaluationargument)
- [page.evaluate(pageFunction[, arg])](./api.md#pageevaluatepagefunction-arg)
- [frame.evaluate(pageFunction[, arg])](./api.md#frameevaluatepagefunction-arg)
- [EvaluationArgument]
<br/>
## Object & Element handles
Playwright can create Node-side handles to the page DOM elements or any other objects inside the page. These handles live in the Node.js process, whereas the actual objects reside in browser.
Playwright can create Node-side handles to the page DOM elements or any other
objects inside the page. These handles live in the Node.js process, whereas the
actual objects reside in browser.
There are two types of handles:
- [`JSHandle`](./api.md#class-jshandle) to reference any JavaScript objects in the page
- [`ElementHandle`](./api.md#class-elementhandle) to reference DOM elements in the page
- [JSHandle] to reference any JavaScript objects in the page
- [ElementHandle] to reference DOM elements in the page
Note that since any DOM element in the page is also a JavaScript object,
Playwright's [`ElementHandle`](./api.md#class-elementhandle) extends
[`JSHandle`](./api.md#class-jshandle).
Playwright's [ElementHandle] extends [JSHandle].
### Handles Lifecycle
- Handles can be acquired using the page methods [`page.evaluateHandle`](./api.md#pageevaluatehandlepagefunction-arg), [`page.$`](./api.md#pageselector) or [`page.$$`](./api.md#pageselector-1) or
their frame counterparts [`frame.evaluateHandle`](./api.md#frameevaluatehandlepagefunction-arg), [`frame.$`](./api.md#frameselector) or [`frame.$$`](./api.md#frameselector-1).
- Once created, handles will retain object from [garbage collection](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management).
- Handles will be **automatically disposed** once the page or frame they belong to navigates or closes.
- Handles can be **manually disposed** using [`jsHandle.dispose`](./api.md#jshandledispose) method.
- Handles can be acquired using the page methods
[page.evaluateHandle(pageFunction[, arg])](./api.md#pageevaluatehandlepagefunction-arg),
[page.$(selector)](./api.md#pageselector) or
[page.$$(selector)](./api.md#pageselector-1) or their frame counterparts
[frame.evaluateHandle(pageFunction[, arg])](./api.md#frameevaluatehandlepagefunction-arg),
[frame.$(selector)](./api.md#frameselector) or
[frame.$$(selector)](./api.md#frameselector-1).
- Once created, handles will retain object from
[garbage collection](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management).
- Handles will be **automatically disposed** once the page or frame they
belong to navigates or closes.
- Handles can be **manually disposed** using
[jsHandle.dispose()](./api.md#jshandledispose) method.
### Example: ElementHandle
@ -312,7 +337,9 @@ const ulElementHandle = await page.$('ul');
await ulElementHandle.evaluate(ulElement => getComputedStyle(ulElement).getPropertyValue('display'));
```
Handles can also be passed as arguments to [`page.evaluate`](./api.md#pageevaluatepagefunction-arg) function:
Handles can also be passed as arguments to
[page.evaluate(pageFunction[, arg])](./api.md#pageevaluatepagefunction-arg)
function:
```js
// In the page API, you can pass handle as a parameter.
@ -350,9 +377,66 @@ await myArrayHandle.dispose();
```
#### API reference
- [class `JSHandle`](./api.md#class-jshandle)
- [class `ElementHandle`](./api.md#class-elementhandle)
- [`page.evaluateHandle()`](./api.md#pageevaluatehandlepagefunction-arg)
- [`page.$()`](./api.md#pageselector)
- [`page.$$()`](./api.md#pageselector-1)
- [`jsHandle.evaluate()`](./api.md#jshandleevaluatepagefunction-arg)
- [JSHandle]
- [ElementHandle]
- [page.evaluateHandle(pageFunction[, arg])](./api.md#pageevaluatehandlepagefunction-arg)
- [page.$(selector)](./api.md#pageselector)
- [page.$$(selector)](./api.md#pageselector-1)
- [jsHandle.evaluate(pageFunction[, arg])](./api.md#jshandleevaluatepagefunction-arg)
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,3 +1,5 @@
<!-- THIS FILE IS NOW GENERATED -->
# Debugging tools
Playwright scripts work with existing debugging tools, like Node.js debuggers
@ -35,7 +37,8 @@ scripts with breakpoints. The debugger can be configured in two ways.
### Use launch config
Setup [`launch.json` configuration](https://code.visualstudio.com/docs/nodejs/nodejs-debugging)
Setup
[`launch.json` configuration](https://code.visualstudio.com/docs/nodejs/nodejs-debugging)
for your Node.js project. Once configured launch the scripts with F5 and use
breakpoints.
@ -43,29 +46,30 @@ breakpoints.
VS Code 1.46+ introduces the new JavaScript debugger behind a feature flag. The
new debugger does not require a `launch.json` configuration. To use this:
1. Enable the preview debugger
* Open JSON settings and add `"debug.javascript.usePreview": true`
* Open settings UI and enable the `Debug JavaScript: Use Preview` setting
* Open JSON settings and add `"debug.javascript.usePreview": true`
* Open settings UI and enable the `Debug JavaScript: Use Preview`
setting
1. Set a breakpoint in VS Code
* Use the `debugger` keyword or set a breakpoint in the VS Code UI
* Use the `debugger` keyword or set a breakpoint in the VS Code UI
1. Run your Node.js script from the terminal
## Browser Developer Tools
You can use browser developer tools in Chromium, Firefox and WebKit while running
a Playwright script. Developer tools help to:
You can use browser developer tools in Chromium, Firefox and WebKit while
running a Playwright script. Developer tools help to:
* Inspect the DOM tree and **find element selectors**
* **See console logs** during execution (or learn how to [read logs via API](verification.md#console-logs))
* **See console logs** during execution (or learn how to
[read logs via API](./verification.md#console-logs))
* Check **network activity** and other developer tools features
<a href="https://user-images.githubusercontent.com/284612/77234134-5f21a500-6b69-11ea-92ec-1c146e1333ec.png"><img src="https://user-images.githubusercontent.com/284612/77234134-5f21a500-6b69-11ea-92ec-1c146e1333ec.png" width="500" alt="Chromium Developer Tools"></a>
<a
href="https://user-images.githubusercontent.com/284612/77234134-5f21a500-6b69-11ea-92ec-1c146e1333ec.png"><img
src="https://user-images.githubusercontent.com/284612/77234134-5f21a500-6b69-11ea-92ec-1c146e1333ec.png"
width="500" alt="Chromium Developer Tools"></a>
> **For WebKit**: Note that launching WebKit Inspector during the execution will
prevent the Playwright script from executing any further.
prevent the Playwright script from executing any further.
### API for Chromium
@ -92,7 +96,6 @@ $ npm run test
### Defaults
With PWDEBUG, the following defaults are configured for you:
* **Run in headful**: With PWDEBUG, browsers always launch in headful mode
* **Disables timeout**: PWDEBUG sets timeout to 0 (= no timeout)
* **Preserve DevTools preferences**: When used with `devtools: true`, PWDEBUG
@ -101,27 +104,34 @@ With PWDEBUG, the following defaults are configured for you:
### Debugging Selectors
PWDEBUG configures a `playwright` object in the browser to highlight
[Playwright selectors](selectors.md). This can be used to verify text or
[Playwright selectors](./selectors.md). This can be used to verify text or
composite selectors. To use this:
1. Setup a breakpoint to pause the execution
1. Open the console panel in browser developer tools
1. Use the `playwright` API
* `playwright.$(selector)`: Highlight the first occurrence of the selector. This reflects
how `page.$` would see the page.
* `playwright.$$(selector)`: Highlight all occurrences of the selector. This reflects
how `page.$$` would see the page.
* `playwright.inspect(selector)`: Inspect the selector in the Elements panel.
* `playwright.clear()`: Clear existing highlights.
* `playwright.$(selector)`: Highlight the first occurrence of the
selector. This reflects how `page.$` would see the page.
* `playwright.$$(selector)`: Highlight all occurrences of the selector.
This reflects how `page.$$` would see the page.
* `playwright.inspect(selector)`: Inspect the selector in the Elements
panel.
* `playwright.clear()`: Clear existing highlights.
<a href="https://user-images.githubusercontent.com/284612/86857345-299abc00-c073-11ea-9e31-02923a9f0d4b.png"><img src="https://user-images.githubusercontent.com/284612/86857345-299abc00-c073-11ea-9e31-02923a9f0d4b.png" width="500" alt="Highlight selectors"></a>
<a
href="https://user-images.githubusercontent.com/284612/86857345-299abc00-c073-11ea-9e31-02923a9f0d4b.png"><img
src="https://user-images.githubusercontent.com/284612/86857345-299abc00-c073-11ea-9e31-02923a9f0d4b.png"
width="500" alt="Highlight selectors"></a>
### Evaluate Source Maps
PWDEBUG also enables source maps for [`page.evaluate` executions](core-concepts.md#evaluation).
This improves the debugging experience for JavaScript executions in the page context.
PWDEBUG also enables source maps for
[`page.evaluate` executions](core-concepts.md#evaluation). This improves the
debugging experience for JavaScript executions in the page context.
<a href="https://user-images.githubusercontent.com/284612/86857568-a6c63100-c073-11ea-82a4-bfd531a4ec87.png"><img src="https://user-images.githubusercontent.com/284612/86857568-a6c63100-c073-11ea-82a4-bfd531a4ec87.png" width="500" alt="Highlight selectors"></a>
<a
href="https://user-images.githubusercontent.com/284612/86857568-a6c63100-c073-11ea-82a4-bfd531a4ec87.png"><img
src="https://user-images.githubusercontent.com/284612/86857568-a6c63100-c073-11ea-82a4-bfd531a4ec87.png"
width="500" alt="Highlight selectors"></a>
## Verbose API logs
@ -135,3 +145,61 @@ $ DEBUG=pw:api npm run test
$ set DEBUG=pw:api
$ npm run test
```
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,12 +1,16 @@
<!-- THIS FILE IS NOW GENERATED -->
# Device and environment emulation
Playwright allows overriding various parameters of the device where the browser is running:
- viewport size, device scale factor, touch support
- locale, timezone
- color scheme
- geolocation
Playwright allows overriding various parameters of the device where the browser
is running:
- viewport size, device scale factor, touch support
- locale, timezone
- color scheme
- geolocation
Most of these parameters are configured during the browser context construction, but some of them such as viewport size can be changed for individual pages.
Most of these parameters are configured during the browser context construction,
but some of them such as viewport size can be changed for individual pages.
<!-- GEN:toc-top-level -->
- [Devices](#devices)
@ -22,7 +26,8 @@ Most of these parameters are configured during the browser context construction,
## Devices
Playwright comes with a registry of device parameters for selected mobile devices. It can be used to simulate browser behavior on a mobile device:
Playwright comes with a registry of device parameters for selected mobile
devices. It can be used to simulate browser behavior on a mobile device:
```js
const { chromium, devices } = require('playwright');
@ -37,9 +42,8 @@ const context = await browser.newContext({
All pages created in the context above will share the same device parameters.
#### API reference
- [`playwright.devices`](./api.md#playwrightdevices)
- [`browser.newContext()`](./api.md#browsernewcontextoptions)
- [playwright.devices](./api.md#playwrightdevices)
- [browser.newContext([options])](./api.md#browsernewcontextoptions)
<br/>
@ -54,8 +58,7 @@ const context = await browser.newContext({
```
#### API reference
- [`browser.newContext()`](./api.md#browsernewcontextoptions)
- [browser.newContext([options])](./api.md#browsernewcontextoptions)
<br/>
@ -80,9 +83,8 @@ const context = await browser.newContext({
```
#### API reference
- [`browser.newContext()`](./api.md#browsernewcontextoptions)
- [`page.setViewportSize()`](./api.md#pagesetviewportsizeviewportsize)
- [browser.newContext([options])](./api.md#browsernewcontextoptions)
- [page.setViewportSize(viewportSize)](./api.md#pagesetviewportsizeviewportsize)
<br/>
@ -97,14 +99,14 @@ const context = await browser.newContext({
```
#### API reference
- [`browser.newContext()`](./api.md#browsernewcontextoptions)
- [browser.newContext([options])](./api.md#browsernewcontextoptions)
<br/>
## Permissions
Allow all pages in the context to show system notifications:
```js
const context = await browser.newContext({
permissions: ['notifications'],
@ -112,37 +114,40 @@ const context = await browser.newContext({
```
Grant all pages in the existing context access to current location:
```js
await context.grantPermissions(['geolocation']);
```
Grant notifications access from a specific domain:
```js
await context.grantPermissions(['notifications'], {origin: 'https://skype.com'} );
```
Revoke all permissions:
```js
await context.clearPermissions();
```
#### API reference
- [`browser.newContext()`](./api.md#browsernewcontextoptions)
- [`browserContext.grantPermissions()`](./api.md#browsercontextgrantpermissionspermissions-options)
- [`browserContext.clearPermissions()`](./api.md#browsercontextclearpermissions)
- [browser.newContext([options])](./api.md#browsernewcontextoptions)
- [browserContext.grantPermissions(permissions[, options])](./api.md#browsercontextgrantpermissionspermissions-options)
- [browserContext.clearPermissions()](./api.md#browsercontextclearpermissions)
<br/>
## Geolocation
Create a context with `"geolocation"` permissions granted:
```js
const context = await browser.newContext({
geolocation: { longitude: 48.858455, latitude: 2.294474 },
permissions: ['geolocation']
});
```
Change the location later:
```js
await context.setGeolocation({ longitude: 29.979097, latitude: 31.134256 });
@ -151,9 +156,8 @@ await context.setGeolocation({ longitude: 29.979097, latitude: 31.134256 });
**Note** you can only change geolocation for all pages in the context.
#### API reference
- [`browser.newContext()`](./api.md#browsernewcontextoptions)
- [`browserContext.setGeolocation()`](./api.md#browsercontextsetgeolocationgeolocation)
- [browser.newContext([options])](./api.md#browsernewcontextoptions)
- [browserContext.setGeolocation(geolocation)](./api.md#browsercontextsetgeolocationgeolocation)
<br/>
@ -181,6 +185,62 @@ await page.emulateMedia({ media: 'print' });
```
#### API reference
- [`browser.newContext()`](./api.md#browsernewcontextoptions)
- [`page.emulateMedia()`](./api.md#pageemulatemediaparams)
- [browser.newContext([options])](./api.md#browsernewcontextoptions)
- [page.emulateMedia(params)](./api.md#pageemulatemediaparams)
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,3 +1,5 @@
<!-- THIS FILE IS NOW GENERATED -->
# Extensibility
<!-- GEN:toc-top-level -->
@ -6,17 +8,29 @@
## Custom selector engines
Playwright supports custom selector engines, registered with [selectors.register(name, script[, options])](api.md#selectorsregistername-script-options).
Playwright supports custom selector engines, registered with
[selectors.register(name, script[, options])](./api.md#selectorsregistername-script-options).
Selector engine should have the following properties:
- `create` function to create a relative selector from `root` (root is either
a `Document`, `ShadowRoot` or `Element`) to a `target` element.
- `query` function to query first element matching `selector` relative to the
`root`.
- `queryAll` function to query all elements matching `selector` relative to
the `root`.
- `create` function to create a relative selector from `root` (root is either a `Document`, `ShadowRoot` or `Element`) to a `target` element.
- `query` function to query first element matching `selector` relative to the `root`.
- `queryAll` function to query all elements matching `selector` relative to the `root`.
By default the engine is run directly in the frame's JavaScript context and, for
example, can call an application-defined function. To isolate the engine from
any JavaScript in the frame, but leave access to the DOM, register the engine
with `{contentScript: true}` option. Content script engine is safer because it
is protected from any tampering with the global objects, for example altering
`Node.prototype` methods. All built-in selector engines run as content scripts.
Note that running as a content script is not guaranteed when the engine is used
together with other custom engines.
By default the engine is run directly in the frame's JavaScript context and, for example, can call an application-defined function. To isolate the engine from any JavaScript in the frame, but leave access to the DOM, register the engine with `{contentScript: true}` option. Content script engine is safer because it is protected from any tampering with the global objects, for example altering `Node.prototype` methods. All built-in selector engines run as content scripts. Note that running as a content script is not guaranteed when the engine is used together with other custom engines.
An example of registering selector engine that queries elements based on a tag
name:
An example of registering selector engine that queries elements based on a tag name:
```js
// Must be a function that evaluates to a selector engine instance.
const createTagNameEngine = () => ({
@ -43,3 +57,61 @@ await page.click('tag=div >> span >> "Click me"');
// We can use it in any methods supporting selectors.
const buttonCount = await page.$$eval('tag=button', buttons => buttons.length);
```
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,3 +1,5 @@
<!-- THIS FILE IS NOW GENERATED -->
# Input
<!-- GEN:toc-top-level -->
@ -15,7 +17,10 @@
## Text input
This is the easiest way to fill out the form fields. It focuses the element and triggers an `input` event with the entered text. It works for `<input>`, `<textarea>`, `[contenteditable]` and `<label>` associated with an input or textarea.
This is the easiest way to fill out the form fields. It focuses the element and
triggers an `input` event with the entered text. It works for `<input>`,
`<textarea>`, `[contenteditable]` and `<label>` associated with an input or
textarea.
```js
// Text input
@ -35,16 +40,17 @@ await page.fill('text=First Name', 'Peter');
```
#### API reference
- [page.fill(selector, value[, options])](./api.md#pagefillselector-value-options) — main frame
- [frame.fill(selector, value[, options])](./api.md#framefillselector-value-options) — given frame
- [elementHandle.fill(value[, options])](./api.md#elementhandlefillvalue-options) — given element
- [page.fill(selector, value[, options])](./api.md#pagefillselector-value-options)
- [frame.fill(selector, value[, options])](./api.md#framefillselector-value-options)
- [elementHandle.fill(value[, options])](./api.md#elementhandlefillvalue-options)
<br/>
## Checkboxes and radio buttons
This is the easiest way to check and uncheck a checkbox or a radio button. This method can be used with `input[type=checkbox]`, `input[type=radio]`, `[role=checkbox]` or `label` associated with checkbox or radio button.
This is the easiest way to check and uncheck a checkbox or a radio button. This
method can be used with `input[type=checkbox]`, `input[type=radio]`,
`[role=checkbox]` or `label` associated with checkbox or radio button.
```js
// Check the checkbox
@ -58,20 +64,20 @@ await page.check('text=XL');
```
#### API reference
- [page.check(selector[, options])](./api.md#pagecheckselector-options) — main frame
- [page.uncheck(selector[, options])](./api.md#pageuncheckselector-options) — main frame
- [frame.check(selector[, options])](./api.md#framecheckselector-options) — given frame
- [frame.uncheck(selector[, options])](./api.md#frameuncheckselector-options) — given frame
- [elementHandle.check(value[, options])](./api.md#elementhandleuncheckoptions) — given element
- [elementHandle.uncheck(value[, options])](./api.md#elementhandleuncheckoptions) — given element
- [page.check(selector[, options])](./api.md#pagecheckselector-options)
- [page.uncheck(selector[, options])](./api.md#pageuncheckselector-options)
- [frame.check(selector[, options])](./api.md#framecheckselector-options)
- [frame.uncheck(selector[, options])](./api.md#frameuncheckselector-options)
- [elementHandle.check([options])](./api.md#elementhandlecheckoptions)
- [elementHandle.uncheck([options])](./api.md#elementhandleuncheckoptions)
<br/>
## Select options
Selects one or multiple options in the `<select>` element.
You can specify option `value`, `label` or `elementHandle` to select. Multiple options can be selected.
Selects one or multiple options in the `<select>` element. You can specify
option `value`, `label` or `elementHandle` to select. Multiple options can be
selected.
```js
// Single selection matching the value
@ -89,10 +95,9 @@ await page.selectOption('select#colors', option);
```
#### API reference
- [page.selectOption(selector, values[, options])](./api.md#pageselectoptionselector-values-options) — main frame
- [frame.selectOption(selector, values[, options])](./api.md#frameselectoptionselector-values-options) — given frame
- [elementHandle.selectOption(values[, options])](./api.md#elementhandleselectoptionvalues-options) — given element
- [page.selectOption(selector, values[, options])](./api.md#pageselectoptionselector-values-options)
- [frame.selectOption(selector, values[, options])](./api.md#frameselectoptionselector-values-options)
- [elementHandle.selectOption(values[, options])](./api.md#elementhandleselectoptionvalues-options)
<br/>
@ -121,17 +126,22 @@ await page.click('#item', { position: { x: 0, y: 0} });
```
Under the hood, this and other pointer-related methods:
- wait for element with given selector to be in DOM
- wait for it to become displayed, i.e. not empty, no `display:none`, no `visibility:hidden`
- wait for it to become displayed, i.e. not empty, no `display:none`, no
`visibility:hidden`
- wait for it to stop moving, for example, until css transition finishes
- scroll the element into view
- wait for it to receive pointer events at the action point, for example, waits until element becomes non-obscured by other elements
- wait for it to receive pointer events at the action point, for example,
waits until element becomes non-obscured by other elements
- retry if the element is detached during any of the above checks
#### Forcing the click
Sometimes, apps use non-trivial logic where hovering the element overlays it with another element that intercepts the click. This behavior is indistinguishable from a bug where element gets covered and the click is dispatched elsewhere. If you know this is taking place, you can bypass the [actionability](./actionability.md) checks and force the click:
Sometimes, apps use non-trivial logic where hovering the element overlays it
with another element that intercepts the click. This behavior is
indistinguishable from a bug where element gets covered and the click is
dispatched elsewhere. If you know this is taking place, you can bypass the
[actionability](./actionability.md) checks and force the click:
```js
await page.click('button#submit', { force: true });
@ -139,49 +149,53 @@ await page.click('button#submit', { force: true });
#### Programmatic click
If you are not interested in testing your app under the real conditions and want to simulate the click by any means possible, you can trigger the [`HTMLElement.click()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click) behavior via simply dispatching a click event on the element:
If you are not interested in testing your app under the real conditions and want
to simulate the click by any means possible, you can trigger the
[`HTMLElement.click()`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/click)
behavior via simply dispatching a click event on the element:
```js
await page.dispatchEvent('button#submit', 'click');
```
#### API reference
- [page.click(selector[, options])](./api.md#pageclickselector-options) — main frame
- [frame.click(selector[, options])](./api.md#frameclickselector-options) — given frame
- [elementHandle.click([options])](./api.md#elementhandleclickoptions) — given element
- [page.dblclick(selector[, options])](./api.md#pagedblclickselector-options) — main frame
- [frame.dblclick(selector[, options])](./api.md#framedblclickselector-options) — given frame
- [elementHandle.dblclick([options])](./api.md#elementhandledblclickoptions) — given element
- [page.hover(selector[, options])](./api.md#pagehoverselector-options) — main frame
- [frame.hover(selector[, options])](./api.md#framehoverselector-options) — given frame
- [elementHandle.hover([options])](./api.md#elementhandlehoveroptions) — given element
- [page.dispatchEvent(selector, type)](./api.md#pagedispatcheventselector-type-eventinit-options) — main frame
- [frame.dispatchEvent(selector, type)](./api.md#framedispatcheventselector-type-eventinit-options) — given frame
- [elementHandle.dispatchEvent(type)](./api.md#elementhandledispatcheventtype-eventinit) — given element
- [page.click(selector[, options])](./api.md#pageclickselector-options)
- [frame.click(selector[, options])](./api.md#frameclickselector-options)
- [elementHandle.click([options])](./api.md#elementhandleclickoptions)
- [page.dblclick(selector[, options])](./api.md#pagedblclickselector-options)
- [frame.dblclick(selector[, options])](./api.md#framedblclickselector-options)
- [elementHandle.dblclick([options])](./api.md#elementhandledblclickoptions)
- [page.hover(selector[, options])](./api.md#pagehoverselector-options)
- [frame.hover(selector[, options])](./api.md#framehoverselector-options)
- [elementHandle.hover([options])](./api.md#elementhandlehoveroptions)
- [page.dispatchEvent(selector, type[, eventInit, options])](./api.md#pagedispatcheventselector-type-eventinit-options)
- [frame.dispatchEvent(selector, type[, eventInit, options])](./api.md#framedispatcheventselector-type-eventinit-options)
- [elementHandle.dispatchEvent(type[, eventInit])](./api.md#elementhandledispatcheventtype-eventinit)
<br/>
## Type characters
Type into the field character by character, as if it was a user with a real keyboard.
Type into the field character by character, as if it was a user with a real
keyboard.
```js
// Type character by character
await page.type('#area', 'Hello World!');
```
This method will emit all the necessary keyboard events, with all the `keydown`, `keyup`, `keypress` events in place. You can even specify the optional `delay` between the key presses to simulate real user behavior.
This method will emit all the necessary keyboard events, with all the `keydown`,
`keyup`, `keypress` events in place. You can even specify the optional `delay`
between the key presses to simulate real user behavior.
> **NOTE** that most of the time, [`page.fill`](#text-input) will just work. You only need to type characters if there is special keyboard handling on the page.
> **NOTE** that most of the time, [`page.fill`](#text-input) will just work. You
only need to type characters if there is special keyboard handling on the page.
#### API reference
- [page.type(selector, text[, options])](./api.md#pagetypeselector-text-options) — main frame
- [frame.type(selector, text[, options])](./api.md#frametypeselector-text-options) — given frame
- [elementHandle.type(text[, options])](./api.md#elementhandletypetext-options) — given element
- [keyboard.type(text[, options])](./api.md#keyboardtypetext-options) — focused element
- [page.type(selector, text[, options])](./api.md#pagetypeselector-text-options)
- [frame.type(selector, text[, options])](./api.md#frametypeselector-text-options)
- [elementHandle.type(text[, options])](./api.md#elementhandletypetext-options)
- [keyboard.type(text[, options])](./api.md#keyboardtypetext-options)
<br/>
@ -198,7 +212,10 @@ await page.press('#name', 'Control+ArrowRight');
await page.press('#value', '$');
```
This method focuses the selected element and produces a single keystroke. It accepts the logical key names that are emitted in the [keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) property of the keyboard events:
This method focuses the selected element and produces a single keystroke. It
accepts the logical key names that are emitted in the
[keyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key)
property of the keyboard events:
```
Backquote, Minus, Equal, Backslash, Backspace, Tab, Delete, Escape,
@ -206,12 +223,13 @@ ArrowDown, End, Enter, Home, Insert, PageDown, PageUp, ArrowRight,
ArrowUp, F1 - F12, Digit0 - Digit9, KeyA - KeyZ, etc.
```
- You can alternatively specify a single character you'd like to produce such as `"a"` or `"#"`.
- Following modification shortcuts are also supported: `Shift, Control, Alt, Meta`.
Simple version produces a single character. This character is case-sensitive, so `"a"` and `"A"` will produce different results.
- You can alternatively specify a single character you'd like to produce such
as `"a"` or `"#"`.
- Following modification shortcuts are also supported: `Shift, Control, Alt,
Meta`.
Simple version produces a single character. This character is case-sensitive, so
`"a"` and `"A"` will produce different results.
```js
// <input id=name>
@ -221,17 +239,19 @@ await page.press('#name', 'Shift+A');
await page.press('#name', 'Shift+ArrowLeft');
```
Shortcuts such as `"Control+o"` or `"Control+Shift+T"` are supported as well. When specified with the modifier, modifier is pressed and being held while the subsequent key is being pressed.
Note that you still need to specify the capital `A` in `Shift-A` to produce the capital character. `Shift-a` produces a lower-case one as if you had the `CapsLock` toggled.
Shortcuts such as `"Control+o"` or `"Control+Shift+T"` are supported as well.
When specified with the modifier, modifier is pressed and being held while the
subsequent key is being pressed.
Note that you still need to specify the capital `A` in `Shift-A` to produce the
capital character. `Shift-a` produces a lower-case one as if you had the
`CapsLock` toggled.
#### API reference
- [page.press(selector, key[, options])](./api.md#pagepressselector-key-options) — main frame
- [frame.press(selector, key[, options])](./api.md#framepressselector-key-options) — given frame
- [elementHandle.press(key[, options])](./api.md#elementhandlepresskey-options) — given element
- [keyboard.press(key[, options])](./api.md#keyboardpresskey-options) — focused element
- [page.press(selector, key[, options])](./api.md#pagepressselector-key-options)
- [frame.press(selector, key[, options])](./api.md#framepressselector-key-options)
- [elementHandle.press(key[, options])](./api.md#elementhandlepresskey-options)
- [keyboard.press(key[, options])](./api.md#keyboardpresskey-options)
<br/>
@ -255,17 +275,23 @@ await page.setInputFiles('input#upload', {
});
```
You can select input files for upload using the `page.setInputFiles` method. It expects first argument to point to an [input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input) with the type `"file"`. Multiple files can be passed in the array. If some of the file paths are relative, they are resolved relative to the [current working directory](https://nodejs.org/api/process.html#process_process_cwd). Empty array clears the selected files.
You can select input files for upload using the `page.setInputFiles` method. It
expects first argument to point to an
[input element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input)
with the type `"file"`. Multiple files can be passed in the array. If some of
the file paths are relative, they are resolved relative to the
[current working directory](https://nodejs.org/api/process.html#process_process_cwd).
Empty array clears the selected files.
#### Example
[This script](examples/upload.js) uploads a file to an `input` element that accepts file uploads.
[This script](examples/upload.js) uploads a file to an `input` element that
accepts file uploads.
#### API reference
- [page.setInputFiles(selector, files[, options])](https://github.com/microsoft/playwright/blob/master/docs/api.md#pagesetinputfilesselector-value-options)
- [frame.setInputFiles(selector, files[, options])](https://github.com/microsoft/playwright/blob/master/docs/api.md#framesetinputfilesselector-value-options)
- [elementHandle.setInputFiles(files[, options])](https://github.com/microsoft/playwright/blob/master/docs/api.md#elementhandlesetinputfilesfiles-options)
- [page.setInputFiles(selector, files[, options])](./api.md#pagesetinputfilesselector-files-options)
- [frame.setInputFiles(selector, files[, options])](./api.md#framesetinputfilesselector-files-options)
- [elementHandle.setInputFiles(files[, options])](./api.md#elementhandlesetinputfilesfiles-options)
<br/>
@ -278,9 +304,65 @@ await page.focus('input#name');
```
#### API reference
- [page.focus(selector, [options])](https://github.com/microsoft/playwright/blob/master/docs/api.md#pagefocusselector-options)
- [frame.focus(selector, [options])](https://github.com/microsoft/playwright/blob/master/docs/api.md#framefocusselector-options)
- [elementHandle.focus([options])](https://github.com/microsoft/playwright/blob/master/docs/api.md#elementhandlefocus-options)
- [page.focus(selector[, options])](./api.md#pagefocusselector-options)
- [frame.focus(selector[, options])](./api.md#framefocusselector-options)
- [elementHandle.focus()](./api.md#elementhandlefocus)
<br/>
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,3 +1,5 @@
<!-- THIS FILE IS NOW GENERATED -->
# Advanced installation
<!-- GEN:toc -->
@ -11,8 +13,9 @@
## Managing browser binaries
Each version of Playwright needs specific versions of browser binaries to operate. By default Playwright downloads Chromium, WebKit and Firefox browsers into the OS-specific cache folders:
Each version of Playwright needs specific versions of browser binaries to
operate. By default Playwright downloads Chromium, WebKit and Firefox browsers
into the OS-specific cache folders:
- `%USERPROFILE%\AppData\Local\ms-playwright` on Windows
- `~/Library/Caches/ms-playwright` on MacOS
- `~/.cache/ms-playwright` on Linux
@ -21,7 +24,8 @@ Each version of Playwright needs specific versions of browser binaries to operat
npm i -D playwright
```
These browsers will take few hundreds of megabytes of the disk space when installed:
These browsers will take few hundreds of megabytes of the disk space when
installed:
```sh
du -hs ./Library/Caches/ms-playwright/*
@ -30,7 +34,8 @@ du -hs ./Library/Caches/ms-playwright/*
180M webkit-XXXX
```
You can override default behavior using environment variables. When installing Playwright, ask it to download browsers into a specific location:
You can override default behavior using environment variables. When installing
Playwright, ask it to download browsers into a specific location:
```sh
# Linux/macOS
@ -41,7 +46,8 @@ $ set PLAYWRIGHT_BROWSERS_PATH=%USERPROFILE%\pw-browsers
$ npm i -D playwright
```
When running Playwright scripts, ask it to search for browsers in a shared location:
When running Playwright scripts, ask it to search for browsers in a shared
location:
```sh
# Linux/macOS
@ -52,7 +58,8 @@ $ set PLAYWRIGHT_BROWSERS_PATH=%USERPROFILE%\pw-browsers
$ node playwright-script.js
```
Or you can opt into the hermetic install and place binaries under the `node_modules/` folder:
Or you can opt into the hermetic install and place binaries under the
`node_modules/` folder:
```sh
# Linux/macOS
@ -63,11 +70,13 @@ $ set PLAYWRIGHT_BROWSERS_PATH=0
$ npm i -D playwright
```
Playwright keeps track of packages that need those browsers and will garbage collect them as you update Playwright to the newer versions.
Playwright keeps track of packages that need those browsers and will garbage
collect them as you update Playwright to the newer versions.
<br>
> **NOTE** Developers can opt-in in this mode via exporting `PLAYWRIGHT_BROWSERS_PATH=$HOME/pw-browsers` in their `.bashrc`.
> **NOTE** Developers can opt-in in this mode via exporting
`PLAYWRIGHT_BROWSERS_PATH=$HOME/pw-browsers` in their `.bashrc`.
<br>
@ -88,8 +97,10 @@ $ set PLAYWRIGHT_DOWNLOAD_HOST=192.168.1.78
$ npm i -D playwright
```
It is also possible to use a per-browser download hosts using `PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST`, `PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST` and `PLAYWRIGHT_WEBKIT_DOWNLOAD_HOST` env variables that
take precedence over `PLAYWRIGHT_DOWNLOAD_HOST`.
It is also possible to use a per-browser download hosts using
`PLAYWRIGHT_CHROMIUM_DOWNLOAD_HOST`, `PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST` and
`PLAYWRIGHT_WEBKIT_DOWNLOAD_HOST` env variables that take precedence over
`PLAYWRIGHT_DOWNLOAD_HOST`.
```sh
# Linux/macOS
@ -103,7 +114,8 @@ $ PLAYWRIGHT_FIREFOX_DOWNLOAD_HOST=192.168.1.1 PLAYWRIGHT_DOWNLOAD_HOST=192.168.
In certain cases, it is desired to avoid browser downloads altogether because
browser binaries are managed separately.
This can be done by setting `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` variable before installation.
This can be done by setting `PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD` variable before
installation.
```sh
# Linux/macOS
@ -144,3 +156,61 @@ const { webkit } = require('playwright-webkit');
// ...
})();
```
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,3 +1,5 @@
<!-- THIS FILE IS NOW GENERATED -->
# Getting Started
<!-- GEN:toc-top-level -->
@ -11,17 +13,21 @@
## Installation
Use npm or Yarn to install Playwright in your Node.js project. See [system requirements](#system-requirements).
Use npm or Yarn to install Playwright in your Node.js project. See
[system requirements](#system-requirements).
```sh
npm i -D playwright
```
This single command downloads the Playwright NPM package and browser binaries for Chromium, Firefox and WebKit. To modify this behavior see [installation parameters](installation.md).
This single command downloads the Playwright NPM package and browser binaries
for Chromium, Firefox and WebKit. To modify this behavior see
[installation parameters](./installation.md).
## Usage
Once installed, you can `require` Playwright in a Node.js script, and launch any of the 3 browsers (`chromium`, `firefox` and `webkit`).
Once installed, you can `require` Playwright in a Node.js script, and launch any
of the 3 browsers (`chromium`, `firefox` and `webkit`).
```js
const { chromium } = require('playwright');
@ -33,7 +39,11 @@ const { chromium } = require('playwright');
})();
```
Playwright APIs are asynchronous and return Promise objects. Our code examples use [the async/await pattern](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await) to ease readability. The code is wrapped in an unnamed async arrow function which is invoking itself.
Playwright APIs are asynchronous and return Promise objects. Our code examples
use
[the async/await pattern](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await)
to ease readability. The code is wrapped in an unnamed async arrow function
which is invoking itself.
```js
(async () => { // Start of async arrow function
@ -44,7 +54,8 @@ Playwright APIs are asynchronous and return Promise objects. Our code examples u
## First script
In our first script, we will navigate to `whatsmyuseragent.org` and take a screenshot in WebKit.
In our first script, we will navigate to `whatsmyuseragent.org` and take a
screenshot in WebKit.
```js
const { webkit } = require('playwright');
@ -58,7 +69,9 @@ const { webkit } = require('playwright');
})();
```
By default, Playwright runs the browsers in headless mode. To see the browser UI, pass the `headless: false` flag while launching the browser. You can also use `slowMo` to slow down execution.
By default, Playwright runs the browsers in headless mode. To see the browser
UI, pass the `headless: false` flag while launching the browser. You can also
use `slowMo` to slow down execution.
```js
firefox.launch({ headless: false, slowMo: 50 });
@ -66,7 +79,8 @@ firefox.launch({ headless: false, slowMo: 50 });
## Record scripts
[Playwright CLI](./cli.md) can be used to record user interactions and generate JavaScript code.
[Playwright CLI](./cli.md) can be used to record user interactions and generate
JavaScript code.
```sh
npx playwright codegen wikipedia.org
@ -74,10 +88,14 @@ npx playwright codegen wikipedia.org
## TypeScript support
Playwright includes built-in support for TypeScript. Type definitions will be imported automatically. It is recommended to use type-checking to improve the IDE experience.
Playwright includes built-in support for TypeScript. Type definitions will be
imported automatically. It is recommended to use type-checking to improve the
IDE experience.
### In JavaScript
Add the following to the top of your JavaScript file to get type-checking in VS Code or WebStorm.
Add the following to the top of your JavaScript file to get type-checking in VS
Code or WebStorm.
```js
//@ts-check
@ -92,7 +110,9 @@ let page;
```
### In TypeScript
TypeScript support will work out-of-the-box. Types can also be imported explicitly.
TypeScript support will work out-of-the-box. Types can also be imported
explicitly.
```ts
let page: import('playwright').Page;
@ -100,13 +120,70 @@ let page: import('playwright').Page;
## System requirements
Playwright requires Node.js version 10.17 or above. The browser binaries for Chromium,
Firefox and WebKit work across the 3 platforms (Windows, macOS, Linux):
Playwright requires Node.js version 10.17 or above. The browser binaries for
Chromium, Firefox and WebKit work across the 3 platforms (Windows, macOS,
Linux):
* **Windows**: Works with Windows and Windows Subsystem for Linux (WSL).
* **macOS**: Requires 10.14 or above.
* **Linux**: Depending on your Linux distribution, you might need to install additional
dependencies to run the browsers.
* **Linux**: Depending on your Linux distribution, you might need to install
additional dependencies to run the browsers.
* Firefox requires Ubuntu 18.04+
* For Ubuntu 18.04, the additional dependencies are defined in [our Docker image](docker/Dockerfile.bionic),
which is based on Ubuntu.
* For Ubuntu 18.04, the additional dependencies are defined in
[our Docker image](docker/Dockerfile.bionic), which is based on Ubuntu.
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,3 +1,5 @@
<!-- THIS FILE IS NOW GENERATED -->
# Supported languages
The Playwright API is available in multiple languages.
@ -53,3 +55,60 @@ go get github.com/mxschmitt/playwright-go
* [GitHub repo](https://github.com/mxschmitt/playwright-go)
* [Documentation](https://pkg.go.dev/github.com/mxschmitt/playwright-go?tab=doc)
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,15 +1,22 @@
<!-- THIS FILE IS NOW GENERATED -->
# Experimental support for Chrome for Android
You can try Playwright against Chrome for Android today. This support is experimental. Support for devices is tracked in the issue [#1122](https://github.com/microsoft/playwright/issues/1122).
You can try Playwright against Chrome for Android today. This support is
experimental. Support for devices is tracked in the issue
[#1122](https://github.com/microsoft/playwright/issues/1122).
## Requirements
- [Playwright 1.6](https://www.npmjs.com/package/playwright) or newer
- [ADB daemon](https://developer.android.com/studio/command-line/adb) running and authenticated with your device.
- [`Chrome 87`](https://play.google.com/store/apps/details?id=com.android.chrome) or newer installed on the device
- [ADB daemon](https://developer.android.com/studio/command-line/adb) running
and authenticated with your device.
- [`Chrome 87`](https://play.google.com/store/apps/details?id=com.android.chrome)
or newer installed on the device
- "Enable command line on non-rooted devices" enabled in `chrome://flags`.
> Playwright will be looking for ADB daemon on the default port `5037`. It will use the first device available. Typically running `adb devices` is all you need to do.
> Playwright will be looking for ADB daemon on the default port `5037`. It
will use the first device available. Typically running `adb devices` is all
you need to do.
## How to run
@ -28,11 +35,73 @@ const { _clank } = require('playwright');
})();
```
> [Clank](https://chromium.googlesource.com/chromium/src/+/master/docs/memory/android_dev_tips.md) is a code name for Chrome for Android.
>
[Clank](https://chromium.googlesource.com/chromium/src/+/master/docs/memory/android_dev_tips.md)
is a code name for Chrome for Android.
## Known limitations
- Raw USB operation is not yet supported, so you need ADB.
- Only `launchPersistentContext` works, launching ephemeral contexts is not supported.
- Passing `viewport: null` is necessary to make sure resolution is not emulated.
- Device needs to be awake to produce screenshots. Enabling "Stay awake" developer mode will help.
- Only `launchPersistentContext` works, launching ephemeral contexts is not
supported.
- Passing `viewport: null` is necessary to make sure resolution is not
emulated.
- Device needs to be awake to produce screenshots. Enabling "Stay awake"
developer mode will help.
- We didn't run all the tests against the device, so not everything works.
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,7 +1,9 @@
<!-- THIS FILE IS NOW GENERATED -->
# Multi-page scenarios
Playwright can automate scenarios that span multiple browser contexts or multiple
tabs in a browser window.
Playwright can automate scenarios that span multiple browser contexts or
multiple tabs in a browser window.
<!-- GEN:toc-top-level -->
- [Multiple contexts](#multiple-contexts)
@ -14,8 +16,8 @@ tabs in a browser window.
[Browser contexts](core-concepts.md#browser-contexts) are isolated environments
on a single browser instance. Playwright can create multiple browser contexts
within a single scenario. This is useful when you want to test for
multi-user functionality, like chat.
within a single scenario. This is useful when you want to test for multi-user
functionality, like chat.
```js
const { chromium } = require('playwright');
@ -33,17 +35,15 @@ await adminContext.addCookies(adminCookies);
```
#### API reference
- [class `BrowserContext`](./api.md#class-browsercontext)
- [`browser.newContext([options])`](./api.md#browsernewcontextoptions)
- [`browserContext.addCookies(cookies)`](api.md#browsercontextaddcookiescookies)
- [BrowserContext]
- [browser.newContext([options])](./api.md#browsernewcontextoptions)
- [browserContext.addCookies(cookies)](./api.md#browsercontextaddcookiescookies)
## Multiple pages
Each browser context can host multiple pages (tabs).
* Each page behaves like a focused, active page. Bringing the page to front
is not required.
* Each page behaves like a focused, active page. Bringing the page to front is
not required.
* Pages inside a context respect context-level emulation, like viewport sizes,
custom network routes or browser locale.
@ -57,10 +57,9 @@ const allPages = context.pages();
```
#### API reference
- [class `Page`](./api.md#class-page)
- [`browserContext.newPage()`](./api.md#browsercontextnewpage)
- [`browserContext.pages()`](./api.md#browsercontextpages)
- [Page]
- [browserContext.newPage()](./api.md#browsercontextnewpage)
- [browserContext.pages()](./api.md#browsercontextpages)
## Handling new pages
@ -78,7 +77,8 @@ await newPage.waitForLoadState();
console.log(await newPage.title());
```
If the action that triggers the new page is unknown, the following pattern can be used.
If the action that triggers the new page is unknown, the following pattern can
be used.
```js
// Get all new pages (including popups) in the context
@ -89,7 +89,6 @@ context.on('page', async page => {
```
#### API reference
- [browserContext.on('page')](./api.md#browsercontextonpage)
## Handling popups
@ -110,7 +109,8 @@ await popup.waitForLoadState();
await popup.title();
```
If the action that triggers the popup is unknown, the following pattern can be used.
If the action that triggers the popup is unknown, the following pattern can be
used.
```js
// Get all popups when they open
@ -121,5 +121,61 @@ page.on('popup', async popup => {
```
#### API reference
- [page.on('popup')](./api.md#pageonpopup)
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,6 +1,10 @@
<!-- THIS FILE IS NOW GENERATED -->
# Navigations
Playwright can navigate to URLs and handle navigations caused by page interactions. This guide covers common scenarios to wait for page navigations and loading to complete.
Playwright can navigate to URLs and handle navigations caused by page
interactions. This guide covers common scenarios to wait for page navigations
and loading to complete.
<!-- GEN:toc-top-level -->
- [Navigation lifecycle](#navigation-lifecycle)
@ -10,25 +14,38 @@ Playwright can navigate to URLs and handle navigations caused by page interactio
<!-- GEN:stop -->
## Navigation lifecycle
Playwright splits the process of showing a new document in a page into **navigation** and **loading**.
**Navigations** can be initiated by changing the page URL or by interacting with the page (e.g., clicking a link). Navigation ends when response headers have been parsed and session history is updated. The navigation intent may be canceled, for example, on hitting an unresolved DNS address or transformed into a file download. Only after the navigation succeeds, page starts **loading** the document.
Playwright splits the process of showing a new document in a page into
**navigation** and **loading**.
**Loading** covers getting the remaining response body over the network, parsing, executing the scripts and firing load events:
**Navigations** can be initiated by changing the page URL or by interacting with
the page (e.g., clicking a link). Navigation ends when response headers have
been parsed and session history is updated. The navigation intent may be
canceled, for example, on hitting an unresolved DNS address or transformed into
a file download. Only after the navigation succeeds, page starts **loading** the
document.
- [`page.url()`](api.md#pageurl) is set to the new url
**Loading** covers getting the remaining response body over the network,
parsing, executing the scripts and firing load events:
- [page.url()](./api.md#pageurl) is set to the new url
- document content is loaded over network and parsed
- [`domcontentloaded`](api.md#pageondomcontentloaded) event is fired
- [page.on('domcontentloaded')](./api.md#pageondomcontentloaded) event is
fired
- page executes some scripts and loads resources like stylesheets and images
- [`load`](api.md#pageonload) event is fired
- [page.on('load')](./api.md#pageonload) event is fired
- page executes dynamically loaded scripts
- `networkidle` is fired when no new network requests are made for 500 ms
## Scenarios initiated by browser UI
Navigations can be initiated by changing the URL bar, reloading the page or going back or forward in session history.
Navigations can be initiated by changing the URL bar, reloading the page or
going back or forward in session history.
### Auto-wait
Navigating to a URL auto-waits for the page to fire the `load` event. If the page does a client-side redirect before `load`, `page.goto` will auto-wait for the redirected page to fire the `load` event.
Navigating to a URL auto-waits for the page to fire the `load` event. If the
page does a client-side redirect before `load`, `page.goto` will auto-wait for
the redirected page to fire the `load` event.
```js
// Navigate the page
@ -36,7 +53,9 @@ await page.goto('https://example.com');
```
### Custom wait
Override the default behavior to wait until a specific event, like `networkidle`.
Override the default behavior to wait until a specific event, like
`networkidle`.
```js
// Navigate and wait until network is idle
@ -44,7 +63,12 @@ await page.goto('https://example.com', { waitUntil: 'networkidle' });
```
### Wait for element
In lazy-loaded pages, it can be useful to wait until an element is visible with [`page.waitForSelector`](./api.md#pagewaitforselectorselector-options). Alternatively, page interactions like [`page.click`](./api.md#pageclickselector-options) auto-wait for elements.
In lazy-loaded pages, it can be useful to wait until an element is visible with
[page.waitForSelector(selector[, options])](./api.md#pagewaitforselectorselector-options).
Alternatively, page interactions like
[page.click(selector[, options])](./api.md#pageclickselector-options) auto-wait
for elements.
```js
// Navigate and wait for element
@ -58,16 +82,21 @@ await page.click('text=Example Domain');
```
#### API reference
- [`page.goto(url[, options])`](./api.md#pagegotourl-options)
- [`page.reload([options])`](./api.md#pagereloadoptions)
- [`page.goBack([options])`](./api.md#pagegobackoptions)
- [`page.goForward([options])`](./api.md#pagegoforwardoptions)
- [page.goto(url[, options])](./api.md#pagegotourl-options)
- [page.reload([options])](./api.md#pagereloadoptions)
- [page.goBack([options])](./api.md#pagegobackoptions)
- [page.goForward([options])](./api.md#pagegoforwardoptions)
## Scenarios initiated by page interaction
In the scenarios below, `page.click` initiates a navigation and then waits for the navigation to complete.
In the scenarios below, `page.click` initiates a navigation and then waits for
the navigation to complete.
### Auto-wait
By default, `page.click` will wait for the navigation step to complete. This can be combined with a page interaction on the navigated page which would auto-wait for an element.
By default, `page.click` will wait for the navigation step to complete. This can
be combined with a page interaction on the navigated page which would auto-wait
for an element.
```js
// Click will auto-wait for navigation to complete
@ -77,7 +106,10 @@ await page.fill('#username', 'John Doe');
```
### Custom wait
`page.click` can be combined with [`page.waitForLoadState`](./api.md#pagewaitforloadstatestate-options) to wait for a loading event.
`page.click` can be combined with
[page.waitForLoadState([state, options])](./api.md#pagewaitforloadstatestate-options)
to wait for a loading event.
```js
await page.click('button'); // Click triggers navigation
@ -85,7 +117,12 @@ await page.waitForLoadState('networkidle'); // This resolves after 'networkidle'
```
### Wait for element
In lazy-loaded pages, it can be useful to wait until an element is visible with [`page.waitForSelector`](./api.md#pagewaitforselectorselector-options). Alternatively, page interactions like [`page.click`](./api.md#pageclickselector-options) auto-wait for elements.
In lazy-loaded pages, it can be useful to wait until an element is visible with
[page.waitForSelector(selector[, options])](./api.md#pagewaitforselectorselector-options).
Alternatively, page interactions like
[page.click(selector[, options])](./api.md#pageclickselector-options) auto-wait
for elements.
```js
// Click triggers navigation
@ -100,7 +137,11 @@ await page.fill('#username', 'John Doe');
```
### Asynchronous navigation
Clicking an element could trigger asychronous processing before initiating the navigation. In these cases, it is recommended to explicitly call [`page.waitForNavigation`](api.md#pagewaitfornavigationoptions). For example:
Clicking an element could trigger asychronous processing before initiating the
navigation. In these cases, it is recommended to explicitly call
[page.waitForNavigation([options])](./api.md#pagewaitfornavigationoptions). For
example:
* Navigation is triggered from a `setTimeout`
* Page waits for network requests before navigation
@ -111,10 +152,15 @@ await Promise.all([
]);
```
The `Promise.all` pattern prevents a race condition between `page.click` and `page.waitForNavigation` when navigation happens quickly.
The `Promise.all` pattern prevents a race condition between `page.click` and
`page.waitForNavigation` when navigation happens quickly.
### Multiple navigations
Clicking an element could trigger multiple navigations. In these cases, it is recommended to explicitly [`page.waitForNavigation`](api.md#pagewaitfornavigationoptions) to a specific url. For example:
Clicking an element could trigger multiple navigations. In these cases, it is
recommended to explicitly
[page.waitForNavigation([options])](./api.md#pagewaitfornavigationoptions) to a
specific url. For example:
* Client-side redirects issued after the `load` event
* Multiple pushes to history state
@ -125,10 +171,14 @@ await Promise.all([
]);
```
The `Promise.all` pattern prevents a race condition between `page.click` and `page.waitForNavigation` when navigation happens quickly.
The `Promise.all` pattern prevents a race condition between `page.click` and
`page.waitForNavigation` when navigation happens quickly.
### Loading a popup
When popup is opened, explicitly calling [`page.waitForLoadState`](api.md#pagewaitforloadstatestate-options) ensures that popup is loaded to the desired state.
When popup is opened, explicitly calling
[page.waitForLoadState([state, options])](./api.md#pagewaitforloadstatestate-options)
ensures that popup is loaded to the desired state.
```js
const [ popup ] = await Promise.all([
@ -139,14 +189,17 @@ await popup.waitForLoadState('load');
```
#### API reference
- [`page.click(selector[, options])`](./api.md#pageclickselector-options)
- [`page.waitForLoadState([state[, options]])`](./api.md#pagewaitforloadstatestate-options)
- [`page.waitForSelector(selector[, options])`](./api.md#pagewaitforselectorselector-options)
- [`page.waitForNavigation([options])`](./api.md#pagewaitfornavigationoptions)
- [`page.waitForFunction(pageFunction[, arg, options])`](./api.md#pagewaitforfunctionpagefunction-arg-options)
- [page.click(selector[, options])](./api.md#pageclickselector-options)
- [page.waitForLoadState([state, options])](./api.md#pagewaitforloadstatestate-options)
- [page.waitForSelector(selector[, options])](./api.md#pagewaitforselectorselector-options)
- [page.waitForNavigation([options])](./api.md#pagewaitfornavigationoptions)
- [page.waitForFunction(pageFunction[, arg, options])](./api.md#pagewaitforfunctionpagefunction-arg-options)
## Advanced patterns
For pages that have complicated loading patterns, [`page.waitForFunction`](./api.md#pagewaitforfunctionpagefunction-arg-options) is a powerful and extensible approach to define a custom wait criteria.
For pages that have complicated loading patterns,
[page.waitForFunction(pageFunction[, arg, options])](./api.md#pagewaitforfunctionpagefunction-arg-options)
is a powerful and extensible approach to define a custom wait criteria.
```js
await page.goto('http://example.com');
@ -156,4 +209,61 @@ await page.screenshot();
```
#### API reference
- [`page.waitForFunction(pageFunction[, arg, options])`](./api.md#pagewaitforfunctionpagefunction-arg-options)
- [page.waitForFunction(pageFunction[, arg, options])](./api.md#pagewaitforfunctionpagefunction-arg-options)
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,8 +1,12 @@
<!-- THIS FILE IS NOW GENERATED -->
# Network
Playwright provides APIs to **monitor** and **modify** network traffic, both HTTP and HTTPS.
Any requests that page does, including [XHRs](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) and
[fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) requests, can be tracked, modified and handled.
Playwright provides APIs to **monitor** and **modify** network traffic, both
HTTP and HTTPS. Any requests that page does, including
[XHRs](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) and
[fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) requests,
can be tracked, modified and handled.
<!-- GEN:toc-top-level -->
- [HTTP Authentication](#http-authentication)
@ -29,7 +33,6 @@ await page.goto('https://example.com');
```
#### API reference
- [browser.newContext([options])](./api.md#browsernewcontextoptions)
<br/>
@ -44,7 +47,11 @@ const [ download ] = await Promise.all([
const path = await download.path();
```
For every attachment downloaded by the page, [`"download"`](https://github.com/microsoft/playwright/blob/master/docs/api.md#event-download) event is emitted. If you create a browser context with the `acceptDownloads: true`, all these attachments are going to be downloaded into a temporary folder. You can obtain the download url, file system path and payload stream using the [`Download`](https://github.com/microsoft/playwright/blob/master/docs/api.md#class-download) object from the event.
For every attachment downloaded by the page,
[page.on('download')](./api.md#pageondownload) event is emitted. If you create a
browser context with the `acceptDownloads: true`, all these attachments are
going to be downloaded into a temporary folder. You can obtain the download url,
file system path and payload stream using the [Download] object from the event.
#### Variations
@ -54,13 +61,14 @@ If you have no idea what initiates the download, you can still handle the event:
page.on('download', download => download.path().then(console.log));
```
Note that handling the event forks the control flow and makes script harder to follow. Your scenario might end while you are downloading a file since your main control flow is not awaiting for this operation to resolve.
Note that handling the event forks the control flow and makes script harder to
follow. Your scenario might end while you are downloading a file since your main
control flow is not awaiting for this operation to resolve.
#### API reference
- [Download](https://github.com/microsoft/playwright/blob/master/docs/api.md#class-download)
- [page.on('download')](https://github.com/microsoft/playwright/blob/master/docs/api.md#event-download)
- [page.waitForEvent(event)](https://github.com/microsoft/playwright/blob/master/docs/api.md##pagewaitforeventevent-optionsorpredicate)
- [Download]
- [page.on('download')](./api.md#pageondownload)
- [page.waitForEvent(event[, optionsOrPredicate])](./api.md#pagewaitforeventevent-optionsorpredicate)
<br/>
@ -113,9 +121,8 @@ const [response] = await Promise.all([
```
#### API reference
- [class `Request`](./api.md#class-request)
- [class `Response`](./api.md#class-response)
- [Request]
- [Response]
- [page.on('request')](./api.md#pageonrequest)
- [page.on('response')](./api.md#pageonresponse)
- [page.waitForRequest(urlOrPredicate[, options])](./api.md#pagewaitforrequesturlorpredicate-options)
@ -133,7 +140,8 @@ await page.route('**/api/fetch_data', route => route.fulfill({
await page.goto('https://example.com');
```
You can mock API endpoints via handling the network quests in your Playwright script.
You can mock API endpoints via handling the network quests in your Playwright
script.
#### Variations
@ -149,12 +157,11 @@ await page.goto('https://example.com');
```
#### API reference
- [browserContext.route(url, handler)](./api.md#browsercontextrouteurl-handler)
- [browserContext.unroute(url[, handler])](./api.md#browsercontextunrouteurl-handler)
- [page.route(url, handler)](./api.md#pagerouteurl-handler)
- [page.unroute(url[, handler])](./api.md#pageunrouteurl-handler)
- [Route](./api.md#class-route)
- [Route]
<br/>
@ -172,7 +179,8 @@ await page.route('**/*', route => {
await page.route('**/*', route => route.continue({method: 'POST'}));
```
You can continue requests with modifications. Example above removes an HTTP header from the outgoing requests.
You can continue requests with modifications. Example above removes an HTTP
header from the outgoing requests.
## Abort requests
@ -187,9 +195,65 @@ await page.route('**/*', route => {
```
#### API reference
- [page.route(url, handler)](./api.md#pagerouteurl-handler)
- [browserContext.route(url, handler)](./api.md#browsercontextrouteurl-handler)
- [route.abort([errorCode])](./api.md#routeaborterrorcode)
<br/>
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,6 +1,10 @@
<!-- THIS FILE IS NOW GENERATED -->
# Page Object Models
Large test suites can be structured to optimize ease of authoring and maintenance.
Page object models are one such approach to structure your test suite.
Large test suites can be structured to optimize ease of authoring and
maintenance. Page object models are one such approach to structure your test
suite.
<!-- GEN:toc-top-level -->
- [Introduction](#introduction)
@ -8,6 +12,7 @@ Page object models are one such approach to structure your test suite.
<!-- GEN:stop -->
## Introduction
A page object represents a part of your web application. An e-commerce web
application might have a home page, a listings page and a checkout page. Each of
them can be represented by page object models.
@ -15,11 +20,12 @@ them can be represented by page object models.
Page objects **simplify authoring**. They create a higher-level API which suits
your application.
Page objects **simplify maintenance**. They capture element selectors in one place
and create reusable code to avoid repetition.
Page objects **simplify maintenance**. They capture element selectors in one
place and create reusable code to avoid repetition.
## Implementation
Page object models wrap over a Playwright [`page`](./api.md#class-page).
Page object models wrap over a Playwright [Page].
```js
// models/Search.js
@ -52,4 +58,61 @@ await searchPage.search('search query');
```
### API reference
- [class `Page`](./api.md#class-page)
- [Page]
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,6 +1,12 @@
<!-- THIS FILE IS NOW GENERATED -->
# Element selectors
Selectors query elements on the web page for interactions, like [page.click](api.md#pageclickselector-options), and to obtain `ElementHandle` through [page.$](api.md#pageselector). Built-in selectors auto-pierce [shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
Selectors query elements on the web page for interactions, like
[page.click(selector[, options])](./api.md#pageclickselector-options), and to
obtain `ElementHandle` through [page.$(selector)](./api.md#pageselector).
Built-in selectors auto-pierce
[shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
<!-- GEN:toc-top-level -->
- [Syntax](#syntax)
@ -10,34 +16,44 @@ Selectors query elements on the web page for interactions, like [page.click](api
<!-- GEN:stop -->
## Syntax
Selectors are defined by selector engine name and selector body, `engine=body`.
Selectors are defined by selector engine name and selector body, `engine=body`.
* `engine` refers to one of the [supported engines](#selector-engines)
* Built-in selector engines: [css], [text], [xpath] and [id selectors][id]
* Learn more about [custom selector engines](./extensibility.md)
* `body` refers to the query string for the respective engine
* For `text`, body is the text content
* For `css`, body is a [css selector](https://developer.mozilla.org/en/docs/Web/CSS/CSS_Selectors)
* For `css`, body is a
[css selector](https://developer.mozilla.org/en/docs/Web/CSS/CSS_Selectors)
Body format is assumed to ignore leading and trailing white spaces, so that extra whitespace can be added for readability.
Body format is assumed to ignore leading and trailing white spaces, so that
extra whitespace can be added for readability.
### Short-forms
For convenience, common selectors have short-forms:
- Selector starting with `//` or `..` is assumed to be `xpath=selector`
- Example: `page.click('//html')` is converted to `page.click('xpath=//html')`.
- Selector starting and ending with a quote (either `"` or `'`) is assumed to be `text=selector`
- Example: `page.click('"foo"')` is converted to `page.click('text="foo"')`.
- Example: `page.click('//html')` is converted to
`page.click('xpath=//html')`.
- Selector starting and ending with a quote (either `"` or `'`) is assumed to
be `text=selector`
- Example: `page.click('"foo"')` is converted to
`page.click('text="foo"')`.
- Otherwise, selector is assumed to be `css=selector`
- Example: `page.click('div')` is converted to `page.click('css=div')`.
### Chaining selectors
Selectors defined as `engine=body` or in short-form can be combined with the `>>` token, e.g. `selector1 >> selector2 >> selectors3`. When selectors are chained, next one is queried relative to the previous one's result.
Selectors defined as `engine=body` or in short-form can be combined with the
`>>` token, e.g. `selector1 >> selector2 >> selectors3`. When selectors are
chained, next one is queried relative to the previous one's result.
For example,
```
css=article >> css=.bar > .baz >> css=span[attr=value]
```
is equivalent to
```js
document
.querySelector('article')
@ -45,18 +61,30 @@ document
.querySelector('span[attr=value]')
```
If a selector needs to include `>>` in the body, it should be escaped inside a string to not be confused with chaining separator, e.g. `text="some >> text"`.
If a selector needs to include `>>` in the body, it should be escaped inside a
string to not be confused with chaining separator, e.g. `text="some >> text"`.
### Intermediate matches
By default, chained selectors resolve to an element queried by the last selector. A selector can be prefixed with `*` to capture elements that are queried by an intermediate selector.
For example, `css=article >> text=Hello` captures the element with the text `Hello`, and `*css=article >> text=Hello` (note the `*`) captures the `article` element that contains some element with the text `Hello`.
By default, chained selectors resolve to an element queried by the last
selector. A selector can be prefixed with `*` to capture elements that are
queried by an intermediate selector.
For example, `css=article >> text=Hello` captures the element with the text
`Hello`, and `*css=article >> text=Hello` (note the `*`) captures the `article`
element that contains some element with the text `Hello`.
## Best practices
The choice of selectors determines the resiliency of automation scripts. To reduce the maintenance burden, we recommend prioritizing user-facing attributes and explicit contracts.
The choice of selectors determines the resiliency of automation scripts. To
reduce the maintenance burden, we recommend prioritizing user-facing attributes
and explicit contracts.
### Prioritize user-facing attributes
Attributes like text content, input placeholder, accessibility roles and labels are user-facing attributes that change rarely. These attributes are not impacted by DOM structure changes.
Attributes like text content, input placeholder, accessibility roles and labels
are user-facing attributes that change rarely. These attributes are not impacted
by DOM structure changes.
The following examples use the built-in [text] and [css] selector engines.
@ -79,7 +107,9 @@ await page.click('css=nav >> text=Login');
### Define explicit contract
When user-facing attributes change frequently, it is recommended to use explicit test ids, like `data-test-id`. These `data-*` attributes are supported by the [css] and [id selectors][id].
When user-facing attributes change frequently, it is recommended to use explicit
test ids, like `data-test-id`. These `data-*` attributes are supported by the
[css] and [id selectors][id].
```html
<button data-test-id="directions">Itinéraire</button>
@ -95,7 +125,9 @@ await page.click('data-test-id=directions');
```
### Avoid selectors tied to implementation
[xpath] and [css] can be tied to the DOM structure or implementation. These selectors can break when the DOM structure changes.
[xpath] and [css] can be tied to the DOM structure or implementation. These
selectors can break when the DOM structure changes.
```js
// avoid long css or xpath chains
@ -132,19 +164,35 @@ const handle = await divHandle.$('css=span');
```
## Selector engines
### css and css:light
`css` is a default engine - any malformed selector not starting with `//` nor starting and ending with a quote is assumed to be a css selector. For example, Playwright converts `page.$('span > button')` to `page.$('css=span > button')`.
`css` is a default engine - any malformed selector not starting with `//` nor
starting and ending with a quote is assumed to be a css selector. For example,
Playwright converts `page.$('span > button')` to `page.$('css=span > button')`.
Playwright augments standard CSS selectors in two ways, see below for more details:
Playwright augments standard CSS selectors in two ways, see below for more
details:
* `css` engine pierces open shadow DOM by default.
* Playwright adds a few custom pseudo-classes like `:visible`.
#### Shadow piercing
`css:light` engine is equivalent to [`Document.querySelector`](https://developer.mozilla.org/en/docs/Web/API/Document/querySelector) and behaves according to the CSS spec. However, it does not pierce shadow roots, which may be inconvenient when working with [Shadow DOM and Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM). For that reason, `css` engine pierces shadow roots. More specifically, any [Descendant combinator](https://developer.mozilla.org/en-US/docs/Web/CSS/Descendant_combinator) or [Child combinator](https://developer.mozilla.org/en-US/docs/Web/CSS/Child_combinator) pierces an arbitrary number of open shadow roots, including the implicit descendant combinator at the start of the selector.
`css:light` engine is equivalent to
[`Document.querySelector`](https://developer.mozilla.org/en/docs/Web/API/Document/querySelector)
and behaves according to the CSS spec. However, it does not pierce shadow roots,
which may be inconvenient when working with
[Shadow DOM and Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM).
For that reason, `css` engine pierces shadow roots. More specifically, any
[Descendant combinator](https://developer.mozilla.org/en-US/docs/Web/CSS/Descendant_combinator)
or
[Child combinator](https://developer.mozilla.org/en-US/docs/Web/CSS/Child_combinator)
pierces an arbitrary number of open shadow roots, including the implicit
descendant combinator at the start of the selector.
`css` engine first searches for elements in the light dom in the iteration order, and then recursively inside open shadow roots in the iteration order. It does not search inside closed shadow roots or iframes.
`css` engine first searches for elements in the light dom in the iteration
order, and then recursively inside open shadow roots in the iteration order. It
does not search inside closed shadow roots or iframes.
```html
<article>
@ -164,20 +212,32 @@ Playwright augments standard CSS selectors in two ways, see below for more detai
</article>
```
Note that `<open mode shadow root>` is not an html element, but rather a shadow root created with `element.attachShadow({mode: 'open'})`.
- Both `"css=article div"` and `"css:light=article div"` match the first `<div>In the light dom</div>`.
- Both `"css=article > div"` and `"css:light=article > div"` match two `div` elements that are direct children of the `article`.
- `"css=article .in-the-shadow"` matches the `<div class='in-the-shadow'>`, piercing the shadow root, while `"css:light=article .in-the-shadow"` does not match anything.
- `"css:light=article div > span"` does not match anything, because both light-dom `div` elements do not contain a `span`.
- `"css=article div > span"` matches the `<span class='content'>`, piercing the shadow root.
- `"css=article > .in-the-shadow"` does not match anything, because `<div class='in-the-shadow'>` is not a direct child of `article`
Note that `<open mode shadow root>` is not an html element, but rather a shadow
root created with `element.attachShadow({mode: 'open'})`.
- Both `"css=article div"` and `"css:light=article div"` match the first
`<div>In the light dom</div>`.
- Both `"css=article > div"` and `"css:light=article > div"` match two `div`
elements that are direct children of the `article`.
- `"css=article .in-the-shadow"` matches the `<div class='in-the-shadow'>`,
piercing the shadow root, while `"css:light=article .in-the-shadow"` does
not match anything.
- `"css:light=article div > span"` does not match anything, because both
light-dom `div` elements do not contain a `span`.
- `"css=article div > span"` matches the `<span class='content'>`, piercing
the shadow root.
- `"css=article > .in-the-shadow"` does not match anything, because `<div
class='in-the-shadow'>` is not a direct child of `article`
- `"css:light=article > .in-the-shadow"` does not match anything.
- `"css=article li#target"` matches the `<li id='target'>Deep in the shadow</li>`, piercing two shadow roots.
- `"css=article li#target"` matches the `<li id='target'>Deep in the
shadow</li>`, piercing two shadow roots.
#### CSS extension: visible
The `:visible` pseudo-class matches elements that are visible as defined in the [actionability](./actionability.md#visible) guide. For example, `input` matches all the inputs on the page, while `input:visible` matches only visible inputs. This is useful to distinguish elements that are very similar but differ in visibility.
The `:visible` pseudo-class matches elements that are visible as defined in the
[actionability](./actionability.md#visible) guide. For example, `input` matches
all the inputs on the page, while `input:visible` matches only visible inputs.
This is useful to distinguish elements that are very similar but differ in
visibility.
```js
// Clicks the first button.
@ -187,18 +247,29 @@ await page.click('button:visible');
```
Use `:visible` with caution, because it has two major drawbacks:
* When elements change their visibility dynamically, `:visible` will give upredictable results based on the timing.
* `:visible` forces a layout and may lead to querying being slow, especially when used with `page.waitForSelector(selector[, options])` method.
* When elements change their visibility dynamically, `:visible` will give
upredictable results based on the timing.
* `:visible` forces a layout and may lead to querying being slow, especially
when used with `page.waitForSelector(selector[, options])` method.
#### CSS extension: text
The `:text` pseudo-class matches elements that have a text node child with specific text. It is similar to the [text engine](#text-and-textlight). There are a few variations that support different arguments:
* `:text("substring")` - Matches when element's text contains "substring" somewhere. Matching is case-insensitive. Matching also normalizes whitespace, for example it turns multiple spaces into one, trusn line breaks into spaces and ignores leading and trailing whitespace.
* `:text-is("string")` - Matches when element's text equals the "string". Matching is case-insensitive and normalizes whitespace.
The `:text` pseudo-class matches elements that have a text node child with
specific text. It is similar to the [text engine](#text-and-textlight). There
are a few variations that support different arguments:
* `:text("substring")` - Matches when element's text contains "substring"
somewhere. Matching is case-insensitive. Matching also normalizes
whitespace, for example it turns multiple spaces into one, trusn line breaks
into spaces and ignores leading and trailing whitespace.
* `:text-is("string")` - Matches when element's text equals the "string".
Matching is case-insensitive and normalizes whitespace.
* `button:text("Sign in")` - Text selector may be combined with regular CSS.
* `:text-matches("[+-]?\\d+")` - Matches text against a regular expression. Note that special characters like back-slash `\`, quotes `"`, square brackets `[]` and more should be escaped. Learn more about [regular expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp).
* `:text-matches("value", "i")` - Matches text against a regular expression with specified flags.
* `:text-matches("[+-]?\\d+")` - Matches text against a regular expression.
Note that special characters like back-slash `\`, quotes `"`, square
brackets `[]` and more should be escaped. Learn more about
[regular expressions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp).
* `:text-matches("value", "i")` - Matches text against a regular expression
with specified flags.
```js
// Click a button with text "Sign in".
@ -207,25 +278,39 @@ await page.click('button:text("Sign in")');
#### CSS extension: light
`css` engine [pierces shadow](#shadow-piercing) by default. It is possible to disable this behavior by wrapping a selector in `:light` pseudo-class: `:light(section > button.class)` matches in light DOM only.
`css` engine [pierces shadow](#shadow-piercing) by default. It is possible to
disable this behavior by wrapping a selector in `:light` pseudo-class:
`:light(section > button.class)` matches in light DOM only.
```js
await page.click(':light(.article > .header)');
```
<!--
#### CSS extension: proximity
Playwright provides a few proximity selectors based on the page layout. These can be combined with regular CSS for better results, for example `input:right-of(:text("Password"))` matches an input field that is to the right of text "Password".
Playwright provides a few proximity selectors based on the page layout. These
can be combined with regular CSS for better results, for example
`input:right-of(:text("Password"))` matches an input field that is to the right
of text "Password".
Note that Playwright uses some heuristics to determine whether one element should be considered to the left/right/above/below/near/within another. Therefore, using proximity selectors may produce unpredictable results. For example, selector could stop matching when element moves by one pixel.
* `:right-of(css > selector)` - Matches elements that are to the right of any element matching the inner selector.
* `:left-of(css > selector)` - Matches elements that are to the left of any element matching the inner selector.
* `:above(css > selector)` - Matches elements that are above any of the elements matching the inner selector.
* `:below(css > selector)` - Matches elements that are below any of the elements matching the inner selector.
* `:near(css > selector)` - Matches elements that are near any of the elements matching the inner selector.
* `:within(css > selector)` - Matches elements that are within any of the elements matching the inner selector.
Note that Playwright uses some heuristics to determine whether one element
should be considered to the left/right/above/below/near/within another.
Therefore, using proximity selectors may produce unpredictable results. For
example, selector could stop matching when element moves by one pixel.
* `:right-of(css > selector)` - Matches elements that are to the right of any
element matching the inner selector.
* `:left-of(css > selector)` - Matches elements that are to the left of any
element matching the inner selector.
* `:above(css > selector)` - Matches elements that are above any of the
elements matching the inner selector.
* `:below(css > selector)` - Matches elements that are below any of the
elements matching the inner selector.
* `:near(css > selector)` - Matches elements that are near any of the elements
matching the inner selector.
* `:within(css > selector)` - Matches elements that are within any of the
elements matching the inner selector.
```js
// Fill an input to the right of "Username".
@ -234,34 +319,116 @@ await page.fill('input:right-of(:text("Username"))');
// Click a button near the promo card.
await page.click('button:near(.promo-card)');
```
-->
### xpath
XPath engine is equivalent to [`Document.evaluate`](https://developer.mozilla.org/en/docs/Web/API/Document/evaluate). Example: `xpath=//html/body`.
XPath engine is equivalent to
[`Document.evaluate`](https://developer.mozilla.org/en/docs/Web/API/Document/evaluate).
Example: `xpath=//html/body`.
Malformed selector starting with `//` or `..` is assumed to be an xpath selector. For example, Playwright converts `page.$('//html/body')` to `page.$('xpath=//html/body')`.
Malformed selector starting with `//` or `..` is assumed to be an xpath
selector. For example, Playwright converts `page.$('//html/body')` to
`page.$('xpath=//html/body')`.
Note that `xpath` does not pierce shadow roots.
### text and text:light
Text engine finds an element that contains a text node with the passed text. For example, `page.click('text=Login')` clicks on a login button, and `page.waitForSelector('"lazy loaded text")` waits for the `"lazy loaded text"` to appear in the page.
Text engine finds an element that contains a text node with the passed text. For
example, `page.click('text=Login')` clicks on a login button, and
`page.waitForSelector('"lazy loaded text")` waits for the `"lazy loaded text"`
to appear in the page.
- By default, the match is case-insensitive, ignores leading/trailing
whitespace and searches for a substring. This means `text= Login` matches
`<button>Button loGIN (click me)</button>`.
- Text body can be escaped with single or double quotes for precise matching,
insisting on exact match, including specified whitespace and case. This
means `text="Login "` will only match `<button>Login </button>` with exactly
one space after "Login". Quoted text follows the usual escaping rules, e.g.
use `\"` to escape double quote in a double-quoted string:
`text="foo\"bar"`.
- Text body can also be a JavaScript-like regex wrapped in `/` symbols. This
means `text=/^\\s*Login$/i` will match `<button> loGIN</button>` with any
number of spaces before "Login" and no spaces after.
- Input elements of the type `button` and `submit` are rendered with their
value as text, and text engine finds them. For example, `text=Login` matches
`<input type=button value="Login">`.
- By default, the match is case-insensitive, ignores leading/trailing whitespace and searches for a substring. This means `text= Login` matches `<button>Button loGIN (click me)</button>`.
- Text body can be escaped with single or double quotes for precise matching, insisting on exact match, including specified whitespace and case. This means `text="Login "` will only match `<button>Login </button>` with exactly one space after "Login". Quoted text follows the usual escaping rules, e.g. use `\"` to escape double quote in a double-quoted string: `text="foo\"bar"`.
- Text body can also be a JavaScript-like regex wrapped in `/` symbols. This means `text=/^\\s*Login$/i` will match `<button> loGIN</button>` with any number of spaces before "Login" and no spaces after.
- Input elements of the type `button` and `submit` are rendered with their value as text, and text engine finds them. For example, `text=Login` matches `<input type=button value="Login">`.
Malformed selector starting and ending with a quote (either `"` or `'`) is
assumed to be a text selector. For example, Playwright converts
`page.click('"Login"')` to `page.click('text="Login"')`.
Malformed selector starting and ending with a quote (either `"` or `'`) is assumed to be a text selector. For example, Playwright converts `page.click('"Login"')` to `page.click('text="Login"')`.
`text` engine open pierces shadow roots similarly to `css`, while `text:light` does not. Text engine first searches for elements in the light dom in the iteration order, and then recursively inside open shadow roots in the iteration order. It does not search inside closed shadow roots or iframes.
`text` engine open pierces shadow roots similarly to `css`, while `text:light`
does not. Text engine first searches for elements in the light dom in the
iteration order, and then recursively inside open shadow roots in the iteration
order. It does not search inside closed shadow roots or iframes.
### id, data-testid, data-test-id, data-test and their :light counterparts
Attribute engines are selecting based on the corresponding attribute value. For example: `data-test-id=foo` is equivalent to `css=[data-test-id="foo"]`, and `id:light=foo` is equivalent to `css:light=[id="foo"]`.
Attribute engines are selecting based on the corresponding attribute value. For
example: `data-test-id=foo` is equivalent to `css=[data-test-id="foo"]`, and
`id:light=foo` is equivalent to `css:light=[id="foo"]`.
[css]: #css-and-csslight
[text]: #text-and-textlight
[xpath]: #xpath
[id]: #id-data-testid-data-test-id-data-test-and-their-light-counterparts
[id]: #id-data-testid-data-test-id-data-test-and-their-light-counterparts
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,57 +1,154 @@
<!-- THIS FILE IS NOW GENERATED -->
# Community Showcase
## Users
* [VS Code](https://github.com/microsoft/vscode): Playwright is used to run cross-browser tests on their web builds
* [TypeScript](https://github.com/microsoft/TypeScript): Playwright is used test typescript.js across browsers
* [Elastic APM JS agent](https://github.com/elastic/apm-agent-rum-js): Playwright is used to run benchmark tests across browsers
* [Blockstack](https://github.com/blockstack/ux): Playwright is used to run cross-browser UI tests
* [Instakittens React admin](https://github.com/fredericbonnet/instakittens-react-admin): Playwright is used to run end-to-end test scenarios written with Cucumber
* [xterm.js](https://github.com/xtermjs/xterm.js): Playwright is used to run cross-browser integration tests
* [Accessibility Insights for Web](https://github.com/microsoft/accessibility-insights-web): Playwright is used with Jest and axe-core to run end-to-end functional and accessibility tests of a WebExtension-based browser extension
* [VS Code](https://github.com/microsoft/vscode): Playwright is used to run
cross-browser tests on their web builds
* [TypeScript](https://github.com/microsoft/TypeScript): Playwright is used
test typescript.js across browsers
* [Elastic APM JS agent](https://github.com/elastic/apm-agent-rum-js):
Playwright is used to run benchmark tests across browsers
* [Blockstack](https://github.com/blockstack/ux): Playwright is used to run
cross-browser UI tests
* [Instakittens React admin](https://github.com/fredericbonnet/instakittens-react-admin):
Playwright is used to run end-to-end test scenarios written with Cucumber
* [xterm.js](https://github.com/xtermjs/xterm.js): Playwright is used to run
cross-browser integration tests
* [Accessibility Insights for Web](https://github.com/microsoft/accessibility-insights-web):
Playwright is used with Jest and axe-core to run end-to-end functional and
accessibility tests of a WebExtension-based browser extension
## Tools
* [CodeceptJS](https://github.com/Codeception/CodeceptJS): Write scenario-driven Playwright tests with synchronous code
* [dom-to-playwright](https://github.com/Xiphe/dom-to-playwright) to copy a JSDOM snapshot into a Playwright page.
* [expected-condition-playwright](https://github.com/elaichenkov/expected-condition-playwright): Supplies a set of common expected conditions that can wait for certain states and conditions
* [Headless Testing](https://headlesstesting.com/support/start/playwright.html): Run Playwright tests on browsers in the cloud
* [Lumberjack](https://github.com/JakePartusch/lumberjack): Automated accessibility scanner to run checks on your entire website
* [mockiavelli](https://github.com/HLTech/mockiavelli) Request mocking library for Playwright to test SPA in isolation from backend APIs.
* [Moon](https://github.com/aerokube/moon): Run Playwright tests in parallel in Kubernetes cluster (free up to 4 parallel sessions)
* [playwright-test](https://github.com/hugomrdias/playwright-test) Run unit tests and benchmarks in browsers with Node's seamless experience.
* [playwright-video](https://github.com/qawolf/playwright-video): Capture a video while executing a Playwright script
* [QA Wolf](https://github.com/qawolf/qawolf): Record and create Playwright tests and then run them in CI
* [Root Cause OSS](https://github.com/testimio/root-cause): Capture screenshots on every step and display in a viewer with logs for easy troubleshooting.
* [test-real-styles](https://github.com/Xiphe/test-real-styles): Utility to test real styling of virtual DOM elements in a browser
* [Testim Playground](https://www.testim.io/playground/): Record Playwright UI tests as code
* [Try Playwright](https://try.playwright.tech/): Interactive playground for Playwright to run examples directly from your browser
* [Applitools](https://applitools.com): Add AI-powered visual assertions and run your Playwright tests on all browser, device, and viewport combinations in parallel, without requiring any setup.
* [CodeceptJS](https://github.com/Codeception/CodeceptJS): Write
scenario-driven Playwright tests with synchronous code
* [dom-to-playwright](https://github.com/Xiphe/dom-to-playwright) to copy a
JSDOM snapshot into a Playwright page.
* [expected-condition-playwright](https://github.com/elaichenkov/expected-condition-playwright):
Supplies a set of common expected conditions that can wait for certain
states and conditions
* [Headless Testing](https://headlesstesting.com/support/start/playwright.html):
Run Playwright tests on browsers in the cloud
* [Lumberjack](https://github.com/JakePartusch/lumberjack): Automated
accessibility scanner to run checks on your entire website
* [mockiavelli](https://github.com/HLTech/mockiavelli) Request mocking library
for Playwright to test SPA in isolation from backend APIs.
* [Moon](https://github.com/aerokube/moon): Run Playwright tests in parallel
in Kubernetes cluster (free up to 4 parallel sessions)
* [playwright-test](https://github.com/hugomrdias/playwright-test) Run unit
tests and benchmarks in browsers with Node's seamless experience.
* [playwright-video](https://github.com/qawolf/playwright-video): Capture a
video while executing a Playwright script
* [QA Wolf](https://github.com/qawolf/qawolf): Record and create Playwright
tests and then run them in CI
* [Root Cause OSS](https://github.com/testimio/root-cause): Capture
screenshots on every step and display in a viewer with logs for easy
troubleshooting.
* [test-real-styles](https://github.com/Xiphe/test-real-styles): Utility to
test real styling of virtual DOM elements in a browser
* [Testim Playground](https://www.testim.io/playground/): Record Playwright UI
tests as code
* [Try Playwright](https://try.playwright.tech/): Interactive playground for
Playwright to run examples directly from your browser
* [Applitools](https://applitools.com): Add AI-powered visual assertions and
run your Playwright tests on all browser, device, and viewport combinations
in parallel, without requiring any setup.
## Frameworks
* [jest-playwright](https://github.com/mmarkelov/jest-playwright): Jest preset to run Playwright tests with Jest
* [query-selector-shadow-dom](https://github.com/Georgegriff/query-selector-shadow-dom): Custom selector engine to pierce shadow DOM roots
* [Playwright Sharp](https://github.com/kblok/playwright-sharp): Work in progress port of Playwright to .NET
* [playwright-fluent](https://github.com/hdorgeval/playwright-fluent): Fluent API around Playwright
* [robotframework-browser](https://robotframework-browser.org/) Robotframework library that uses Playwright to achieve good development ergonomics.
* [jest-playwright](https://github.com/mmarkelov/jest-playwright): Jest preset
to run Playwright tests with Jest
* [query-selector-shadow-dom](https://github.com/Georgegriff/query-selector-shadow-dom):
Custom selector engine to pierce shadow DOM roots
* [Playwright Sharp](https://github.com/kblok/playwright-sharp): Work in
progress port of Playwright to .NET
* [playwright-fluent](https://github.com/hdorgeval/playwright-fluent): Fluent
API around Playwright
* [robotframework-browser](https://robotframework-browser.org/) Robotframework
library that uses Playwright to achieve good development ergonomics.
## Examples
* [e2e Boilerplates](https://github.com/e2e-boilerplate?utf8=%E2%9C%93&q=playwright): Project boilerplates for using Playwright with TypeScript, Cucumber, Jest, and other libraries
* [react-app-playwright](https://github.com/KyleADay/react-app-playwright): Using Playwright with a create-react-app project
* [playwright-react-typescript-jest-example](https://github.com/azemetre/playwright-react-typescript-jest-example): Using Playwright + Jest with a custom webpack configuration for React + Typescript project
* [playwright-mocha](https://github.com/roggerfe/playwright-mocha): Using Playwright with Mocha and Chai
* [playwright-cljs](https://github.com/apeckham/playwright-cljs): Playwright examples in ClojureScript
* [playwright-azure-functions](https://github.com/arjun27/playwright-azure-functions): Playwright setup on Azure Functions
* [playwright-aws-lambda](https://github.com/austinkelleher/playwright-aws-lambda): Playwright setup on AWS Lambda
* [playwright-jest-circus-allure](https://github.com/d-shch/playwright-jest-circus-allure): Example how to use allure-report and jest-circus with playwright
* [Heroku Playwright Example](https://github.com/mxschmitt/heroku-playwright-example): Example using Playwright on Heroku
* [e2e Boilerplates](https://github.com/e2e-boilerplate?utf8=%E2%9C%93&q=playwright):
Project boilerplates for using Playwright with TypeScript, Cucumber, Jest,
and other libraries
* [react-app-playwright](https://github.com/KyleADay/react-app-playwright):
Using Playwright with a create-react-app project
* [playwright-react-typescript-jest-example](https://github.com/azemetre/playwright-react-typescript-jest-example):
Using Playwright + Jest with a custom webpack configuration for React +
Typescript project
* [playwright-mocha](https://github.com/roggerfe/playwright-mocha): Using
Playwright with Mocha and Chai
* [playwright-cljs](https://github.com/apeckham/playwright-cljs): Playwright
examples in ClojureScript
* [playwright-azure-functions](https://github.com/arjun27/playwright-azure-functions):
Playwright setup on Azure Functions
* [playwright-aws-lambda](https://github.com/austinkelleher/playwright-aws-lambda):
Playwright setup on AWS Lambda
* [playwright-jest-circus-allure](https://github.com/d-shch/playwright-jest-circus-allure):
Example how to use allure-report and jest-circus with playwright
* [Heroku Playwright Example](https://github.com/mxschmitt/heroku-playwright-example):
Example using Playwright on Heroku
## Guides
* [theheadless.dev](https://theheadless.dev): Practical guides and runnable examples on Playwright (and Puppeteer)
* [theheadless.dev](https://theheadless.dev): Practical guides and runnable
examples on Playwright (and Puppeteer)
## Contributing
Did we miss something in this list? Send us a PR!
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,6 +1,9 @@
<!-- THIS FILE IS NOW GENERATED -->
# Test Runners
With a few lines of code, you can hook up Playwright to your favorite JavaScript test runner.
With a few lines of code, you can hook up Playwright to your favorite JavaScript
test runner.
<!-- GEN:toc -->
- [Jest / Jasmine](#jest--jasmine)
@ -15,7 +18,11 @@ With a few lines of code, you can hook up Playwright to your favorite JavaScript
## Jest / Jasmine
For Jest, [jest-playwright](https://github.com/playwright-community/jest-playwright) can be used. However for a light-weight solution, requiring playwright directly works fine. Jest shares it's syntax with Jasmine, so this applies to Jasmine as well.
For Jest,
[jest-playwright](https://github.com/playwright-community/jest-playwright) can
be used. However for a light-weight solution, requiring playwright directly
works fine. Jest shares it's syntax with Jasmine, so this applies to Jasmine as
well.
```js
const {chromium} = require('playwright');
@ -45,7 +52,8 @@ it('should work', async () => {
## AVA
Tests run concurrently in AVA, so a single page variable cannot be shared between tests. Instead, create new pages with a macro function.
Tests run concurrently in AVA, so a single page variable cannot be shared
between tests. Instead, create new pages with a macro function.
```js
const {chromium} = require('playwright');
@ -72,7 +80,8 @@ test('should work', pageMacro, async (t, page) => {
## Mocha
Mocha looks very similar to the Jest/Jasmine setup, and functions in the same way.
Mocha looks very similar to the Jest/Jasmine setup, and functions in the same
way.
```js
const {chromium} = require('playwright');
@ -97,11 +106,13 @@ it('should work', async () => {
assert.equal(await page.title(), 'Example Domain');
});
```
<br>
## Multiple Browsers
These simple examples can be extended to support multiple browsers using an environment variable.
These simple examples can be extended to support multiple browsers using an
environment variable.
```js
const {chromium, webkit, firefox} = require('playwright');
@ -113,4 +124,60 @@ beforeAll(async() => {
```
Then set `BROWSER=firefox` to run your tests with firefox, or any other browser.
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,3 +1,5 @@
<!-- THIS FILE IS NOW GENERATED -->
# Troubleshooting
<!-- GEN:toc -->
@ -9,16 +11,26 @@
## Browser dependencies
Playwright does self-inspection every time it runs to make sure the browsers can be launched successfully. If there are missing
dependencies, playwright will print instructions to acquire them.
Playwright does self-inspection every time it runs to make sure the browsers can
be launched successfully. If there are missing dependencies, playwright will
print instructions to acquire them.
We also provide [Ubuntu 18.04 dockerfile](docker/Dockerfile.bionic) and [Ubuntu 20.04 dockerfile](docker/Dockerfile.focal) with the list of Debian dependencies.
We also provide [Ubuntu 18.04 dockerfile](docker/Dockerfile.bionic) and
[Ubuntu 20.04 dockerfile](docker/Dockerfile.focal) with the list of Debian
dependencies.
## Code transpilation issues
If you are using a JavaScript transpiler like babel or TypeScript, calling `evaluate()` with an async function might not work. This is because while `playwright` uses `Function.prototype.toString()` to serialize functions while transpilers could be changing the output code in such a way it's incompatible with `playwright`.
If you are using a JavaScript transpiler like babel or TypeScript, calling
`evaluate()` with an async function might not work. This is because while
`playwright` uses `Function.prototype.toString()` to serialize functions while
transpilers could be changing the output code in such a way it's incompatible
with `playwright`.
Some workarounds to this problem would be to instruct the transpiler not to mess up with the code, for example, configure TypeScript to use latest ECMAScript version (`"target": "es2018"`). Another workaround could be using string templates instead of functions:
Some workarounds to this problem would be to instruct the transpiler not to mess
up with the code, for example, configure TypeScript to use latest ECMAScript
version (`"target": "es2018"`). Another workaround could be using string
templates instead of functions:
```js
await page.evaluate(`(async() => {
@ -30,8 +42,67 @@ await page.evaluate(`(async() => {
### ReferenceError: URL is not defined
Playwright requires Node.js 10 or higher. Node.js 8 is not supported, and will cause you to receive this error.
Playwright requires Node.js 10 or higher. Node.js 8 is not supported, and will
cause you to receive this error.
# Please file an issue
Playwright is a new project, and we are watching the issues very closely. As we solve common issues, this document will grow to include the common answers.
Playwright is a new project, and we are watching the issues very closely. As we
solve common issues, this document will grow to include the common answers.
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,3 +1,5 @@
<!-- THIS FILE IS NOW GENERATED -->
# Verification
<!-- GEN:toc-top-level -->
@ -12,7 +14,9 @@
## Videos
Playwright can record videos for all pages in a [browser context](core-concepts.md#browser-contexts). Videos are saved upon context closure, so make sure to await `browserContext.close()`.
Playwright can record videos for all pages in a
[browser context](core-concepts.md#browser-contexts). Videos are saved upon
context closure, so make sure to await `browserContext.close()`.
```js
// With browser.newContext()
@ -35,8 +39,7 @@ const context = await browser.newContext({
```
#### API reference
- [class `BrowserContext`](./api.md#class-browsercontext)
- [BrowserContext]
- [browser.newContext([options])](./api.md#browsernewcontextoptions)
- [browser.newPage([options])](./api.md#browsernewpageoptions)
- [browserContext.close()](./api.md#browsercontextclose)
@ -60,7 +63,6 @@ await elementHandle.screenshot({ path: 'screenshot.png' });
```
#### API reference
- [page.screenshot([options])](./api.md#pagescreenshotoptions)
- [elementHandle.screenshot([options])](./api.md#elementhandlescreenshotoptions)
@ -95,9 +97,8 @@ await msg.args[1].jsonValue() // 42
```
#### API reference
- [class: ConsoleMessage](./api.md#class-consolemessage)
- [class: Page](./api.md#class-page)
- [ConsoleMessage]
- [Page]
- [page.on('console')](./api.md#pageonconsole)
<br/>
@ -117,8 +118,7 @@ await page.goto('data:text/html,<script>throw new Error("Test")</script>');
```
#### API reference
- [class: Page](./api.md#class-page)
- [Page]
- [page.on('pageerror')](./api.md#pageonpageerror)
<br/>
@ -151,8 +151,64 @@ const [popup] = await Promise.all([
```
#### API reference
- [class: Page](./api.md#class-page)
- [Page]
- [page.on('requestfailed')](./api.md#pageonrequestfailed)
- [page.on('dialog')](./api.md#pageondialog)
- [page.on('popup')](./api.md#pageonpopup)
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

View file

@ -1,6 +1,10 @@
<!-- THIS FILE IS NOW GENERATED -->
# Why Playwright?
Playwright enables fast, reliable and capable automation across all modern browsers. This guide covers those key differentiators to help you decide on the right tool for your automated tests.
Playwright enables fast, reliable and capable automation across all modern
browsers. This guide covers those key differentiators to help you decide on the
right tool for your automated tests.
<!-- GEN:toc-top-level -->
- [Support for all browsers](#support-for-all-browsers)
@ -11,47 +15,132 @@ Playwright enables fast, reliable and capable automation across all modern brows
<!-- GEN:stop -->
## Support for all browsers
* **Test on Chromium, Firefox and WebKit**. Playwright has full API coverage for all modern browsers, including Google Chrome and Microsoft Edge (with [Chromium](https://www.chromium.org/)), Apple Safari (with [WebKit](https://webkit.org/)) and Mozilla Firefox.
* **Cross-platform WebKit testing**. With Playwright, test how your app behaves in Apple Safari with WebKit builds for Windows, Linux and macOS. Test locally and on CI.
* **Test for mobile**. Use [device emulation](emulation.md) to test your responsive web apps in mobile web browsers.
* **Headless and headful**. Playwright supports headless (without browser UI) and headful (with browser UI) modes for all browsers and all platforms. Headful is great for debugging, and headless is faster and suited for CI/cloud executions.
* **Test on Chromium, Firefox and WebKit**. Playwright has full API coverage
for all modern browsers, including Google Chrome and Microsoft Edge (with
[Chromium](https://www.chromium.org/)), Apple Safari (with
[WebKit](https://webkit.org/)) and Mozilla Firefox.
* **Cross-platform WebKit testing**. With Playwright, test how your app
behaves in Apple Safari with WebKit builds for Windows, Linux and macOS.
Test locally and on CI.
* **Test for mobile**. Use [device emulation](./emulation.md) to test your
responsive web apps in mobile web browsers.
* **Headless and headful**. Playwright supports headless (without browser UI)
and headful (with browser UI) modes for all browsers and all platforms.
Headful is great for debugging, and headless is faster and suited for
CI/cloud executions.
## Fast and reliable execution
* **Auto-wait APIs**. Playwright interactions [auto-wait for elements](actionability.md) to be ready. This improves reliability and simplifies test authoring.
* **Timeout-free automation**. Playwright receives browser signals, like network requests, page navigations and page load events to eliminate the need for sleep timeouts that cause flakiness.
* **Lean parallelization with browser contexts**. Reuse a single browser instance for multiple parallelized, isolated execution environments with [browser contexts](core-concepts.md).
* **Resilient element selectors**. Playwright can rely on user-facing strings, like text content and accessibility labels to [select elements](selectors.md). These strings are more resilient than selectors tightly-coupled to the DOM structure.
* **Auto-wait APIs**. Playwright interactions
[auto-wait for elements](./actionability.md) to be ready. This improves
reliability and simplifies test authoring.
* **Timeout-free automation**. Playwright receives browser signals, like
network requests, page navigations and page load events to eliminate the
need for sleep timeouts that cause flakiness.
* **Lean parallelization with browser contexts**. Reuse a single browser
instance for multiple parallelized, isolated execution environments with
[browser contexts](core-concepts.md).
* **Resilient element selectors**. Playwright can rely on user-facing strings,
like text content and accessibility labels to
[select elements](./selectors.md). These strings are more resilient than
selectors tightly-coupled to the DOM structure.
## Powerful automation capabilities
* **Multiple domains, pages and frames**. Playwright is an out-of-process automation driver that is not limited by the scope of in-page JavaScript execution and can automate scenarios with [multiple pages](multi-pages.md).
* **Powerful network control**. Playwright introduces context-wide [network interception](network.md) to stub and mock network requests.
* **Modern web features**. Playwright supports web components through [shadow-piercing selectors](selectors.md), [geolocation, permissions](emulation.md), web workers and other modern web APIs.
* **Capabilities to cover all scenarios**. Support for [file downloads](network.md) and [uploads](input.md), out-of-process iframes, native [input events](input.md), and even [dark mode](emulation.md).
* **Multiple domains, pages and frames**. Playwright is an out-of-process
automation driver that is not limited by the scope of in-page JavaScript
execution and can automate scenarios with [multiple pages](multi-pages.md).
* **Powerful network control**. Playwright introduces context-wide
[network interception](./network.md) to stub and mock network requests.
* **Modern web features**. Playwright supports web components through
[shadow-piercing selectors](./selectors.md),
[geolocation, permissions](./emulation.md), web workers and other modern web
APIs.
* **Capabilities to cover all scenarios**. Support for
[file downloads](./network.md) and [uploads](./input.md), out-of-process
iframes, native [input events](./input.md), and even
[dark mode](./emulation.md).
## Integrates with your workflow
* **One-line installation**. Running `npm i playwright` auto-downloads browser dependencies for your team to be onboarded quickly.
* **TypeScript support**. Playwright ships with built-in types for auto-completion and other benefits.
* **Debugging tools**. Playwright works with the [editor debugger and browser developer tools](debug.md) to pause execution and inspect the web page.
* **Language bindings**. Playwright is also available in [Python](https://github.com/microsoft/playwright-python) and [C#](https://github.com/microsoft/playwright-sharp). Learn about [supported languages](./languages.md).
* **Deploy tests to CI**. First-party [Docker image](docker/README.md) and [GitHub Actions](https://github.com/microsoft/playwright-github-action) to deploy tests to [your preferred CI/CD provider](ci.md).
* **One-line installation**. Running `npm i playwright` auto-downloads browser
dependencies for your team to be onboarded quickly.
* **TypeScript support**. Playwright ships with built-in types for
auto-completion and other benefits.
* **Debugging tools**. Playwright works with the
[editor debugger and browser developer tools](./debug.md) to pause execution
and inspect the web page.
* **Language bindings**. Playwright is also available in
[Python](https://github.com/microsoft/playwright-python) and
[C#](https://github.com/microsoft/playwright-sharp). Learn about
[supported languages](./languages.md).
* **Deploy tests to CI**. First-party [Docker image](docker/README.md) and
[GitHub Actions](https://github.com/microsoft/playwright-github-action) to
deploy tests to [your preferred CI/CD provider](./ci.md).
## Limitations
* **Legacy Edge and IE11 support**. Playwright does not support legacy Microsoft Edge or IE11 ([deprecation notice](https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666)). The new Microsoft Edge (on Chromium) is supported.
* **Java language bindings**: The Playwright API cannot be used in Java or Ruby today. This is a temporary limitation as Playwright is built to support bindings for any language.
* **Test on real mobile devices**: Playwright uses desktop browsers to emulate mobile devices. If you are interested in running on real mobile devices, please [upvote this issue](https://github.com/microsoft/playwright/issues/1122).
* **Legacy Edge and IE11 support**. Playwright does not support legacy
Microsoft Edge or IE11
([deprecation notice](https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666)).
The new Microsoft Edge (on Chromium) is supported.
* **Java language bindings**: The Playwright API cannot be used in Java or
Ruby today. This is a temporary limitation as Playwright is built to support
bindings for any language.
* **Test on real mobile devices**: Playwright uses desktop browsers to emulate
mobile devices. If you are interested in running on real mobile devices,
please
[upvote this issue](https://github.com/microsoft/playwright/issues/1122).
[Playwright]: api.md#class-playwright "Playwright"
[Browser]: api.md#class-browser "Browser"
[BrowserContext]: api.md#class-browsercontext "BrowserContext"
[Page]: api.md#class-page "Page"
[Frame]: api.md#class-frame "Frame"
[ElementHandle]: api.md#class-elementhandle "ElementHandle"
[JSHandle]: api.md#class-jshandle "JSHandle"
[ConsoleMessage]: api.md#class-consolemessage "ConsoleMessage"
[Dialog]: api.md#class-dialog "Dialog"
[Download]: api.md#class-download "Download"
[Video]: api.md#class-video "Video"
[FileChooser]: api.md#class-filechooser "FileChooser"
[Keyboard]: api.md#class-keyboard "Keyboard"
[Mouse]: api.md#class-mouse "Mouse"
[Touchscreen]: api.md#class-touchscreen "Touchscreen"
[Request]: api.md#class-request "Request"
[Response]: api.md#class-response "Response"
[Selectors]: api.md#class-selectors "Selectors"
[Route]: api.md#class-route "Route"
[WebSocket]: api.md#class-websocket "WebSocket"
[TimeoutError]: api.md#class-timeouterror "TimeoutError"
[Accessibility]: api.md#class-accessibility "Accessibility"
[Worker]: api.md#class-worker "Worker"
[BrowserServer]: api.md#class-browserserver "BrowserServer"
[BrowserType]: api.md#class-browsertype "BrowserType"
[Logger]: api.md#class-logger "Logger"
[ChromiumBrowser]: api.md#class-chromiumbrowser "ChromiumBrowser"
[ChromiumBrowserContext]: api.md#class-chromiumbrowsercontext "ChromiumBrowserContext"
[ChromiumCoverage]: api.md#class-chromiumcoverage "ChromiumCoverage"
[CDPSession]: api.md#class-cdpsession "CDPSession"
[FirefoxBrowser]: api.md#class-firefoxbrowser "FirefoxBrowser"
[WebKitBrowser]: api.md#class-webkitbrowser "WebKitBrowser"
[Array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array "Array"
[Buffer]: https://nodejs.org/api/buffer.html#buffer_class_buffer "Buffer"
[ChildProcess]: https://nodejs.org/api/child_process.html "ChildProcess"
[Element]: https://developer.mozilla.org/en-US/docs/Web/API/element "Element"
[Error]: https://nodejs.org/api/errors.html#errors_class_error "Error"
[EvaluationArgument]: #evaluationargument "Evaluation Argument"
[Map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map "Map"
[Object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object "Object"
[Promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise "Promise"
[RegExp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp "RegExp"
[Serializable]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#Description "Serializable"
[UIEvent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail "UIEvent.detail"
[URL]: https://nodejs.org/api/url.html "URL"
[USKeyboardLayout]: ../src/usKeyboardLayout.ts "USKeyboardLayout"
[UnixTime]: https://en.wikipedia.org/wiki/Unix_time "Unix Time"
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type "Boolean"
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function "Function"
[iterator]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols "Iterator"
[null]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null "null"
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type "Number"
[origin]: https://developer.mozilla.org/en-US/docs/Glossary/Origin "Origin"
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors "selector"
[Readable]: https://nodejs.org/api/stream.html#stream_class_stream_readable "Readable"
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type "string"
[xpath]: https://developer.mozilla.org/en-US/docs/Web/XPath "xpath"

927
types/types.d.ts vendored

File diff suppressed because it is too large Load diff

View file

@ -75,9 +75,7 @@ class MDOutline {
/**
* @param {Renderer} linkRenderer
*/
renderLinks(linkRenderer) {
const externalLinksMap = new Map();
setLinkRenderer(linkRenderer) {
// @type {Map<string, Documentation.Class>}
const classesMap = new Map();
const membersMap = new Map();
@ -86,11 +84,22 @@ class MDOutline {
for (const member of clazz.membersArray)
membersMap.set(`${member.kind}: ${clazz.name}.${member.name}`, member);
}
this._patchLinks = nodes => patchLinks(nodes, classesMap, membersMap, linkRenderer);
for (const clazz of this.classesArray)
clazz.visit(item => patchLinks(item, item.spec, classesMap, membersMap, linkRenderer));
clazz.visit(item => this._patchLinks(item.spec));
}
/**
* @param {string} content
* @return {string}
*/
renderLinksInText(content) {
const nodes = md.parse(content);
this._patchLinks(nodes);
return md.render(nodes, 80);
}
generateSourceCodeComments() {
for (const clazz of this.classesArray)
clazz.visit(item => item.comment = generateSourceCodeComment(item.spec));
@ -128,7 +137,7 @@ function extractComments(item) {
* @param {MarkdownNode[]} spec
*/
function generateSourceCodeComment(spec) {
const comments = (spec || []).filter(n => n.type !== 'gen' && !n.type.startsWith('h') && (n.type !== 'li' || n.liType !== 'default')).map(c => md.clone(c));
const comments = (spec || []).filter(n => !n.type.startsWith('h') && (n.type !== 'li' || n.liType !== 'default')).map(c => md.clone(c));
md.visitAll(comments, node => {
if (node.liType === 'bullet')
node.liType = 'default';
@ -137,13 +146,12 @@ function generateSourceCodeComment(spec) {
}
/**
* @param {Documentation.Class|Documentation.Member} item
* @param {MarkdownNode[]} spec
* @param {Map<string, Documentation.Class>} classesMap
* @param {Map<string, Documentation.Member>} membersMap
* @param {Renderer} linkRenderer
*/
function patchLinks(item, spec, classesMap, membersMap, linkRenderer) {
function patchLinks(spec, classesMap, membersMap, linkRenderer) {
if (!spec)
return;
md.visitAll(spec, node => {
@ -151,17 +159,15 @@ function patchLinks(item, spec, classesMap, membersMap, linkRenderer) {
return;
node.text = node.text.replace(/\[`((?:event|method|property): [^\]]+)`\]/g, (match, p1) => {
const member = membersMap.get(p1);
if (!member)
throw new Error('Undefined member references: ' + match);
return linkRenderer({ member }) || match;
});
node.text = node.text.replace(/\[`(param|option): ([^\]]+)`\]/g, (match, p1, p2) => {
const context = {
clazz: item instanceof Documentation.Class ? item : undefined,
member: item instanceof Documentation.Member ? item : undefined,
};
if (p1 === 'param')
return linkRenderer({ ...context, param: p2 }) || match;
return linkRenderer({ param: p2 }) || match;
if (p1 === 'option')
return linkRenderer({ ...context, option: p2 }) || match;
return linkRenderer({ option: p2 }) || match;
});
node.text = node.text.replace(/\[([\w]+)\]/, (match, p1) => {
const clazz = classesMap.get(p1);

View file

@ -50,7 +50,6 @@ class Source {
this._name = path.basename(filePath);
this._text = text;
this._originalText = text;
this._hasUpdatedText = false;
}
/**

View file

@ -38,6 +38,9 @@ const RED_COLOR = '\x1b[31m';
const YELLOW_COLOR = '\x1b[33m';
const RESET_COLOR = '\x1b[0m';
const links = new Map();
const rLinks = new Map();
run().catch(e => {
console.error(e);
process.exit(1);
@ -46,25 +49,22 @@ run().catch(e => {
async function run() {
const startTime = Date.now();
const api = await Source.readFile(path.join(PROJECT_DIR, 'docs', 'api.md'));
const readme = await Source.readFile(path.join(PROJECT_DIR, 'README.md'));
const binReadme = await Source.readFile(path.join(PROJECT_DIR, 'bin', 'README.md'));
const contributing = await Source.readFile(path.join(PROJECT_DIR, 'CONTRIBUTING.md'));
const docs = await Source.readdir(path.join(PROJECT_DIR, 'docs'), '.md');
const mdSources = [readme, binReadme, api, contributing, ...docs];
/** @type {!Array<string>} */
const errors = [];
let changedFiles = false;
const header = fs.readFileSync(path.join(PROJECT_DIR, 'docs-src', 'api-header.md')).toString();
const footer = fs.readFileSync(path.join(PROJECT_DIR, 'docs-src', 'api-footer.md')).toString();
let footer = fs.readFileSync(path.join(PROJECT_DIR, 'docs-src', 'api-footer.md')).toString();
const links = fs.readFileSync(path.join(PROJECT_DIR, 'docs-src', 'api-links.md')).toString();
const outline = new MDOutline(path.join(PROJECT_DIR, 'docs-src', 'api-body.md'), path.join(PROJECT_DIR, 'docs-src', 'api-params.md'));
const generatedComment = '<!-- THIS FILE IS NOW GENERATED -->';
let generatedLinksSuffix;
const api = await Source.readFile(path.join(PROJECT_DIR, 'docs', 'api.md'));
const docs = await (await Source.readdir(path.join(PROJECT_DIR, 'docs'), '.md')).filter(s => s.name() !== 'api.md');
// Produce api.md
{
outline.renderLinks(item => {
outline.setLinkRenderer(item => {
const { clazz, member, param, option } = item;
if (param)
return `\`${param}\``;
@ -72,16 +72,16 @@ async function run() {
return `\`${option}\``;
if (clazz)
return `[${clazz.name}]`;
if (member.kind === 'method')
return createMemberLink(`${member.clazz.varName}.${member.name}(${member.signature})`);
if (member.kind === 'event')
return createMemberLink(`${member.clazz.varName}.on('${member.name}')`);
if (member.kind === 'property')
return createMemberLink(`${member.clazz.varName}.${member.name}`);
throw new Error('Unknown member kind ' + member.kind);
return createMemberLink(member);
});
const comment = '<!-- THIS FILE IS NOW GENERATED -->';
{
const localLinks = [];
for (const clazz of outline.classesArray)
localLinks.push(`[${clazz.name}]: api.md#class-${clazz.name.toLowerCase()} "${clazz.name}"`);
generatedLinksSuffix = localLinks.join('\n') + '\n' + links;
}
{
/** @type {MarkdownNode[]} */
const result = [];
@ -90,16 +90,11 @@ async function run() {
/** @type {MarkdownNode} */
const classNode = { type: 'h3', text: `class: ${clazz.name}` };
result.push(classNode);
// Append link shortcut to resolve text like [Browser]
result.push({
type: 'text',
text: `[${clazz.name}]: #class-${clazz.name.toLowerCase()} "${clazz.name}"`
});
// Append class comments
classNode.children = (clazz.spec || []).map(c => md.clone(c));
classNode.children.push({
type: 'text',
text: '<!-- TOC -->'
text: ''
});
classNode.children.push(...generateToc(clazz));
if (clazz.extends && clazz.extends !== 'EventEmitter' && clazz.extends !== 'Error') {
@ -140,16 +135,28 @@ async function run() {
classNode.children.push(memberNode);
}
}
result.push({
type: 'text',
text: links
});
api.setText([comment, header, md.render(result), footer].join('\n'));
footer = outline.renderLinksInText(footer);
api.setText([generatedComment, header, md.render(result), footer, generatedLinksSuffix].join('\n'));
}
}
// Produce other docs
{
for (const doc of docs) {
if (!fs.existsSync(path.join(PROJECT_DIR, 'docs-src', doc.name())))
continue;
const content = fs.readFileSync(path.join(PROJECT_DIR, 'docs-src', doc.name())).toString();
doc.setText([generatedComment, outline.renderLinksInText(content), generatedLinksSuffix].join('\n'));
}
}
// Documentation checks.
{
const readme = await Source.readFile(path.join(PROJECT_DIR, 'README.md'));
const binReadme = await Source.readFile(path.join(PROJECT_DIR, 'bin', 'README.md'));
const contributing = await Source.readFile(path.join(PROJECT_DIR, 'CONTRIBUTING.md'));
const mdSources = [readme, binReadme, api, contributing, ...docs];
const browserVersions = await getBrowserVersions();
errors.push(...(await preprocessor.runCommands(mdSources, {
libversion: VERSION,
@ -158,10 +165,7 @@ async function run() {
webkitVersion: browserVersions.webkit,
})));
errors.push(...preprocessor.autocorrectInvalidLinks(PROJECT_DIR, mdSources, getRepositoryFiles()));
for (const source of mdSources.filter(source => source.hasUpdatedText()))
errors.push(`WARN: updated ${source.projectPath()}`);
errors.push(...preprocessor.findInvalidLinks(PROJECT_DIR, mdSources, getRepositoryFiles()));
const jsSources = await Source.readdir(path.join(PROJECT_DIR, 'src', 'client'), '', []);
errors.push(...missingDocs(outline, jsSources, path.join(PROJECT_DIR, 'src', 'client', 'api.ts')));
@ -209,27 +213,41 @@ function getRepositoryFiles() {
return files.map(file => path.join(PROJECT_DIR, file));
}
const memberLinks = new Map();
const rMemberLinks = new Map();
/**
* @param {string} file
* @param {string} text
*/
function createMemberLink(text) {
if (memberLinks.has(text))
return memberLinks.get(text);
const baseAnchor = text.toLowerCase().split(',').map(c => c.replace(/[^a-z]/g, '')).join('-');
let anchor = baseAnchor;
function createLink(file, text) {
const key = file + '#' + text;
if (links.has(key))
return links.get(key);
const baseLink = file + '#' + text.toLowerCase().split(',').map(c => c.replace(/[^a-z]/g, '')).join('-');
let link = baseLink;
let index = 0;
while (rMemberLinks.has(anchor))
anchor = baseAnchor + '-' + (++index);
const result = `[${text}](#${anchor})`;
memberLinks.set(text, result);
rMemberLinks.set(anchor, text);
while (rLinks.has(link))
link = baseLink + '-' + (++index);
const result = `[${text}](${link})`;
links.set(key, result);
rLinks.set(link, text);
return result;
};
/**
* @param {Documentation.Member} member
* @return {string}
*/
function createMemberLink(member) {
const file = `./api.md`;
if (member.kind === 'property')
return createLink(file, `${member.clazz.varName}.${member.name}`);
if (member.kind === 'event')
return createLink(file, `${member.clazz.varName}.on('${member.name}')`);
if (member.kind === 'method')
return createLink(file, `${member.clazz.varName}.${member.name}(${member.signature})`);
}
/**
* @param {Documentation.Class} clazz
* @return {MarkdownNode[]}
@ -238,28 +256,15 @@ function generateToc(clazz) {
/** @type {MarkdownNode[]} */
const result = [];
for (const member of clazz.membersArray) {
if (member.kind === 'property') {
result.push({
type: 'li',
liType: 'default',
text: createMemberLink(`${clazz.varName}.${member.name}`)
});
} else if (member.kind === 'event') {
result.push({
type: 'li',
liType: 'default',
text: createMemberLink(`${clazz.varName}.on('${member.name}')`)
});
} else if (member.kind === 'method') {
result.push({
type: 'li',
liType: 'default',
text: createMemberLink(`${clazz.varName}.${member.name}(${member.signature})`)
});
}
result.push({
type: 'li',
liType: 'default',
text: createMemberLink(member)
});
}
return result;
}
/**
* @param {string} name
* @param {Type} type

View file

@ -23,7 +23,7 @@ const PROJECT_DIR = path.join(__dirname, '..', '..');
{
const outline = new MDOutline(path.join(PROJECT_DIR, 'docs-src', 'api-body.md'), path.join(PROJECT_DIR, 'docs-src', 'api-params.md'));
outline.renderLinks(item => {
outline.setLinkRenderer(item => {
const { clazz, member, param, option } = item;
if (param)
return `\`${param}\``;

View file

@ -14,6 +14,8 @@
* limitations under the License.
*/
//@ts-check
const path = require('path');
function runCommands(sources, {libversion, chromiumVersion, firefoxVersion, webkitVersion}) {
@ -58,13 +60,11 @@ function runCommands(sources, {libversion, chromiumVersion, firefoxVersion, webk
newText = generateTableOfContents(source.text(), to, false /* topLevelOnly */);
else if (commandName === 'toc-top-level')
newText = generateTableOfContents(source.text(), to, true /* topLevelOnly */);
else if (commandName.startsWith('toc-extends-'))
newText = generateTableOfContentsForSuperclass(source.text(), 'class: ' + commandName.substring('toc-extends-'.length));
if (newText === null)
errors.push(`Unknown command 'gen:${commandName}'`);
else
sourceEdits.edit(from, to, newText);
sourceEdits.edit(from, to, newText);
}
sourceEdits.commit(errors);
}
@ -105,10 +105,7 @@ function getTOCEntriesForText(text) {
return tocEntries;
}
/**
* @param {string} text
*/
function autocorrectInvalidLinks(projectRoot, sources, allowedFilePaths) {
function findInvalidLinks(projectRoot, sources, allowedFilePaths) {
const pathToHashLinks = new Map();
for (const source of sources) {
const text = source.text();
@ -123,7 +120,6 @@ function autocorrectInvalidLinks(projectRoot, sources, allowedFilePaths) {
allRelativePaths.push('/' + path.relative(projectRoot, filepath));
allRelativePaths.push(path.relative(path.dirname(source.filePath()), filepath));
}
const sourceEdits = new SourceEdits(source);
let offset = 0;
const lines = source.text().split('\n');
@ -136,37 +132,25 @@ function autocorrectInvalidLinks(projectRoot, sources, allowedFilePaths) {
if (href.startsWith('http://') || href.startsWith('https://') || href.startsWith('mailto:'))
continue;
const [relativePath, hash] = href.split('#');
const hashOffset = hrefOffset + relativePath.length + 1;
let resolvedPath = resolveLinkPath(source, relativePath);
let hashLinks = pathToHashLinks.get(resolvedPath);
if (!hashLinks) {
// Attempt to autocorrect
const newRelativePath = autocorrectText(relativePath, allRelativePaths);
if (!newRelativePath) {
errors.push(`Bad link in ${source.projectPath()}:${lineNumber + 1}: file ${relativePath} does not exist`);
continue;
}
resolvedPath = resolveLinkPath(source, newRelativePath);
hashLinks = pathToHashLinks.get(resolvedPath);
sourceEdits.edit(hrefOffset, hrefOffset + relativePath.length, newRelativePath);
if (relativePath !== newRelativePath)
errors.push(`Bad link in ${source.projectPath()}:${lineNumber + 1}: file ${relativePath} does not exist, did you mean ${newRelativePath}?`);
continue;
}
if (!hash || hashLinks.has(hash))
continue;
const newHashLink = autocorrectText(hash, [...hashLinks]);
if (newHashLink) {
sourceEdits.edit(hashOffset, hashOffset + hash.length, newHashLink);
} else {
errors.push(`Bad link in ${source.projectPath()}:${lineNumber + 1}: hash "#${hash}" does not exist in "${path.relative(projectRoot, resolvedPath)}"`);
}
if (hash !== newHashLink)
errors.push(`Bad link in ${source.projectPath()}:${lineNumber + 1}: hash "#${hash}" does not exist in "${path.relative(projectRoot, resolvedPath)}", did you mean ${newHashLink}?`);
}
offset += line.length;
});
sourceEdits.commit(errors);
}
return errors;
@ -272,16 +256,4 @@ function generateTableOfContents(text, offset, topLevelOnly) {
}).join('\n') + '\n';
}
function generateTableOfContentsForSuperclass(text, name) {
const allTocEntries = getTOCEntriesForText(text);
for (const tocEntry of allTocEntries) {
if (tocEntry.name !== name)
continue;
const offset = text.indexOf('<!-- GEN:stop -->', tocEntry.offset);
return generateTableOfContents(text, offset, false);
}
return text;
}
module.exports = {autocorrectInvalidLinks, runCommands};
module.exports = {findInvalidLinks, runCommands};

View file

@ -40,9 +40,9 @@ let hadChanges = false;
outline.copyDocsFromSuperclasses([]);
const createMemberLink = (text) => {
const anchor = text.toLowerCase().split(',').map(c => c.replace(/[^a-z]/g, '')).join('-');
return `[\`${text}\`](https://github.com/microsoft/playwright/blob/master/docs/api.md#${anchor})`;
return `[${text}](https://github.com/microsoft/playwright/blob/master/docs/api.md#${anchor})`;
};
outline.renderLinks(item => {
outline.setLinkRenderer(item => {
const { clazz, member, param, option } = item;
if (param)
return `\`${param}\``;
@ -261,9 +261,9 @@ function parentClass(classDesc) {
function writeComment(comment, indent = '') {
const parts = [];
comment = comment.replace(/\[`([^`]+)`\]\(#([^\)]+)\)/g, '[`$1`](https://github.com/microsoft/playwright/blob/master/docs/api.md#$2)');
comment = comment.replace(/\[`([^`]+)`\]\(#([^\)]+)\)/g, '[$1](https://github.com/microsoft/playwright/blob/master/docs/api.md#$2)');
comment = comment.replace(/\[([^\]]+)\]\(#([^\)]+)\)/g, '[$1](https://github.com/microsoft/playwright/blob/master/docs/api.md#$2)');
comment = comment.replace(/\[`([^`]+)`\]\(\.\/([^\)]+)\)/g, '[`$1`](https://github.com/microsoft/playwright/blob/master/docs/$2)');
comment = comment.replace(/\[`([^`]+)`\]\(\.\/([^\)]+)\)/g, '[$1](https://github.com/microsoft/playwright/blob/master/docs/$2)');
comment = comment.replace(/\[([^\]]+)\]\(\.\/([^\)]+)\)/g, '[$1](https://github.com/microsoft/playwright/blob/master/docs/$2)');
parts.push(indent + '/**');

View file

@ -17,7 +17,7 @@
// @ts-check
/** @typedef {{
* type: 'text' | 'li' | 'code' | 'gen' | 'h0' | 'h1' | 'h2' | 'h3' | 'h4',
* type: 'text' | 'li' | 'code' | 'h0' | 'h1' | 'h2' | 'h3' | 'h4',
* text?: string,
* codeLang?: string,
* lines?: string[],
@ -25,33 +25,35 @@
* children?: MarkdownNode[]
* }} MarkdownNode */
function normalizeLines(content) {
function flattenWrappedLines(content) {
const inLines = content.replace(/\r\n/g, '\n').split('\n');
let inCodeBlock = false;
const outLines = [];
let outLineTokens = [];
for (const line of inLines) {
const trimmedLine = line.trim();
let singleLineExpression = line.startsWith('#');
let flushParagraph = !line.trim()
|| line.trim().startsWith('1.')
|| line.trim().startsWith('<')
|| line.trim().startsWith('>')
|| line.trim().startsWith('-')
|| line.trim().startsWith('*')
let flushLastParagraph = !trimmedLine
|| trimmedLine.startsWith('1.')
|| trimmedLine.startsWith('<')
|| trimmedLine.startsWith('>')
|| trimmedLine.startsWith('|')
|| trimmedLine.startsWith('-')
|| trimmedLine.startsWith('*')
|| line.match(/\[[^\]]+\]:.*/)
|| singleLineExpression;
if (line.startsWith('```')) {
if (trimmedLine.startsWith('```')) {
inCodeBlock = !inCodeBlock;
flushParagraph = true;
flushLastParagraph = true;
}
if (flushParagraph && outLineTokens.length) {
if (flushLastParagraph && outLineTokens.length) {
outLines.push(outLineTokens.join(' '));
outLineTokens = [];
}
const trimmedLine = line.trim();
if (inCodeBlock || singleLineExpression)
outLines.push(line);
else if (trimmedLine)
outLineTokens.push(trimmedLine.startsWith('-') ? line : trimmedLine);
outLineTokens.push(outLineTokens.length ? line.trim() : line);
}
if (outLineTokens.length)
outLines.push(outLineTokens.join(' '));
@ -68,81 +70,82 @@ function buildTree(lines) {
text: '<root>',
children: []
};
/** @type {MarkdownNode[]} */
const stack = [root];
/** @type {MarkdownNode[]} */
let liStack = null;
const headerStack = [root];
/** @type {{ indent: string, node: MarkdownNode }[]} */
let sectionStack = [];
/**
* @param {string} indent
* @param {MarkdownNode} node
*/
const appendNode = (indent, node) => {
while (sectionStack.length && sectionStack[0].indent.length >= indent.length)
sectionStack.shift();
const parentNode = sectionStack.length ? sectionStack[0].node :headerStack[0];
if (!parentNode.children)
parentNode.children = [];
parentNode.children.push(node);
if (node.type === 'li')
sectionStack.unshift({ indent, node });
};
for (let i = 0; i < lines.length; ++i) {
let line = lines[i];
if (line.startsWith('```')) {
/** @type {MarkdownNode} */
const node = {
type: 'code',
lines: [],
codeLang: line.substring(3)
};
stack[0].children.push(node);
line = lines[++i];
while (!line.startsWith('```')) {
node.lines.push(line);
line = lines[++i];
}
continue;
}
if (line.startsWith('<!-- GEN')) {
/** @type {MarkdownNode} */
const node = {
type: 'gen',
lines: [line]
};
stack[0].children.push(node);
line = lines[++i];
while (!line.startsWith('<!-- GEN')) {
node.lines.push(line);
line = lines[++i];
}
node.lines.push(line);
continue;
}
// Headers form hierarchy.
const header = line.match(/^(#+)/);
if (header) {
const h = header[1].length;
const node = /** @type {MarkdownNode} */({ type: 'h' + h, text: line.substring(h + 1), children: [] });
while (true) {
const lastH = +stack[0].type.substring(1);
const lastH = +headerStack[0].type.substring(1);
if (h <= lastH)
stack.shift();
headerStack.shift();
else
break;
}
stack[0].children.push(node);
stack.unshift(node);
liStack = [node];
headerStack[0].children.push(node);
headerStack.unshift(node);
continue;
}
const list = line.match(/^(\s*)(-|1.|\*) /);
const depth = list ? (list[1].length / 2) : 0;
const node = /** @type {MarkdownNode} */({ type: 'text', text: line });
if (list) {
// Remaining items respect indent-based nesting.
const [, indent, content] = line.match('^([ ]*)(.*)');
if (content.startsWith('```')) {
/** @type {MarkdownNode} */
const node = {
type: 'code',
lines: [],
codeLang: content.substring(3)
};
line = lines[++i];
while (!line.trim().startsWith('```')) {
if (!line.startsWith(indent))
throw new Error('Bad code block ' + line);
node.lines.push(line.substring(indent.length));
line = lines[++i];
}
appendNode(indent, node);
continue;
}
const liType = content.match(/^(-|1.|\*) /);
const node = /** @type {MarkdownNode} */({ type: 'text', text: content });
if (liType) {
node.type = 'li';
node.text = line.substring(list[0].length);
if (line.trim().startsWith('1.'))
node.text = content.substring(liType[0].length);
if (content.startsWith('1.'))
node.liType = 'ordinal';
else if (line.trim().startsWith('*'))
else if (content.startsWith('*'))
node.liType = 'bullet';
else
node.liType = 'default';
}
if (!liStack[depth].children)
liStack[depth].children = [];
liStack[depth].children.push(node);
liStack[depth + 1] = node;
appendNode(indent, node);
}
return root.children;
}
@ -151,7 +154,7 @@ function buildTree(lines) {
* @param {string} content
*/
function parse(content) {
return buildTree(normalizeLines(content));
return buildTree(flattenWrappedLines(content));
}
/**
@ -162,19 +165,20 @@ function render(nodes, maxColumns) {
const result = [];
let lastNode;
for (let node of nodes) {
innerRenderMdNode(node, lastNode, result, maxColumns);
innerRenderMdNode('', node, lastNode, result, maxColumns);
lastNode = node;
}
return result.join('\n');
}
/**
* @param {string} indent
* @param {MarkdownNode} node
* @param {MarkdownNode} lastNode
* @param {number=} maxColumns
* @param {string[]} result
*/
function innerRenderMdNode(node, lastNode, result, maxColumns) {
function innerRenderMdNode(indent, node, lastNode, result, maxColumns) {
const newLine = () => {
if (result[result.length - 1] !== '')
result.push('');
@ -186,54 +190,50 @@ function innerRenderMdNode(node, lastNode, result, maxColumns) {
result.push(`${'#'.repeat(depth)} ${node.text}`);
let lastNode = node;
for (const child of node.children || []) {
innerRenderMdNode(child, lastNode, result, maxColumns);
innerRenderMdNode('', child, lastNode, result, maxColumns);
lastNode = child;
}
}
if (node.type === 'text') {
const bothTables = node.text.startsWith('|') && lastNode && lastNode.type === 'text' && lastNode.text.startsWith('|');
const bothGen = node.text.startsWith('<!--') && lastNode && lastNode.type === 'text' && lastNode.text.startsWith('<!--');
const bothComments = node.text.startsWith('>') && lastNode && lastNode.type === 'text' && lastNode.text.startsWith('>');
if (!bothComments && lastNode && lastNode.text)
const bothLinks = node.text.match(/\[[^\]]+\]:/) && lastNode && lastNode.type === 'text' && lastNode.text.match(/\[[^\]]+\]:/);
if (!bothTables && !bothGen && !bothComments && !bothLinks && lastNode && lastNode.text)
newLine();
result.push(wrapText(node.text, maxColumns));
result.push(wrapText(node.text, maxColumns, indent));
}
if (node.type === 'code') {
newLine();
result.push('```' + node.codeLang);
result.push(`${indent}\`\`\`${node.codeLang}`);
for (const line of node.lines)
result.push(line);
result.push('```');
newLine();
}
if (node.type === 'gen') {
newLine();
for (const line of node.lines)
result.push(line);
result.push(indent + line);
result.push(`${indent}\`\`\``);
newLine();
}
if (node.type === 'li') {
const visit = (node, indent) => {
let char;
switch (node.liType) {
case 'bullet': char = '*'; break;
case 'default': char = '-'; break;
case 'ordinal': char = '1.'; break;
}
result.push(`${indent}${char} ${wrapText(node.text, maxColumns, indent + ' '.repeat(char.length + 1))}`);
for (const child of node.children || [])
visit(child, indent + ' ');
};
visit(node, '');
let char;
switch (node.liType) {
case 'bullet': char = '*'; break;
case 'default': char = '-'; break;
case 'ordinal': char = '1.'; break;
}
result.push(`${wrapText(node.text, maxColumns, `${indent}${char} `)}`);
const newIndent = indent + ' '.repeat(char.length + 1);
for (const child of node.children || []) {
innerRenderMdNode(newIndent, child, lastNode, result, maxColumns);
lastNode = child;
}
}
}
/**
* @param {string} text
*/
function tokenizeText(text) {
function tokenizeNoBreakLinks(text) {
const links = [];
// Don't wrap simple links with spaces.
text = text.replace(/\[[^\]]+\]/g, match => {
@ -246,14 +246,17 @@ function tokenizeText(text) {
/**
* @param {string} text
* @param {number=} maxColumns
* @param {string=} indent
* @param {string=} prefix
*/
function wrapText(text, maxColumns = 0, indent = '') {
function wrapText(text, maxColumns = 0, prefix = '') {
if (!maxColumns)
return text;
return prefix + text;
if (text.trim().startsWith('|'))
return prefix + text;
const indent = ' '.repeat(prefix.length);
const lines = [];
maxColumns -= indent.length;
const words = tokenizeText(text);
const words = tokenizeNoBreakLinks(text);
let line = '';
for (const word of words) {
if (line.length && line.length + word.length < maxColumns) {
@ -261,7 +264,7 @@ function wrapText(text, maxColumns = 0, indent = '') {
} else {
if (line)
lines.push(line);
line = (lines.length ? indent : '') + word;
line = (lines.length ? indent : prefix) + word;
}
}
if (line)