docs: generate all docs off docs-src (#4858)
This commit is contained in:
parent
75198f044d
commit
ba291372e7
56
docs-src/actionability.md
Normal file
56
docs-src/actionability.md
Normal 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"
|
||||
|
|
@ -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
121
docs-src/assertions.md
Normal 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
166
docs-src/auth.md
Normal 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
272
docs-src/ci.md
Normal 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 you’re 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
143
docs-src/cli.md
Normal 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
350
docs-src/core-concepts.md
Normal 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
126
docs-src/debug.md
Normal 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
179
docs-src/emulation.md
Normal 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
44
docs-src/extensibility.md
Normal 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
277
docs-src/input.md
Normal 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
142
docs-src/installation.md
Normal 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
106
docs-src/intro.md
Normal 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
51
docs-src/languages.md
Normal 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
38
docs-src/mobile.md
Normal 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
121
docs-src/multi-pages.md
Normal 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
155
docs-src/navigations.md
Normal 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
189
docs-src/network.md
Normal 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
53
docs-src/pom.md
Normal 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
263
docs-src/selectors.md
Normal 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
57
docs-src/showcase.md
Normal 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
112
docs-src/test-runners.md
Normal 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.
|
||||
|
||||
33
docs-src/troubleshooting.md
Normal file
33
docs-src/troubleshooting.md
Normal 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
153
docs-src/verification.md
Normal 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`]
|
||||
52
docs-src/why-playwright.md
Normal file
52
docs-src/why-playwright.md
Normal 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).
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
1459
docs/api.md
1459
docs/api.md
File diff suppressed because it is too large
Load diff
|
|
@ -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"
|
||||
|
|
|
|||
139
docs/auth.md
139
docs/auth.md
|
|
@ -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"
|
||||
|
|
|
|||
214
docs/ci.md
214
docs/ci.md
|
|
@ -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 you’re using Playwright through Jest, then you may encounter an error spawning child processes:
|
||||
1. If you’re 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"
|
||||
|
|
|
|||
97
docs/cli.md
97
docs/cli.md
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
120
docs/debug.md
120
docs/debug.md
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
222
docs/input.md
222
docs/input.md
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
111
docs/intro.md
111
docs/intro.md
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
100
docs/network.md
100
docs/network.md
|
|
@ -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"
|
||||
|
|
|
|||
75
docs/pom.md
75
docs/pom.md
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
181
docs/showcase.md
181
docs/showcase.md
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
927
types/types.d.ts
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ class Source {
|
|||
this._name = path.basename(filePath);
|
||||
this._text = text;
|
||||
this._originalText = text;
|
||||
this._hasUpdatedText = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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}\``;
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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 + '/**');
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue