Compare commits

...

16 commits

Author SHA1 Message Date
Pavel Feldman 11f51455f2 cherry-pick(#13689): fix(tracing): do not reset frame counter on every chunk 2022-04-21 15:41:35 -07:00
Ross Wollman e5d0e42e9c
chore: roll docs to v1.21.1 (#13619) 2022-04-18 15:40:40 -07:00
Ross Wollman 2e29fd431b
chore: mark v1.21.1 (#13617) 2022-04-18 13:49:41 -07:00
Andrey Lushnikov c16a110519
cherry-pick(#13536): revert: chore: print error if install-deps is used != ubuntu (#13539)
Reason: turns out Debian Buster requires just one source list to
install `ttf-ubuntu-font-family` font.

All other dependencies are satisfied.

Fixes #13530
2022-04-18 10:47:42 -07:00
Ross Wollman d1ae23c750
cherry-pick(#13584): avoid premature stop of worker in fullyParallel (#13597)
Prior to this change, we were pre-maturely stopping a worker (since it
was deemed redundant under a race condition), and then we immediately
created a new worker with the same hash to finish off the test run. The
worker creation is expensive, so this slowed down the overall test run
time.

See the following for logs of the old code illustrating the extra stops and starts: https://gist.github.com/rwoll/1c592ed9e8f9169274fa972674de6703
2022-04-18 07:05:38 -07:00
Andrey Lushnikov 953003882a
chore: mark v1.21.0 (#13416) 2022-04-11 17:04:32 -07:00
Andrey Lushnikov 02f4cfd4d7
cherry-pick(#13491): release notes for 1.21 (#13493)
SHA 52e326abd1
2022-04-11 17:02:01 -07:00
Andrey Lushnikov 414cc9b769
cherry-pick(#13487): do not require --force flag when installing channel on CI (#13488)
SHA 0f6638190e
2022-04-11 23:22:08 +02:00
Yury Semikhatsky fe8dad0ac2
docs: mark fetch params as optional again (#13479) (#13480) 2022-04-11 10:07:00 -07:00
Ross Wollman 9dbc124d98
cherry-pick(#13464): fix test.step return type docs (#13478) 2022-04-11 09:58:57 -07:00
Yury Semikhatsky 1756566b17
cherry-pick(#13443, #13407): feat(webkit): roll to r1630 (#13476) 2022-04-11 09:57:59 -07:00
Pavel Feldman 06d100ddcf cherry-pick(#13437): chore: flatten supplements 2022-04-08 13:08:50 -07:00
Yury Semikhatsky bb810f5a09
cherry-pick(#13431): docs(java): clarify source list format (#13434) 2022-04-08 11:51:20 -07:00
Max Schmitt ee0119f503
cherry-pick(#13425): docs(dotnet): fix broken generated docs links (#13430) 2022-04-08 18:32:03 +02:00
Pavel Feldman 14a241e900 cherry-pick(#13417): chore: use utils via index export (6) 2022-04-07 22:35:50 -07:00
Pavel Feldman 18c8862b97 cherry-pick(#13413): chore: use utils via index export (5) 2022-04-07 21:29:35 -07:00
146 changed files with 801 additions and 540 deletions

View file

@ -234,7 +234,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force chrome - run: npx playwright install --with-deps chrome
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest
env: env:
PWTEST_CHANNEL: chrome PWTEST_CHANNEL: chrome
@ -259,7 +259,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force chrome - run: npx playwright install --with-deps chrome
- run: npm run ctest - run: npm run ctest
shell: bash shell: bash
env: env:
@ -286,7 +286,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force chrome - run: npx playwright install --with-deps chrome
- run: npm run ctest - run: npm run ctest
env: env:
PWTEST_CHANNEL: chrome PWTEST_CHANNEL: chrome
@ -388,7 +388,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force msedge - run: npx playwright install --with-deps msedge
- run: npm run ctest - run: npm run ctest
env: env:
PWTEST_CHANNEL: msedge PWTEST_CHANNEL: msedge
@ -414,7 +414,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force msedge - run: npx playwright install --with-deps msedge
- run: npm run ctest - run: npm run ctest
shell: bash shell: bash
env: env:
@ -438,7 +438,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force msedge - run: npx playwright install --with-deps msedge
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest
env: env:
PWTEST_CHANNEL: msedge PWTEST_CHANNEL: msedge
@ -463,7 +463,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force msedge-beta - run: npx playwright install --with-deps msedge-beta
- run: npm run ctest - run: npm run ctest
env: env:
PWTEST_CHANNEL: msedge-beta PWTEST_CHANNEL: msedge-beta
@ -488,7 +488,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force msedge-beta - run: npx playwright install --with-deps msedge-beta
- run: npm run ctest - run: npm run ctest
shell: bash shell: bash
env: env:
@ -512,7 +512,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force msedge-beta - run: npx playwright install --with-deps msedge-beta
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest
env: env:
PWTEST_CHANNEL: msedge-beta PWTEST_CHANNEL: msedge-beta
@ -537,7 +537,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force msedge-dev - run: npx playwright install --with-deps msedge-dev
- run: npm run ctest - run: npm run ctest
env: env:
PWTEST_CHANNEL: msedge-dev PWTEST_CHANNEL: msedge-dev
@ -562,7 +562,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force msedge-dev - run: npx playwright install --with-deps msedge-dev
- run: npm run ctest - run: npm run ctest
shell: bash shell: bash
env: env:
@ -586,7 +586,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force msedge-dev - run: npx playwright install --with-deps msedge-dev
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest
env: env:
PWTEST_CHANNEL: msedge-dev PWTEST_CHANNEL: msedge-dev
@ -611,7 +611,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force chrome-beta - run: npx playwright install --with-deps chrome-beta
- run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- npm run ctest
env: env:
PWTEST_CHANNEL: chrome-beta PWTEST_CHANNEL: chrome-beta
@ -636,7 +636,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force chrome-beta - run: npx playwright install --with-deps chrome-beta
- run: npm run ctest - run: npm run ctest
shell: bash shell: bash
env: env:
@ -663,7 +663,7 @@ jobs:
env: env:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
- run: npm run build - run: npm run build
- run: npx playwright install --with-deps --force chrome-beta - run: npx playwright install --with-deps chrome-beta
- run: npm run ctest - run: npm run ctest
env: env:
PWTEST_CHANNEL: chrome-beta PWTEST_CHANNEL: chrome-beta

View file

@ -140,7 +140,8 @@ Whether to include source files for trace actions.
- `sources` <[boolean]> - `sources` <[boolean]>
Whether to include source files for trace actions. List of the directories with source code for the application Whether to include source files for trace actions. List of the directories with source code for the application
must be provided via `PLAYWRIGHT_JAVA_SRC` environment variable. must be provided via `PLAYWRIGHT_JAVA_SRC` environment variable (the paths should be separated by ';' on Windows
and by ':' on other platforms).
### option: Tracing.start.title ### option: Tracing.start.title
- `title` <[string]> - `title` <[string]>

View file

@ -312,7 +312,7 @@ Query parameters to be sent with the URL.
## java-fetch-params ## java-fetch-params
* langs: java * langs: java
- `options` <[RequestOptions]> - `options` ?<[RequestOptions]>
Optional request parameters. Optional request parameters.
@ -552,7 +552,7 @@ call [`method: BrowserContext.close`] for the HAR to be saved.
## context-option-recordhar-omit-content ## context-option-recordhar-omit-content
* langs: csharp, java, python * langs: csharp, java, python
- alias-python: record_har_omit_content - alias-python: record_har_omit_content
- `recordHarOmitContent` <[boolean]> - `recordHarOmitContent` ?<[boolean]>
Optional setting to control whether to omit request content from the HAR. Defaults to `false`. Optional setting to control whether to omit request content from the HAR. Defaults to `false`.

View file

@ -143,7 +143,7 @@ Alternatively, you can use [Command line tools](./cli.md#install-system-dependen
pool: pool:
vmImage: 'ubuntu-20.04' vmImage: 'ubuntu-20.04'
container: mcr.microsoft.com/playwright:v1.21.0-focal container: mcr.microsoft.com/playwright:v1.21.1-focal
steps: steps:
... ...
@ -157,7 +157,7 @@ Running Playwright on CircleCI requires the following steps:
```yml ```yml
docker: docker:
- image: mcr.microsoft.com/playwright:v1.21.0-focal - image: mcr.microsoft.com/playwright:v1.21.1-focal
environment: environment:
NODE_ENV: development # Needed if playwright is in `devDependencies` NODE_ENV: development # Needed if playwright is in `devDependencies`
``` ```
@ -179,7 +179,7 @@ to run tests on Jenkins.
```groovy ```groovy
pipeline { pipeline {
agent { docker { image 'mcr.microsoft.com/playwright:v1.21.0-focal' } } agent { docker { image 'mcr.microsoft.com/playwright:v1.21.1-focal' } }
stages { stages {
stage('e2e-tests') { stage('e2e-tests') {
steps { steps {
@ -196,7 +196,7 @@ pipeline {
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.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.md)).
```yml ```yml
image: mcr.microsoft.com/playwright:v1.21.0-focal image: mcr.microsoft.com/playwright:v1.21.1-focal
``` ```
### GitLab CI ### GitLab CI
@ -209,7 +209,7 @@ stages:
tests: tests:
stage: test stage: test
image: mcr.microsoft.com/playwright:v1.21.0-focal image: mcr.microsoft.com/playwright:v1.21.1-focal
script: script:
... ...
``` ```

View file

@ -16,19 +16,19 @@ This image is published on [Docker Hub].
Replace 1.20.0 with your Playwright version: Replace 1.20.0 with your Playwright version:
```bash js ```bash js
docker pull mcr.microsoft.com/playwright:v1.21.0-focal docker pull mcr.microsoft.com/playwright:v1.21.1-focal
``` ```
```bash python ```bash python
docker pull mcr.microsoft.com/playwright/python:v1.21.0-focal docker pull mcr.microsoft.com/playwright/python:v1.21.1-focal
``` ```
```bash csharp ```bash csharp
docker pull mcr.microsoft.com/playwright/dotnet:v1.21.0-focal docker pull mcr.microsoft.com/playwright/dotnet:v1.21.1-focal
``` ```
```bash java ```bash java
docker pull mcr.microsoft.com/playwright/java:v1.21.0-focal docker pull mcr.microsoft.com/playwright/java:v1.21.1-focal
``` ```
### Run the image ### Run the image
@ -40,19 +40,19 @@ By default, the Docker image will use the `root` user to run the browsers. This
On trusted websites, you can avoid creating a separate user and use root for it since you trust the code which will run on the browsers. On trusted websites, you can avoid creating a separate user and use root for it since you trust the code which will run on the browsers.
```bash js ```bash js
docker run -it --rm --ipc=host mcr.microsoft.com/playwright:v1.21.0-focal /bin/bash docker run -it --rm --ipc=host mcr.microsoft.com/playwright:v1.21.1-focal /bin/bash
``` ```
```bash python ```bash python
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/python:v1.21.0-focal /bin/bash docker run -it --rm --ipc=host mcr.microsoft.com/playwright/python:v1.21.1-focal /bin/bash
``` ```
```bash csharp ```bash csharp
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/dotnet:v1.21.0-focal /bin/bash docker run -it --rm --ipc=host mcr.microsoft.com/playwright/dotnet:v1.21.1-focal /bin/bash
``` ```
```bash java ```bash java
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/java:v1.21.0-focal /bin/bash docker run -it --rm --ipc=host mcr.microsoft.com/playwright/java:v1.21.1-focal /bin/bash
``` ```
#### Crawling and scraping #### Crawling and scraping
@ -60,19 +60,19 @@ docker run -it --rm --ipc=host mcr.microsoft.com/playwright/java:v1.21.0-focal /
On untrusted websites, it's recommended to use a separate user for launching the browsers in combination with the seccomp profile. Inside the container or if you are using the Docker image as a base image you have to use `adduser` for it. On untrusted websites, it's recommended to use a separate user for launching the browsers in combination with the seccomp profile. Inside the container or if you are using the Docker image as a base image you have to use `adduser` for it.
```bash js ```bash js
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright:v1.21.0-focal /bin/bash docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright:v1.21.1-focal /bin/bash
``` ```
```bash python ```bash python
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/python:v1.21.0-focal /bin/bash docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/python:v1.21.1-focal /bin/bash
``` ```
```bash csharp ```bash csharp
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/dotnet:v1.21.0-focal /bin/bash docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/dotnet:v1.21.1-focal /bin/bash
``` ```
```bash java ```bash java
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/java:v1.21.0-focal /bin/bash docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/java:v1.21.1-focal /bin/bash
``` ```
[`seccomp_profile.json`](https://github.com/microsoft/playwright/blob/main/utils/docker/seccomp_profile.json) is needed to run Chromium with sandbox. This is a [default Docker seccomp profile](https://github.com/docker/engine/blob/d0d99b04cf6e00ed3fc27e81fc3d94e7eda70af3/profiles/seccomp/default.json) with extra user namespace cloning permissions: [`seccomp_profile.json`](https://github.com/microsoft/playwright/blob/main/utils/docker/seccomp_profile.json) is needed to run Chromium with sandbox. This is a [default Docker seccomp profile](https://github.com/docker/engine/blob/d0d99b04cf6e00ed3fc27e81fc3d94e7eda70af3/profiles/seccomp/default.json) with extra user namespace cloning permissions:

View file

@ -31,17 +31,20 @@ configures Playwright for debugging and opens the inspector.
``` ```
```bash bash-flavor=bash lang=java ```bash bash-flavor=bash lang=java
PWDEBUG=1 PLAYWRIGHT_JAVA_SRC=<java src root> mvn test # Source directories in the list are separated by : on macos and linux and by ; on win.
PWDEBUG=1 PLAYWRIGHT_JAVA_SRC=<java source dirs> mvn test
``` ```
```bash bash-flavor=batch lang=java ```bash bash-flavor=batch lang=java
set PLAYWRIGHT_JAVA_SRC=<java src root> # Source directories in the list are separated by : on macos and linux and by ; on win.
set PLAYWRIGHT_JAVA_SRC=<java source dirs>
set PWDEBUG=1 set PWDEBUG=1
mvn test mvn test
``` ```
```bash bash-flavor=powershell lang=java ```bash bash-flavor=powershell lang=java
$env:PLAYWRIGHT_JAVA_SRC="<java src root>" # Source directories in the list are separated by : on macos and linux and by ; on win.
$env:PLAYWRIGHT_JAVA_SRC="<java source dirs>"
$env:PWDEBUG=1 $env:PWDEBUG=1
mvn test mvn test
``` ```

View file

@ -5,6 +5,40 @@ title: "Release notes"
<!-- TOC --> <!-- TOC -->
## Version 1.21
### Highlights
- New **experimental** role selectors that allow selecting elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles), [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
```csharp
// Click a button with accessible name "log in"
await page.ClickAsync("role=button[name='log in']")
```
To use role selectors, make sure to pass `PLAYWRIGHT_EXPERIMENTAL_FEATURES=1` environment variable.
Read more in [our documentation](./selectors#role-selector).
- New `scale` option in [`method: Page.screenshot`] for smaller sized screenshots.
- New `caret` option in [`method: Page.screenshot`] to control text caret. Defaults to `"hide"`.
- We now ship a designated .NET docker image `mcr.microsoft.com/playwright/dotnet`. Read more in [our documentation](./docker).
### Behavior Changes
- Playwright now supports large file uploads (100s of MBs) via [`method: Locator.setInputFiles`] API.
### Browser Versions
- Chromium 101.0.4951.26
- Mozilla Firefox 98.0.2
- WebKit 15.4
This version was also tested against the following stable channels:
- Google Chrome 100
- Microsoft Edge 100
## Version 1.20 ## Version 1.20
### Web-First Assertions ### Web-First Assertions

View file

@ -5,6 +5,39 @@ title: "Release notes"
<!-- TOC --> <!-- TOC -->
## Version 1.21
### Highlights
- New **experimental** role selectors that allow selecting elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles), [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
```java
// Click a button with accessible name "log in"
page.click("role=button[name='log in']")
```
To use role selectors, make sure to pass `PLAYWRIGHT_EXPERIMENTAL_FEATURES=1` environment variable.
Read more in [our documentation](./selectors#role-selector).
- New `scale` option in [`method: Page.screenshot`] for smaller sized screenshots.
- New `caret` option in [`method: Page.screenshot`] to control text caret. Defaults to `"hide"`.
### Behavior Changes
- Playwright now supports large file uploads (100s of MBs) via [`method: Locator.setInputFiles`] API.
### Browser Versions
- Chromium 101.0.4951.26
- Mozilla Firefox 98.0.2
- WebKit 15.4
This version was also tested against the following stable channels:
- Google Chrome 100
- Microsoft Edge 100
## Version 1.20 ## Version 1.20
### Highlights ### Highlights

View file

@ -5,6 +5,64 @@ title: "Release notes"
<!-- TOC --> <!-- TOC -->
## Version 1.21
### Highlights
- New **experimental** role selectors that allow selecting elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles), [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
```js
// Click a button with accessible name "log in"
await page.click('role=button[name="log in"]')
```
To use role selectors, make sure to pass `PLAYWRIGHT_EXPERIMENTAL_FEATURES=1` environment variable:
```js
// playwright.config.js
process.env.PLAYWRIGHT_EXPERIMENTAL_FEATURES = '1';
module.exports = {
/* ... */
};
```
Read more in [our documentation](./selectors#role-selector).
- New `scale` option in [`method: Page.screenshot`] for smaller sized screenshots.
- New `caret` option in [`method: Page.screenshot`] to control text caret. Defaults to `"hide"`.
- New method `expect.poll` to wait for an arbitrary condition:
```js
// Poll the method until it returns an expected result.
await expect.poll(async () => {
const response = await page.request.get('https://api.example.com');
return response.status();
}).toBe(200);
```
`expect.poll` supports most synchronous matchers, like `.toBe()`, `.toContain()`, etc.
Read more in [our documentation](./test-assertions.md#polling).
### Behavior Changes
- ESM support when running TypeScript tests is now enabled by default. The `PLAYWRIGHT_EXPERIMENTAL_TS_ESM` env variable is
no longer required.
- The `mcr.microsoft.com/playwright` docker image no longer contains Python. Please use `mcr.microsoft.com/playwright/python`
as a Playwright-ready docker image with pre-installed Python.
- Playwright now supports large file uploads (100s of MBs) via [`method: Locator.setInputFiles`] API.
### Browser Versions
- Chromium 101.0.4951.26
- Mozilla Firefox 98.0.2
- WebKit 15.4
This version was also tested against the following stable channels:
- Google Chrome 100
- Microsoft Edge 100
## Version 1.20 ## Version 1.20
### Highlights ### Highlights

View file

@ -5,6 +5,46 @@ title: "Release notes"
<!-- TOC --> <!-- TOC -->
## Version 1.21
### Highlights
- New **experimental** role selectors that allow selecting elements by their [ARIA role](https://www.w3.org/TR/wai-aria-1.2/#roles), [ARIA attributes](https://www.w3.org/TR/wai-aria-1.2/#aria-attributes) and [accessible name](https://w3c.github.io/accname/#dfn-accessible-name).
```python async
# Click a button with accessible name "log in"
await page.click("role=button[name='log in']")
```
```python sync
# Click a button with accessible name "log in"
page.click("role=button[name='log in']")
```
To use role selectors, make sure to pass `PLAYWRIGHT_EXPERIMENTAL_FEATURES=1` environment variable.
Read more in [our documentation](./selectors#role-selector).
- New `scale` option in [`method: Page.screenshot`] for smaller sized screenshots.
- New `caret` option in [`method: Page.screenshot`] to control text caret. Defaults to `"hide"`.
### Behavior Changes
- The `mcr.microsoft.com/playwright` docker image no longer contains Python. Please use `mcr.microsoft.com/playwright/python`
as a Playwright-ready docker image with pre-installed Python.
- Playwright now supports large file uploads (100s of MBs) via [`method: Locator.setInputFiles`] API.
### Browser Versions
- Chromium 101.0.4951.26
- Mozilla Firefox 98.0.2
- WebKit 15.4
This version was also tested against the following stable channels:
- Google Chrome 100
- Microsoft Edge 100
## Version 1.20 ## Version 1.20
### Highlights ### Highlights

View file

@ -1249,7 +1249,7 @@ A function that returns whether to mark as "slow", based on test fixtures. Test
Optional description that will be reflected in a test report. Optional description that will be reflected in a test report.
## method: Test.step ## async method: Test.step
Declares a test step. Declares a test step.

View file

@ -56,7 +56,7 @@ The snapshot name `example-test-1-chromium-darwin.png` consists of a few parts:
If you are not on the same operating system as your CI system, you can use Docker to generate/update the screenshots: If you are not on the same operating system as your CI system, you can use Docker to generate/update the screenshots:
```bash ```bash
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.21.0-focal /bin/bash docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.21.1-focal /bin/bash
npm install npm install
npx playwright test --update-snapshots npx playwright test --update-snapshots
``` ```

48
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "playwright-internal", "name": "playwright-internal",
"version": "1.21.0-next", "version": "1.21.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "playwright-internal", "name": "playwright-internal",
"version": "1.21.0-next", "version": "1.21.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
@ -6066,11 +6066,11 @@
"version": "0.0.0" "version": "0.0.0"
}, },
"packages/playwright": { "packages/playwright": {
"version": "1.21.0-next", "version": "1.21.1",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.21.0-next" "playwright-core": "1.21.1"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -6080,11 +6080,11 @@
} }
}, },
"packages/playwright-chromium": { "packages/playwright-chromium": {
"version": "1.21.0-next", "version": "1.21.1",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.21.0-next" "playwright-core": "1.21.1"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -6094,7 +6094,7 @@
} }
}, },
"packages/playwright-core": { "packages/playwright-core": {
"version": "1.21.0-next", "version": "1.21.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"colors": "1.4.0", "colors": "1.4.0",
@ -6136,7 +6136,7 @@
"version": "0.0.2", "version": "0.0.2",
"license": "Apache-2.0", "license": "Apache-2.0",
"devDependencies": { "devDependencies": {
"@playwright/test": "1.21.0-next" "@playwright/test": "1.21.1"
}, },
"engines": { "engines": {
"node": ">=12" "node": ">=12"
@ -6147,7 +6147,7 @@
"version": "0.0.2", "version": "0.0.2",
"license": "Apache-2.0", "license": "Apache-2.0",
"devDependencies": { "devDependencies": {
"@playwright/test": "1.21.0-next" "@playwright/test": "1.21.1"
}, },
"engines": { "engines": {
"node": ">=12" "node": ">=12"
@ -6158,18 +6158,18 @@
"version": "0.0.2", "version": "0.0.2",
"license": "Apache-2.0", "license": "Apache-2.0",
"devDependencies": { "devDependencies": {
"@playwright/test": "1.21.0-next" "@playwright/test": "1.21.1"
}, },
"engines": { "engines": {
"node": ">=12" "node": ">=12"
} }
}, },
"packages/playwright-firefox": { "packages/playwright-firefox": {
"version": "1.21.0-next", "version": "1.21.1",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.21.0-next" "playwright-core": "1.21.1"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -6180,7 +6180,7 @@
}, },
"packages/playwright-test": { "packages/playwright-test": {
"name": "@playwright/test", "name": "@playwright/test",
"version": "1.21.0-next", "version": "1.21.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@babel/code-frame": "7.16.7", "@babel/code-frame": "7.16.7",
@ -6212,7 +6212,7 @@
"ms": "2.1.3", "ms": "2.1.3",
"open": "8.4.0", "open": "8.4.0",
"pirates": "4.0.4", "pirates": "4.0.4",
"playwright-core": "1.21.0-next", "playwright-core": "1.21.1",
"rimraf": "3.0.2", "rimraf": "3.0.2",
"source-map-support": "0.4.18", "source-map-support": "0.4.18",
"stack-utils": "2.0.5", "stack-utils": "2.0.5",
@ -6255,11 +6255,11 @@
} }
}, },
"packages/playwright-webkit": { "packages/playwright-webkit": {
"version": "1.21.0-next", "version": "1.21.1",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.21.0-next" "playwright-core": "1.21.1"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -7016,19 +7016,19 @@
"@playwright/experimental-ct-react": { "@playwright/experimental-ct-react": {
"version": "file:packages/playwright-ct-react", "version": "file:packages/playwright-ct-react",
"requires": { "requires": {
"@playwright/test": "1.21.0-next" "@playwright/test": "1.21.1"
} }
}, },
"@playwright/experimental-ct-svelte": { "@playwright/experimental-ct-svelte": {
"version": "file:packages/playwright-ct-svelte", "version": "file:packages/playwright-ct-svelte",
"requires": { "requires": {
"@playwright/test": "1.21.0-next" "@playwright/test": "1.21.1"
} }
}, },
"@playwright/experimental-ct-vue": { "@playwright/experimental-ct-vue": {
"version": "file:packages/playwright-ct-vue", "version": "file:packages/playwright-ct-vue",
"requires": { "requires": {
"@playwright/test": "1.21.0-next" "@playwright/test": "1.21.1"
} }
}, },
"@playwright/test": { "@playwright/test": {
@ -7063,7 +7063,7 @@
"ms": "2.1.3", "ms": "2.1.3",
"open": "8.4.0", "open": "8.4.0",
"pirates": "4.0.4", "pirates": "4.0.4",
"playwright-core": "1.21.0-next", "playwright-core": "1.21.1",
"rimraf": "3.0.2", "rimraf": "3.0.2",
"source-map-support": "0.4.18", "source-map-support": "0.4.18",
"stack-utils": "2.0.5", "stack-utils": "2.0.5",
@ -9848,13 +9848,13 @@
"playwright": { "playwright": {
"version": "file:packages/playwright", "version": "file:packages/playwright",
"requires": { "requires": {
"playwright-core": "1.21.0-next" "playwright-core": "1.21.1"
} }
}, },
"playwright-chromium": { "playwright-chromium": {
"version": "file:packages/playwright-chromium", "version": "file:packages/playwright-chromium",
"requires": { "requires": {
"playwright-core": "1.21.0-next" "playwright-core": "1.21.1"
} }
}, },
"playwright-core": { "playwright-core": {
@ -9890,13 +9890,13 @@
"playwright-firefox": { "playwright-firefox": {
"version": "file:packages/playwright-firefox", "version": "file:packages/playwright-firefox",
"requires": { "requires": {
"playwright-core": "1.21.0-next" "playwright-core": "1.21.1"
} }
}, },
"playwright-webkit": { "playwright-webkit": {
"version": "file:packages/playwright-webkit", "version": "file:packages/playwright-webkit",
"requires": { "requires": {
"playwright-core": "1.21.0-next" "playwright-core": "1.21.1"
} }
}, },
"pngjs": { "pngjs": {

View file

@ -1,7 +1,7 @@
{ {
"name": "playwright-internal", "name": "playwright-internal",
"private": true, "private": true,
"version": "1.21.0-next", "version": "1.21.1",
"description": "A high-level API to automate web browsers", "description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright", "repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev", "homepage": "https://playwright.dev",

View file

@ -1,6 +1,6 @@
{ {
"name": "playwright-chromium", "name": "playwright-chromium",
"version": "1.21.0-next", "version": "1.21.1",
"description": "A high-level API to automate Chromium", "description": "A high-level API to automate Chromium",
"repository": "github:Microsoft/playwright", "repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev", "homepage": "https://playwright.dev",
@ -26,6 +26,6 @@
"install": "node install.js" "install": "node install.js"
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.21.0-next" "playwright-core": "1.21.1"
} }
} }

View file

@ -23,7 +23,7 @@
}, },
{ {
"name": "webkit", "name": "webkit",
"revision": "1625", "revision": "1630",
"installByDefault": true, "installByDefault": true,
"revisionOverrides": { "revisionOverrides": {
"mac10.14": "1446", "mac10.14": "1446",

View file

@ -1,6 +1,6 @@
{ {
"name": "playwright-core", "name": "playwright-core",
"version": "1.21.0-next", "version": "1.21.1",
"description": "A high-level API to automate web browsers", "description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright", "repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev", "homepage": "https://playwright.dev",
@ -24,9 +24,16 @@
"./lib/outofprocess": "./lib/outofprocess.js", "./lib/outofprocess": "./lib/outofprocess.js",
"./lib/utils": "./lib/utils/index.js", "./lib/utils": "./lib/utils/index.js",
"./lib/utils/comparators": "./lib/utils/comparators.js", "./lib/utils/comparators": "./lib/utils/comparators.js",
"./lib/utils/eventsHelper": "./lib/utils/eventsHelper.js",
"./lib/utils/fileUtils": "./lib/utils/fileUtils.js",
"./lib/utils/httpServer": "./lib/utils/httpServer.js", "./lib/utils/httpServer": "./lib/utils/httpServer.js",
"./lib/utils/hostPlatform": "./lib/utils/hostPlatform.js",
"./lib/utils/manualPromise": "./lib/utils/manualPromise.js",
"./lib/utils/multimap": "./lib/utils/multimap.js",
"./lib/utils/processLauncher": "./lib/utils/processLauncher.js", "./lib/utils/processLauncher": "./lib/utils/processLauncher.js",
"./lib/utils/spawnAsync": "./lib/utils/spawnAsync.js",
"./lib/utils/stackTrace": "./lib/utils/stackTrace.js", "./lib/utils/stackTrace": "./lib/utils/stackTrace.js",
"./lib/utils/timeoutRunner": "./lib/utils/timeoutRunner.js",
"./lib/remote/playwrightServer": "./lib/remote/playwrightServer.js", "./lib/remote/playwrightServer": "./lib/remote/playwrightServer.js",
"./lib/remote/playwrightClient": "./lib/remote/playwrightClient.js", "./lib/remote/playwrightClient": "./lib/remote/playwrightClient.js",
"./lib/server": "./lib/server/index.js" "./lib/server": "./lib/server/index.js"

View file

@ -33,7 +33,8 @@ import type { BrowserType } from '../client/browserType';
import type { BrowserContextOptions, LaunchOptions } from '../client/types'; import type { BrowserContextOptions, LaunchOptions } from '../client/types';
import { spawn } from 'child_process'; import { spawn } from 'child_process';
import { getPlaywrightVersion } from '../common/userAgent'; import { getPlaywrightVersion } from '../common/userAgent';
import { spawnAsync, wrapInASCIIBox } from '../utils'; import { wrapInASCIIBox } from '../utils';
import { spawnAsync } from '../utils/spawnAsync';
import { launchGridAgent } from '../grid/gridAgent'; import { launchGridAgent } from '../grid/gridAgent';
import type { GridFactory } from '../grid/gridServer'; import type { GridFactory } from '../grid/gridServer';
import { GridServer } from '../grid/gridServer'; import { GridServer } from '../grid/gridServer';

View file

@ -1,4 +1,4 @@
[*] [*]
../common/ ../common/
../protocol/ ../protocol/
../utils/ ../utils/**

View file

@ -17,7 +17,7 @@
import type * as channels from '../protocol/channels'; import type * as channels from '../protocol/channels';
import * as fs from 'fs'; import * as fs from 'fs';
import { Stream } from './stream'; import { Stream } from './stream';
import { mkdirIfNeeded } from '../utils'; import { mkdirIfNeeded } from '../utils/fileUtils';
import { ChannelOwner } from './channelOwner'; import { ChannelOwner } from './channelOwner';
import type { Readable } from 'stream'; import type { Readable } from 'stream';

View file

@ -28,7 +28,8 @@ import { Events } from './events';
import { TimeoutSettings } from '../common/timeoutSettings'; import { TimeoutSettings } from '../common/timeoutSettings';
import { Waiter } from './waiter'; import { Waiter } from './waiter';
import type { URLMatch, Headers, WaitForEventOptions, BrowserContextOptions, StorageState, LaunchOptions } from './types'; import type { URLMatch, Headers, WaitForEventOptions, BrowserContextOptions, StorageState, LaunchOptions } from './types';
import { headersObjectToArray, mkdirIfNeeded } from '../utils'; import { headersObjectToArray } from '../utils';
import { mkdirIfNeeded } from '../utils/fileUtils';
import { isSafeCloseError } from '../common/errors'; import { isSafeCloseError } from '../common/errors';
import type * as api from '../../types/types'; import type * as api from '../../types/types';
import type * as structs from '../../types/structs'; import type * as structs from '../../types/structs';

View file

@ -26,7 +26,7 @@ import { envObjectToArray } from './clientHelper';
import { assert, headersObjectToArray, monotonicTime } from '../utils'; import { assert, headersObjectToArray, monotonicTime } from '../utils';
import type * as api from '../../types/types'; import type * as api from '../../types/types';
import { kBrowserClosedError } from '../common/errors'; import { kBrowserClosedError } from '../common/errors';
import { raceAgainstTimeout } from '../utils'; import { raceAgainstTimeout } from '../utils/timeoutRunner';
import type { Playwright } from './playwright'; import type { Playwright } from './playwright';
export interface BrowserServerLauncher { export interface BrowserServerLauncher {

View file

@ -21,7 +21,8 @@ import { createScheme, ValidationError } from '../protocol/validator';
import { debugLogger } from '../common/debugLogger'; import { debugLogger } from '../common/debugLogger';
import type { ParsedStackTrace } from '../utils/stackTrace'; import type { ParsedStackTrace } from '../utils/stackTrace';
import { captureRawStack, captureStackTrace } from '../utils/stackTrace'; import { captureRawStack, captureStackTrace } from '../utils/stackTrace';
import { isUnderTest, zones } from '../utils'; import { isUnderTest } from '../utils';
import { zones } from '../utils/zones';
import type { ClientInstrumentation } from './clientInstrumentation'; import type { ClientInstrumentation } from './clientInstrumentation';
import type { Connection } from './connection'; import type { Connection } from './connection';
import type { Logger } from './types'; import type { Logger } from './types';

View file

@ -23,7 +23,8 @@ import type { SelectOption, FilePayload, Rect, SelectOptionOptions } from './typ
import fs from 'fs'; import fs from 'fs';
import * as mime from 'mime'; import * as mime from 'mime';
import path from 'path'; import path from 'path';
import { assert, isString, mkdirIfNeeded } from '../utils'; import { assert, isString } from '../utils';
import { mkdirIfNeeded } from '../utils/fileUtils';
import type * as api from '../../types/types'; import type * as api from '../../types/types';
import type * as structs from '../../types/structs'; import type * as structs from '../../types/structs';
import type { BrowserContext } from './browserContext'; import type { BrowserContext } from './browserContext';

View file

@ -22,7 +22,8 @@ import type * as api from '../../types/types';
import type { HeadersArray } from '../common/types'; import type { HeadersArray } from '../common/types';
import type * as channels from '../protocol/channels'; import type * as channels from '../protocol/channels';
import { kBrowserOrContextClosedError } from '../common/errors'; import { kBrowserOrContextClosedError } from '../common/errors';
import { assert, headersObjectToArray, isFilePayload, isString, mkdirIfNeeded, objectToArray } from '../utils'; import { assert, headersObjectToArray, isFilePayload, isString, objectToArray } from '../utils';
import { mkdirIfNeeded } from '../utils/fileUtils';
import { ChannelOwner } from './channelOwner'; import { ChannelOwner } from './channelOwner';
import * as network from './network'; import * as network from './network';
import { RawHeaders } from './network'; import { RawHeaders } from './network';

View file

@ -24,7 +24,7 @@ import { ElementHandle } from './elementHandle';
import type { Frame } from './frame'; import type { Frame } from './frame';
import type { FilePayload, FrameExpectOptions, Rect, SelectOption, SelectOptionOptions, TimeoutOptions } from './types'; import type { FilePayload, FrameExpectOptions, Rect, SelectOption, SelectOptionOptions, TimeoutOptions } from './types';
import { parseResult, serializeArgument } from './jsHandle'; import { parseResult, serializeArgument } from './jsHandle';
import { escapeWithQuotes } from '../utils/stringUtils'; import { escapeWithQuotes } from '../utils/isomorphic/stringUtils';
export class Locator implements api.Locator { export class Locator implements api.Locator {
_frame: Frame; _frame: Frame;

View file

@ -22,14 +22,14 @@ import type { Headers, RemoteAddr, SecurityDetails, WaitForEventOptions } from '
import fs from 'fs'; import fs from 'fs';
import * as mime from 'mime'; import * as mime from 'mime';
import { isString, headersObjectToArray } from '../utils'; import { isString, headersObjectToArray } from '../utils';
import { ManualPromise } from '../utils'; import { ManualPromise } from '../utils/manualPromise';
import { Events } from './events'; import { Events } from './events';
import type { Page } from './page'; import type { Page } from './page';
import { Waiter } from './waiter'; import { Waiter } from './waiter';
import type * as api from '../../types/types'; import type * as api from '../../types/types';
import type { HeadersArray, URLMatch } from '../common/types'; import type { HeadersArray, URLMatch } from '../common/types';
import { urlMatches } from './clientHelper'; import { urlMatches } from './clientHelper';
import { MultiMap } from '../utils'; import { MultiMap } from '../utils/multimap';
import { APIResponse } from './fetch'; import { APIResponse } from './fetch';
export type NetworkCookie = { export type NetworkCookie = {

View file

@ -46,7 +46,8 @@ import fs from 'fs';
import path from 'path'; import path from 'path';
import type { Size, URLMatch, Headers, LifecycleEvent, WaitForEventOptions, SelectOption, SelectOptionOptions, FilePayload, WaitForFunctionOptions } from './types'; import type { Size, URLMatch, Headers, LifecycleEvent, WaitForEventOptions, SelectOption, SelectOptionOptions, FilePayload, WaitForFunctionOptions } from './types';
import { evaluationScript, urlMatches } from './clientHelper'; import { evaluationScript, urlMatches } from './clientHelper';
import { isString, isRegExp, isObject, mkdirIfNeeded, headersObjectToArray } from '../utils'; import { isString, isRegExp, isObject, headersObjectToArray } from '../utils';
import { mkdirIfNeeded } from '../utils/fileUtils';
import { isSafeCloseError } from '../common/errors'; import { isSafeCloseError } from '../common/errors';
import { Video } from './video'; import { Video } from './video';
import { Artifact } from './artifact'; import { Artifact } from './artifact';

View file

@ -19,7 +19,7 @@ import { IpcTransport } from './protocol/transport';
import type { Playwright } from './client/playwright'; import type { Playwright } from './client/playwright';
import * as childProcess from 'child_process'; import * as childProcess from 'child_process';
import * as path from 'path'; import * as path from 'path';
import { ManualPromise } from './utils'; import { ManualPromise } from './utils/manualPromise';
export async function start(env: any = {}): Promise<{ playwright: Playwright, stop: () => Promise<void> }> { export async function start(env: any = {}): Promise<{ playwright: Playwright, stop: () => Promise<void> }> {
const client = new PlaywrightClient(env); const client = new PlaywrightClient(env);

View file

@ -4,14 +4,13 @@
../protocol/ ../protocol/
../utils/ ../utils/
./ ./
./common/
./injected/ ./injected/
./supplements/ ./isomorphic/
./har/
./recorder/
./registry/
./trace/recorder/tracing.ts ./trace/recorder/tracing.ts
[browserContext.ts]
./supplements/har/
[playwright.ts] [playwright.ts]
./android/ ./android/
./chromium/ ./chromium/

View file

@ -22,7 +22,8 @@ import os from 'os';
import path from 'path'; import path from 'path';
import type * as stream from 'stream'; import type * as stream from 'stream';
import * as ws from 'ws'; import * as ws from 'ws';
import { createGuid, makeWaitForNextTask, removeFolders } from '../../utils'; import { createGuid, makeWaitForNextTask } from '../../utils';
import { removeFolders } from '../../utils/fileUtils';
import type { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser'; import type { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser';
import type { BrowserContext } from '../browserContext'; import type { BrowserContext } from '../browserContext';
import { validateBrowserContextOptions } from '../browserContext'; import { validateBrowserContextOptions } from '../browserContext';

View file

@ -16,7 +16,7 @@
import fs from 'fs'; import fs from 'fs';
import { assert } from '../utils'; import { assert } from '../utils';
import { ManualPromise } from '../utils'; import { ManualPromise } from '../utils/manualPromise';
import { SdkObject } from './instrumentation'; import { SdkObject } from './instrumentation';
type SaveCallback = (localPath: string, error?: string) => Promise<void>; type SaveCallback = (localPath: string, error?: string) => Promise<void>;

View file

@ -17,7 +17,8 @@
import * as os from 'os'; import * as os from 'os';
import { TimeoutSettings } from '../common/timeoutSettings'; import { TimeoutSettings } from '../common/timeoutSettings';
import { debugMode, mkdirIfNeeded, createGuid } from '../utils'; import { debugMode, createGuid } from '../utils';
import { mkdirIfNeeded } from '../utils/fileUtils';
import type { Browser, BrowserOptions } from './browser'; import type { Browser, BrowserOptions } from './browser';
import type { Download } from './download'; import type { Download } from './download';
import type * as frames from './frames'; import type * as frames from './frames';
@ -32,10 +33,10 @@ import path from 'path';
import fs from 'fs'; import fs from 'fs';
import type { CallMetadata } from './instrumentation'; import type { CallMetadata } from './instrumentation';
import { serverSideCallMetadata, SdkObject } from './instrumentation'; import { serverSideCallMetadata, SdkObject } from './instrumentation';
import { Debugger } from './supplements/debugger'; import { Debugger } from './debugger';
import { Tracing } from './trace/recorder/tracing'; import { Tracing } from './trace/recorder/tracing';
import { HarRecorder } from './supplements/har/harRecorder'; import { HarRecorder } from './har/harRecorder';
import { RecorderSupplement } from './supplements/recorderSupplement'; import { Recorder } from './recorder';
import * as consoleApiSource from '../generated/consoleApiSource'; import * as consoleApiSource from '../generated/consoleApiSource';
import { BrowserContextAPIRequestContext } from './fetch'; import { BrowserContextAPIRequestContext } from './fetch';
@ -111,13 +112,13 @@ export abstract class BrowserContext extends SdkObject {
// When PWDEBUG=1, show inspector for each context. // When PWDEBUG=1, show inspector for each context.
if (debugMode() === 'inspector') if (debugMode() === 'inspector')
await RecorderSupplement.show(this, { pauseOnNextStatement: true }); await Recorder.show(this, { pauseOnNextStatement: true });
// When paused, show inspector. // When paused, show inspector.
if (contextDebugger.isPaused()) if (contextDebugger.isPaused())
RecorderSupplement.showInspector(this); Recorder.showInspector(this);
contextDebugger.on(Debugger.Events.PausedStateChanged, () => { contextDebugger.on(Debugger.Events.PausedStateChanged, () => {
RecorderSupplement.showInspector(this); Recorder.showInspector(this);
}); });
if (debugMode() === 'console') if (debugMode() === 'console')

View file

@ -31,7 +31,8 @@ import type { Progress } from './progress';
import { ProgressController } from './progress'; import { ProgressController } from './progress';
import type * as types from './types'; import type * as types from './types';
import { DEFAULT_TIMEOUT, TimeoutSettings } from '../common/timeoutSettings'; import { DEFAULT_TIMEOUT, TimeoutSettings } from '../common/timeoutSettings';
import { debugMode, existsAsync } from '../utils'; import { debugMode } from '../utils';
import { existsAsync } from '../utils/fileUtils';
import { helper } from './helper'; import { helper } from './helper';
import { RecentLogsCollector } from '../common/debugLogger'; import { RecentLogsCollector } from '../common/debugLogger';
import type { CallMetadata } from './instrumentation'; import type { CallMetadata } from './instrumentation';

View file

@ -33,7 +33,8 @@ import type * as types from '../types';
import type { HTTPRequestParams } from '../../common/netUtils'; import type { HTTPRequestParams } from '../../common/netUtils';
import { fetchData } from '../../common/netUtils'; import { fetchData } from '../../common/netUtils';
import { getUserAgent } from '../../common/userAgent'; import { getUserAgent } from '../../common/userAgent';
import { debugMode, headersArrayToObject, removeFolders, streamToString, wrapInASCIIBox } from '../../utils'; import { debugMode, headersArrayToObject, streamToString, wrapInASCIIBox } from '../../utils';
import { removeFolders } from '../../utils/fileUtils';
import { RecentLogsCollector } from '../../common/debugLogger'; import { RecentLogsCollector } from '../../common/debugLogger';
import type { Progress } from '../progress'; import type { Progress } from '../progress';
import { ProgressController } from '../progress'; import { ProgressController } from '../progress';
@ -43,7 +44,7 @@ import type { CallMetadata } from '../instrumentation';
import http from 'http'; import http from 'http';
import https from 'https'; import https from 'https';
import { registry } from '../registry'; import { registry } from '../registry';
import { ManualPromise } from '../../utils'; import { ManualPromise } from '../../utils/manualPromise';
const ARTIFACTS_FOLDER = path.join(os.tmpdir(), 'playwright-artifacts-'); const ARTIFACTS_FOLDER = path.join(os.tmpdir(), 'playwright-artifacts-');

View file

@ -16,8 +16,8 @@
*/ */
import type { CRSession } from './crConnection'; import type { CRSession } from './crConnection';
import type { RegisteredListener } from '../../utils'; import type { RegisteredListener } from '../../utils/eventsHelper';
import { eventsHelper } from '../../utils'; import { eventsHelper } from '../../utils/eventsHelper';
import type { Protocol } from './protocol'; import type { Protocol } from './protocol';
import type * as types from '../types'; import type * as types from '../types';
import { assert } from '../../utils'; import { assert } from '../../utils';

View file

@ -20,7 +20,7 @@ import { getExceptionMessage, releaseObject } from './crProtocolHelper';
import type { Protocol } from './protocol'; import type { Protocol } from './protocol';
import * as js from '../javascript'; import * as js from '../javascript';
import { rewriteErrorMessage } from '../../utils/stackTrace'; import { rewriteErrorMessage } from '../../utils/stackTrace';
import { parseEvaluationResultValue } from '../common/utilityScriptSerializers'; import { parseEvaluationResultValue } from '../isomorphic/utilityScriptSerializers';
import { isSessionClosedError } from '../protocolError'; import { isSessionClosedError } from '../protocolError';
export class CRExecutionContext implements js.ExecutionContextDelegate { export class CRExecutionContext implements js.ExecutionContextDelegate {

View file

@ -18,8 +18,8 @@
import type { CRSession } from './crConnection'; import type { CRSession } from './crConnection';
import type { Page } from '../page'; import type { Page } from '../page';
import { helper } from '../helper'; import { helper } from '../helper';
import type { RegisteredListener } from '../../utils'; import type { RegisteredListener } from '../../utils/eventsHelper';
import { eventsHelper } from '../../utils'; import { eventsHelper } from '../../utils/eventsHelper';
import type { Protocol } from './protocol'; import type { Protocol } from './protocol';
import * as network from '../network'; import * as network from '../network';
import type * as frames from '../frames'; import type * as frames from '../frames';

View file

@ -16,8 +16,8 @@
*/ */
import path from 'path'; import path from 'path';
import type { RegisteredListener } from '../../utils'; import type { RegisteredListener } from '../../utils/eventsHelper';
import { eventsHelper } from '../../utils'; import { eventsHelper } from '../../utils/eventsHelper';
import { registry } from '../registry'; import { registry } from '../registry';
import { rewriteErrorMessage } from '../../utils/stackTrace'; import { rewriteErrorMessage } from '../../utils/stackTrace';
import { assert, createGuid, headersArrayToObject } from '../../utils'; import { assert, createGuid, headersArrayToObject } from '../../utils';

View file

@ -19,7 +19,7 @@ import type { CRSession } from './crConnection';
import type { Protocol } from './protocol'; import type { Protocol } from './protocol';
import fs from 'fs'; import fs from 'fs';
import type * as types from '../types'; import type * as types from '../types';
import { mkdirIfNeeded } from '../../utils'; import { mkdirIfNeeded } from '../../utils/fileUtils';
import { splitErrorMessage } from '../../utils/stackTrace'; import { splitErrorMessage } from '../../utils/stackTrace';
export function getExceptionMessage(exceptionDetails: Protocol.Runtime.ExceptionDetails): string { export function getExceptionMessage(exceptionDetails: Protocol.Runtime.ExceptionDetails): string {

View file

@ -1 +0,0 @@
Files in this folder are used both in Node.js and injected environments, they can't have dependencies.

View file

@ -15,10 +15,10 @@
*/ */
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { debugMode, isUnderTest, monotonicTime } from '../../utils'; import { debugMode, isUnderTest, monotonicTime } from '../utils';
import type { BrowserContext } from '../browserContext'; import type { BrowserContext } from './browserContext';
import type { CallMetadata, InstrumentationListener, SdkObject } from '../instrumentation'; import type { CallMetadata, InstrumentationListener, SdkObject } from './instrumentation';
import { commandsWithTracingSnapshots, pausesBeforeInputActions } from '../../protocol/channels'; import { commandsWithTracingSnapshots, pausesBeforeInputActions } from '../protocol/channels';
const symbol = Symbol('Debugger'); const symbol = Symbol('Debugger');

View file

@ -19,7 +19,7 @@ import type { DispatcherScope } from './dispatcher';
import { Dispatcher } from './dispatcher'; import { Dispatcher } from './dispatcher';
import { StreamDispatcher } from './streamDispatcher'; import { StreamDispatcher } from './streamDispatcher';
import fs from 'fs'; import fs from 'fs';
import { mkdirIfNeeded } from '../../utils'; import { mkdirIfNeeded } from '../../utils/fileUtils';
import type { Artifact } from '../artifact'; import type { Artifact } from '../artifact';
export class ArtifactDispatcher extends Dispatcher<Artifact, channels.ArtifactChannel> implements channels.ArtifactChannel { export class ArtifactDispatcher extends Dispatcher<Artifact, channels.ArtifactChannel> implements channels.ArtifactChannel {

View file

@ -23,7 +23,7 @@ import type * as channels from '../../protocol/channels';
import { RouteDispatcher, RequestDispatcher, ResponseDispatcher, APIRequestContextDispatcher } from './networkDispatchers'; import { RouteDispatcher, RequestDispatcher, ResponseDispatcher, APIRequestContextDispatcher } from './networkDispatchers';
import { CRBrowserContext } from '../chromium/crBrowser'; import { CRBrowserContext } from '../chromium/crBrowser';
import { CDPSessionDispatcher } from './cdpSessionDispatcher'; import { CDPSessionDispatcher } from './cdpSessionDispatcher';
import { RecorderSupplement } from '../supplements/recorderSupplement'; import { Recorder } from '../recorder';
import type { CallMetadata } from '../instrumentation'; import type { CallMetadata } from '../instrumentation';
import { ArtifactDispatcher } from './artifactDispatcher'; import { ArtifactDispatcher } from './artifactDispatcher';
import type { Artifact } from '../artifact'; import type { Artifact } from '../artifact';
@ -197,7 +197,7 @@ export class BrowserContextDispatcher extends Dispatcher<BrowserContext, channel
} }
async recorderSupplementEnable(params: channels.BrowserContextRecorderSupplementEnableParams): Promise<void> { async recorderSupplementEnable(params: channels.BrowserContextRecorderSupplementEnableParams): Promise<void> {
await RecorderSupplement.show(this._context, params); await Recorder.show(this._context, params);
} }
async pause(params: channels.BrowserContextPauseParams, metadata: CallMetadata) { async pause(params: channels.BrowserContextPauseParams, metadata: CallMetadata) {

View file

@ -20,7 +20,7 @@ import path from 'path';
import yauzl from 'yauzl'; import yauzl from 'yauzl';
import yazl from 'yazl'; import yazl from 'yazl';
import type * as channels from '../../protocol/channels'; import type * as channels from '../../protocol/channels';
import { ManualPromise } from '../../utils'; import { ManualPromise } from '../../utils/manualPromise';
import { assert, createGuid } from '../../utils'; import { assert, createGuid } from '../../utils';
import type { DispatcherScope } from './dispatcher'; import type { DispatcherScope } from './dispatcher';
import { Dispatcher } from './dispatcher'; import { Dispatcher } from './dispatcher';

View file

@ -34,7 +34,7 @@ import type { BrowserWindow } from 'electron';
import type { Progress } from '../progress'; import type { Progress } from '../progress';
import { ProgressController } from '../progress'; import { ProgressController } from '../progress';
import { helper } from '../helper'; import { helper } from '../helper';
import { eventsHelper } from '../../utils'; import { eventsHelper } from '../../utils/eventsHelper';
import type { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser'; import type { BrowserOptions, BrowserProcess, PlaywrightOptions } from '../browser';
import type * as childProcess from 'child_process'; import type * as childProcess from 'child_process';
import * as readline from 'readline'; import * as readline from 'readline';

View file

@ -19,7 +19,7 @@ import * as js from '../javascript';
import type { FFSession } from './ffConnection'; import type { FFSession } from './ffConnection';
import type { Protocol } from './protocol'; import type { Protocol } from './protocol';
import { rewriteErrorMessage } from '../../utils/stackTrace'; import { rewriteErrorMessage } from '../../utils/stackTrace';
import { parseEvaluationResultValue } from '../common/utilityScriptSerializers'; import { parseEvaluationResultValue } from '../isomorphic/utilityScriptSerializers';
import { isSessionClosedError } from '../protocolError'; import { isSessionClosedError } from '../protocolError';
export class FFExecutionContext implements js.ExecutionContextDelegate { export class FFExecutionContext implements js.ExecutionContextDelegate {

View file

@ -15,8 +15,8 @@
* limitations under the License. * limitations under the License.
*/ */
import type { RegisteredListener } from '../../utils'; import type { RegisteredListener } from '../../utils/eventsHelper';
import { eventsHelper } from '../../utils'; import { eventsHelper } from '../../utils/eventsHelper';
import type { FFSession } from './ffConnection'; import type { FFSession } from './ffConnection';
import type { Page } from '../page'; import type { Page } from '../page';
import * as network from '../network'; import * as network from '../network';

View file

@ -18,8 +18,8 @@
import * as dialog from '../dialog'; import * as dialog from '../dialog';
import * as dom from '../dom'; import * as dom from '../dom';
import type * as frames from '../frames'; import type * as frames from '../frames';
import type { RegisteredListener } from '../../utils'; import type { RegisteredListener } from '../../utils/eventsHelper';
import { eventsHelper } from '../../utils'; import { eventsHelper } from '../../utils/eventsHelper';
import { assert } from '../../utils'; import { assert } from '../../utils';
import type { PageBinding, PageDelegate } from '../page'; import type { PageBinding, PageDelegate } from '../page';
import { Page, Worker } from '../page'; import { Page, Worker } from '../page';
@ -34,7 +34,7 @@ import type { Protocol } from './protocol';
import type { Progress } from '../progress'; import type { Progress } from '../progress';
import { splitErrorMessage } from '../../utils/stackTrace'; import { splitErrorMessage } from '../../utils/stackTrace';
import { debugLogger } from '../../common/debugLogger'; import { debugLogger } from '../../common/debugLogger';
import { ManualPromise } from '../../utils'; import { ManualPromise } from '../../utils/manualPromise';
export const UTILITY_WORLD_NAME = '__playwright_utility_world__'; export const UTILITY_WORLD_NAME = '__playwright_utility_world__';

View file

@ -19,8 +19,8 @@ import type * as channels from '../protocol/channels';
import type { ConsoleMessage } from './console'; import type { ConsoleMessage } from './console';
import * as dom from './dom'; import * as dom from './dom';
import { helper } from './helper'; import { helper } from './helper';
import type { RegisteredListener } from '../utils'; import type { RegisteredListener } from '../utils/eventsHelper';
import { eventsHelper } from '../utils'; import { eventsHelper } from '../utils/eventsHelper';
import * as js from './javascript'; import * as js from './javascript';
import * as network from './network'; import * as network from './network';
import type { Dialog } from './dialog'; import type { Dialog } from './dialog';
@ -30,15 +30,15 @@ import { BrowserContext } from './browserContext';
import type { Progress } from './progress'; import type { Progress } from './progress';
import { ProgressController } from './progress'; import { ProgressController } from './progress';
import { assert, constructURLBasedOnBaseURL, makeWaitForNextTask } from '../utils'; import { assert, constructURLBasedOnBaseURL, makeWaitForNextTask } from '../utils';
import { ManualPromise } from '../utils'; import { ManualPromise } from '../utils/manualPromise';
import { debugLogger } from '../common/debugLogger'; import { debugLogger } from '../common/debugLogger';
import type { CallMetadata } from './instrumentation'; import type { CallMetadata } from './instrumentation';
import { serverSideCallMetadata, SdkObject } from './instrumentation'; import { serverSideCallMetadata, SdkObject } from './instrumentation';
import { type InjectedScript } from './injected/injectedScript'; import { type InjectedScript } from './injected/injectedScript';
import type { ElementStateWithoutStable, FrameExpectParams, InjectedScriptPoll, InjectedScriptProgress } from './injected/injectedScript'; import type { ElementStateWithoutStable, FrameExpectParams, InjectedScriptPoll, InjectedScriptProgress } from './injected/injectedScript';
import { isSessionClosedError } from './protocolError'; import { isSessionClosedError } from './protocolError';
import type { ParsedSelector } from './common/selectorParser'; import type { ParsedSelector } from './isomorphic/selectorParser';
import { isInvalidSelectorError, splitSelectorByFrame, stringifySelector } from './common/selectorParser'; import { isInvalidSelectorError, splitSelectorByFrame, stringifySelector } from './isomorphic/selectorParser';
import type { SelectorInfo } from './selectors'; import type { SelectorInfo } from './selectors';
import type { ScreenshotOptions } from './screenshotter'; import type { ScreenshotOptions } from './screenshotter';
import type { InputFilesItems } from './dom'; import type { InputFilesItems } from './dom';

View file

@ -15,9 +15,9 @@
*/ */
import fs from 'fs'; import fs from 'fs';
import type { APIRequestContext } from '../../fetch'; import type { APIRequestContext } from '../fetch';
import { Artifact } from '../../artifact'; import { Artifact } from '../artifact';
import type { BrowserContext } from '../../browserContext'; import type { BrowserContext } from '../browserContext';
import type * as har from './har'; import type * as har from './har';
import { HarTracer } from './harTracer'; import { HarTracer } from './harTracer';

View file

@ -14,18 +14,19 @@
* limitations under the License. * limitations under the License.
*/ */
import { BrowserContext } from '../../browserContext'; import { BrowserContext } from '../browserContext';
import type { APIRequestEvent, APIRequestFinishedEvent } from '../../fetch'; import type { APIRequestEvent, APIRequestFinishedEvent } from '../fetch';
import { APIRequestContext } from '../../fetch'; import { APIRequestContext } from '../fetch';
import { helper } from '../../helper'; import { helper } from '../helper';
import * as network from '../../network'; import * as network from '../network';
import { Page } from '../../page'; import { Page } from '../page';
import type * as har from './har'; import type * as har from './har';
import { calculateSha1, monotonicTime } from '../../../utils'; import { calculateSha1, monotonicTime } from '../../utils';
import type { RegisteredListener } from '../../../utils'; import type { RegisteredListener } from '../../utils/eventsHelper';
import { eventsHelper } from '../../../utils'; import { eventsHelper } from '../../utils/eventsHelper';
import * as mime from 'mime'; import * as mime from 'mime';
import { ManualPromise } from '../../../utils'; import { ManualPromise } from '../../utils/manualPromise';
import { getPlaywrightVersion } from '../../common/userAgent';
const FALLBACK_HTTP_VERSION = 'HTTP/1.1'; const FALLBACK_HTTP_VERSION = 'HTTP/1.1';
@ -382,7 +383,7 @@ export class HarTracer {
version: '1.2', version: '1.2',
creator: { creator: {
name: 'Playwright', name: 'Playwright',
version: require('../../../../package.json')['version'], version: getPlaywrightVersion(),
}, },
browser: { browser: {
name: context?._browser.options.name || '', name: context?._browser.options.name || '',

View file

@ -19,8 +19,8 @@ import type { EventEmitter } from 'events';
import type * as types from './types'; import type * as types from './types';
import type { Progress } from './progress'; import type { Progress } from './progress';
import { debugLogger } from '../common/debugLogger'; import { debugLogger } from '../common/debugLogger';
import type { RegisteredListener } from '../utils'; import type { RegisteredListener } from '../utils/eventsHelper';
import { eventsHelper } from '../utils'; import { eventsHelper } from '../utils/eventsHelper';
class Helper { class Helper {
static completeUserURL(urlString: string): string { static completeUserURL(urlString: string): string {

View file

@ -1,2 +1,4 @@
# Files in this folder are used in browser environment, they can only depend on isomorphic files.
[*] [*]
../common/ ../isomorphic/
../../utils/isomorphic

View file

@ -14,9 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
import { escapeWithQuotes } from '../../../utils/stringUtils'; import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils';
import { type InjectedScript } from '../../injected/injectedScript'; import { type InjectedScript } from './injectedScript';
import { generateSelector } from '../../injected/selectorGenerator'; import { generateSelector } from './selectorGenerator';
function createLocator(injectedScript: InjectedScript, initial: string, options?: { hasText?: string | RegExp }) { function createLocator(injectedScript: InjectedScript, initial: string, options?: { hasText?: string | RegExp }) {
class Locator { class Locator {

View file

@ -19,11 +19,11 @@ import { XPathEngine } from './xpathSelectorEngine';
import { ReactEngine } from './reactSelectorEngine'; import { ReactEngine } from './reactSelectorEngine';
import { VueEngine } from './vueSelectorEngine'; import { VueEngine } from './vueSelectorEngine';
import { RoleEngine } from './roleSelectorEngine'; import { RoleEngine } from './roleSelectorEngine';
import type { ParsedSelector, ParsedSelectorPart } from '../common/selectorParser'; import type { ParsedSelector, ParsedSelectorPart } from '../isomorphic/selectorParser';
import { allEngineNames, parseSelector, stringifySelector } from '../common/selectorParser'; import { allEngineNames, parseSelector, stringifySelector } from '../isomorphic/selectorParser';
import type { TextMatcher } from './selectorEvaluator'; import type { TextMatcher } from './selectorEvaluator';
import { SelectorEvaluatorImpl, isVisible, parentElementOrShadowHost, elementMatchesText, createRegexTextMatcher, createStrictTextMatcher, createLaxTextMatcher } from './selectorEvaluator'; import { SelectorEvaluatorImpl, isVisible, parentElementOrShadowHost, elementMatchesText, createRegexTextMatcher, createStrictTextMatcher, createLaxTextMatcher } from './selectorEvaluator';
import type { CSSComplexSelectorList } from '../common/cssParser'; import type { CSSComplexSelectorList } from '../isomorphic/cssParser';
import { generateSelector } from './selectorGenerator'; import { generateSelector } from './selectorGenerator';
import type * as channels from '../../protocol/channels'; import type * as channels from '../../protocol/channels';
import { Highlight } from './highlight'; import { Highlight } from './highlight';

View file

@ -15,11 +15,11 @@
*/ */
import type * as actions from '../recorder/recorderActions'; import type * as actions from '../recorder/recorderActions';
import { type InjectedScript } from '../../injected/injectedScript'; import { type InjectedScript } from '../injected/injectedScript';
import { generateSelector, querySelector } from '../../injected/selectorGenerator'; import { generateSelector, querySelector } from '../injected/selectorGenerator';
import type { Point } from '../../../common/types'; import type { Point } from '../../common/types';
import type { UIState } from '../recorder/recorderTypes'; import type { UIState } from '../recorder/recorderTypes';
import { Highlight } from '../../injected/highlight'; import { Highlight } from '../injected/highlight';
declare module globalThis { declare module globalThis {

View file

@ -14,8 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
import type { CSSComplexSelector, CSSSimpleSelector, CSSComplexSelectorList, CSSFunctionArgument } from '../common/cssParser'; import type { CSSComplexSelector, CSSSimpleSelector, CSSComplexSelectorList, CSSFunctionArgument } from '../isomorphic/cssParser';
import { customCSSNames } from '../common/selectorParser'; import { customCSSNames } from '../isomorphic/selectorParser';
export type QueryContext = { export type QueryContext = {
scope: Element | Document; scope: Element | Document;

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { serializeAsCallArgument, parseEvaluationResultValue } from '../common/utilityScriptSerializers'; import { serializeAsCallArgument, parseEvaluationResultValue } from '../isomorphic/utilityScriptSerializers';
export class UtilityScript { export class UtilityScript {
evaluate(isFunction: boolean | undefined, returnByValue: boolean, expression: string, argCount: number, ...argsAndHandles: any[]) { evaluate(isFunction: boolean | undefined, returnByValue: boolean, expression: string, argCount: number, ...argsAndHandles: any[]) {

View file

@ -0,0 +1,2 @@
# Files in this folder are used both in Node.js and injected environments, they are isomorphic and can't have dependencies.
[*]

View file

@ -16,10 +16,10 @@
import type * as dom from './dom'; import type * as dom from './dom';
import * as utilityScriptSource from '../generated/utilityScriptSource'; import * as utilityScriptSource from '../generated/utilityScriptSource';
import { serializeAsCallArgument } from './common/utilityScriptSerializers'; import { serializeAsCallArgument } from './isomorphic/utilityScriptSerializers';
import { type UtilityScript } from './injected/utilityScript'; import { type UtilityScript } from './injected/utilityScript';
import { SdkObject } from './instrumentation'; import { SdkObject } from './instrumentation';
import { ManualPromise } from '../utils'; import { ManualPromise } from '../utils/manualPromise';
export type ObjectId = string; export type ObjectId = string;
export type RemoteObject = { export type RemoteObject = {

View file

@ -18,7 +18,7 @@ import type * as frames from './frames';
import type * as types from './types'; import type * as types from './types';
import type * as channels from '../protocol/channels'; import type * as channels from '../protocol/channels';
import { assert } from '../utils'; import { assert } from '../utils';
import { ManualPromise } from '../utils'; import { ManualPromise } from '../utils/manualPromise';
import { SdkObject } from './instrumentation'; import { SdkObject } from './instrumentation';
import type { NameValue } from '../common/types'; import type { NameValue } from '../common/types';
import { APIRequestContext } from './fetch'; import { APIRequestContext } from './fetch';

View file

@ -31,7 +31,7 @@ import { FileChooser } from './fileChooser';
import type { Progress } from './progress'; import type { Progress } from './progress';
import { ProgressController } from './progress'; import { ProgressController } from './progress';
import { assert, isError } from '../utils'; import { assert, isError } from '../utils';
import { ManualPromise } from '../utils'; import { ManualPromise } from '../utils/manualPromise';
import { debugLogger } from '../common/debugLogger'; import { debugLogger } from '../common/debugLogger';
import type { ImageComparatorOptions } from '../utils/comparators'; import type { ImageComparatorOptions } from '../utils/comparators';
import { getComparator } from '../utils/comparators'; import { getComparator } from '../utils/comparators';
@ -40,8 +40,8 @@ import type { CallMetadata } from './instrumentation';
import { SdkObject } from './instrumentation'; import { SdkObject } from './instrumentation';
import type { Artifact } from './artifact'; import type { Artifact } from './artifact';
import type { TimeoutOptions } from '../common/types'; import type { TimeoutOptions } from '../common/types';
import type { ParsedSelector } from './common/selectorParser'; import type { ParsedSelector } from './isomorphic/selectorParser';
import { isInvalidSelectorError } from './common/selectorParser'; import { isInvalidSelectorError } from './isomorphic/selectorParser';
export interface PageDelegate { export interface PageDelegate {
readonly rawMouse: input.RawMouse; readonly rawMouse: input.RawMouse;

View file

@ -19,7 +19,7 @@ import { assert, monotonicTime } from '../utils';
import type { LogName } from '../common/debugLogger'; import type { LogName } from '../common/debugLogger';
import type { CallMetadata, Instrumentation, SdkObject } from './instrumentation'; import type { CallMetadata, Instrumentation, SdkObject } from './instrumentation';
import type { ElementHandle } from './dom'; import type { ElementHandle } from './dom';
import { ManualPromise } from '../utils'; import { ManualPromise } from '../utils/manualPromise';
import type { LogEntry } from './injected/injectedScript'; import type { LogEntry } from './injected/injectedScript';
export interface Progress { export interface Progress {

View file

@ -16,35 +16,35 @@
import * as fs from 'fs'; import * as fs from 'fs';
import type * as actions from './recorder/recorderActions'; import type * as actions from './recorder/recorderActions';
import type * as channels from '../../protocol/channels'; import type * as channels from '../protocol/channels';
import type { ActionInContext } from './recorder/codeGenerator'; import type { ActionInContext } from './recorder/codeGenerator';
import { CodeGenerator } from './recorder/codeGenerator'; import { CodeGenerator } from './recorder/codeGenerator';
import { toClickOptions, toModifiers } from './recorder/utils'; import { toClickOptions, toModifiers } from './recorder/utils';
import { Page } from '../page'; import { Page } from './page';
import { Frame } from '../frames'; import { Frame } from './frames';
import { BrowserContext } from '../browserContext'; import { BrowserContext } from './browserContext';
import { JavaLanguageGenerator } from './recorder/java'; import { JavaLanguageGenerator } from './recorder/java';
import { JavaScriptLanguageGenerator } from './recorder/javascript'; import { JavaScriptLanguageGenerator } from './recorder/javascript';
import { CSharpLanguageGenerator } from './recorder/csharp'; import { CSharpLanguageGenerator } from './recorder/csharp';
import { PythonLanguageGenerator } from './recorder/python'; import { PythonLanguageGenerator } from './recorder/python';
import * as recorderSource from '../../generated/recorderSource'; import * as recorderSource from '../generated/recorderSource';
import * as consoleApiSource from '../../generated/consoleApiSource'; import * as consoleApiSource from '../generated/consoleApiSource';
import type { IRecorderApp } from './recorder/recorderApp'; import type { IRecorderApp } from './recorder/recorderApp';
import { RecorderApp } from './recorder/recorderApp'; import { RecorderApp } from './recorder/recorderApp';
import type { CallMetadata, InstrumentationListener, SdkObject } from '../instrumentation'; import type { CallMetadata, InstrumentationListener, SdkObject } from './instrumentation';
import type { Point } from '../../common/types'; import type { Point } from '../common/types';
import type { CallLog, CallLogStatus, EventData, Mode, Source, UIState } from './recorder/recorderTypes'; import type { CallLog, CallLogStatus, EventData, Mode, Source, UIState } from './recorder/recorderTypes';
import { createGuid, monotonicTime } from '../../utils'; import { createGuid, monotonicTime } from '../utils';
import { metadataToCallLog } from './recorder/recorderUtils'; import { metadataToCallLog } from './recorder/recorderUtils';
import { Debugger } from './debugger'; import { Debugger } from './debugger';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { raceAgainstTimeout } from '../../utils'; import { raceAgainstTimeout } from '../utils/timeoutRunner';
type BindingSource = { frame: Frame, page: Page }; type BindingSource = { frame: Frame, page: Page };
const symbol = Symbol('RecorderSupplement'); const symbol = Symbol('RecorderSupplement');
export class RecorderSupplement implements InstrumentationListener { export class Recorder implements InstrumentationListener {
private _context: BrowserContext; private _context: BrowserContext;
private _mode: Mode; private _mode: Mode;
private _highlightedSelector = ''; private _highlightedSelector = '';
@ -57,13 +57,13 @@ export class RecorderSupplement implements InstrumentationListener {
private _contextRecorder: ContextRecorder; private _contextRecorder: ContextRecorder;
static showInspector(context: BrowserContext) { static showInspector(context: BrowserContext) {
RecorderSupplement.show(context, {}).catch(() => {}); Recorder.show(context, {}).catch(() => {});
} }
static show(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams = {}): Promise<RecorderSupplement> { static show(context: BrowserContext, params: channels.BrowserContextRecorderSupplementEnableParams = {}): Promise<Recorder> {
let recorderPromise = (context as any)[symbol] as Promise<RecorderSupplement>; let recorderPromise = (context as any)[symbol] as Promise<Recorder>;
if (!recorderPromise) { if (!recorderPromise) {
const recorder = new RecorderSupplement(context, params); const recorder = new Recorder(context, params);
recorderPromise = recorder.install().then(() => recorder); recorderPromise = recorder.install().then(() => recorder);
(context as any)[symbol] = recorderPromise; (context as any)[symbol] = recorderPromise;
} }

View file

@ -0,0 +1,9 @@
[*]
../
../registry/**
../../common/
../../protocol/
../../utils/**
[recorderApp.ts]
../chromium/crApp.ts

View file

@ -15,8 +15,8 @@
*/ */
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import type { BrowserContextOptions, LaunchOptions } from '../../../..'; import type { BrowserContextOptions, LaunchOptions } from '../../..';
import type { Frame } from '../../frames'; import type { Frame } from '../frames';
import type { LanguageGenerator, LanguageGeneratorOptions } from './language'; import type { LanguageGenerator, LanguageGeneratorOptions } from './language';
import type { Action, Signal, FrameDescription } from './recorderActions'; import type { Action, Signal, FrameDescription } from './recorderActions';

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import type { BrowserContextOptions } from '../../../..'; import type { BrowserContextOptions } from '../../..';
import type { LanguageGenerator, LanguageGeneratorOptions } from './language'; import type { LanguageGenerator, LanguageGeneratorOptions } from './language';
import { sanitizeDeviceOptions, toSignalMap } from './language'; import { sanitizeDeviceOptions, toSignalMap } from './language';
import type { ActionInContext } from './codeGenerator'; import type { ActionInContext } from './codeGenerator';
@ -22,8 +22,8 @@ import type { Action } from './recorderActions';
import { actionTitle } from './recorderActions'; import { actionTitle } from './recorderActions';
import type { MouseClickOptions } from './utils'; import type { MouseClickOptions } from './utils';
import { toModifiers } from './utils'; import { toModifiers } from './utils';
import { escapeWithQuotes } from '../../../utils/stringUtils'; import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils';
import deviceDescriptors from '../../deviceDescriptors'; import deviceDescriptors from '../deviceDescriptors';
export class CSharpLanguageGenerator implements LanguageGenerator { export class CSharpLanguageGenerator implements LanguageGenerator {
id = 'csharp'; id = 'csharp';

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import type { BrowserContextOptions } from '../../../..'; import type { BrowserContextOptions } from '../../..';
import type { LanguageGenerator, LanguageGeneratorOptions } from './language'; import type { LanguageGenerator, LanguageGeneratorOptions } from './language';
import { toSignalMap } from './language'; import { toSignalMap } from './language';
import type { ActionInContext } from './codeGenerator'; import type { ActionInContext } from './codeGenerator';
@ -22,9 +22,9 @@ import type { Action } from './recorderActions';
import { actionTitle } from './recorderActions'; import { actionTitle } from './recorderActions';
import type { MouseClickOptions } from './utils'; import type { MouseClickOptions } from './utils';
import { toModifiers } from './utils'; import { toModifiers } from './utils';
import deviceDescriptors from '../../deviceDescriptors'; import deviceDescriptors from '../deviceDescriptors';
import { JavaScriptFormatter } from './javascript'; import { JavaScriptFormatter } from './javascript';
import { escapeWithQuotes } from '../../../utils/stringUtils'; import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils';
export class JavaLanguageGenerator implements LanguageGenerator { export class JavaLanguageGenerator implements LanguageGenerator {
id = 'java'; id = 'java';

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import type { BrowserContextOptions } from '../../../..'; import type { BrowserContextOptions } from '../../..';
import type { LanguageGenerator, LanguageGeneratorOptions } from './language'; import type { LanguageGenerator, LanguageGeneratorOptions } from './language';
import { sanitizeDeviceOptions, toSignalMap } from './language'; import { sanitizeDeviceOptions, toSignalMap } from './language';
import type { ActionInContext } from './codeGenerator'; import type { ActionInContext } from './codeGenerator';
@ -22,8 +22,8 @@ import type { Action } from './recorderActions';
import { actionTitle } from './recorderActions'; import { actionTitle } from './recorderActions';
import type { MouseClickOptions } from './utils'; import type { MouseClickOptions } from './utils';
import { toModifiers } from './utils'; import { toModifiers } from './utils';
import deviceDescriptors from '../../deviceDescriptors'; import deviceDescriptors from '../deviceDescriptors';
import { escapeWithQuotes } from '../../../utils/stringUtils'; import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils';
export class JavaScriptLanguageGenerator implements LanguageGenerator { export class JavaScriptLanguageGenerator implements LanguageGenerator {
id: string; id: string;

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import type { BrowserContextOptions, LaunchOptions } from '../../../..'; import type { BrowserContextOptions, LaunchOptions } from '../../..';
import type { ActionInContext } from './codeGenerator'; import type { ActionInContext } from './codeGenerator';
import type { Action, DialogSignal, DownloadSignal, NavigationSignal, PopupSignal } from './recorderActions'; import type { Action, DialogSignal, DownloadSignal, NavigationSignal, PopupSignal } from './recorderActions';

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import type { BrowserContextOptions } from '../../../..'; import type { BrowserContextOptions } from '../../..';
import type { LanguageGenerator, LanguageGeneratorOptions } from './language'; import type { LanguageGenerator, LanguageGeneratorOptions } from './language';
import { sanitizeDeviceOptions, toSignalMap } from './language'; import { sanitizeDeviceOptions, toSignalMap } from './language';
import type { ActionInContext } from './codeGenerator'; import type { ActionInContext } from './codeGenerator';
@ -22,8 +22,8 @@ import type { Action } from './recorderActions';
import { actionTitle } from './recorderActions'; import { actionTitle } from './recorderActions';
import type { MouseClickOptions } from './utils'; import type { MouseClickOptions } from './utils';
import { toModifiers } from './utils'; import { toModifiers } from './utils';
import { escapeWithQuotes } from '../../../utils/stringUtils'; import { escapeWithQuotes } from '../../utils/isomorphic/stringUtils';
import deviceDescriptors from '../../deviceDescriptors'; import deviceDescriptors from '../deviceDescriptors';
export class PythonLanguageGenerator implements LanguageGenerator { export class PythonLanguageGenerator implements LanguageGenerator {
id = 'python'; id = 'python';

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import type { Point } from '../../../common/types'; import type { Point } from '../../common/types';
export type ActionName = export type ActionName =
'check' | 'check' |

View file

@ -16,15 +16,15 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import type { Page } from '../../page'; import type { Page } from '../page';
import { ProgressController } from '../../progress'; import { ProgressController } from '../progress';
import { EventEmitter } from 'events'; import { EventEmitter } from 'events';
import { serverSideCallMetadata } from '../../instrumentation'; import { serverSideCallMetadata } from '../instrumentation';
import type { CallLog, EventData, Mode, Source } from './recorderTypes'; import type { CallLog, EventData, Mode, Source } from './recorderTypes';
import { isUnderTest } from '../../../utils'; import { isUnderTest } from '../../utils';
import * as mime from 'mime'; import * as mime from 'mime';
import { installAppIcon } from '../../chromium/crApp'; import { installAppIcon } from '../chromium/crApp';
import { findChromiumChannel } from '../../registry'; import { findChromiumChannel } from '../registry';
declare global { declare global {
interface Window { interface Window {
@ -70,7 +70,7 @@ export class RecorderApp extends EventEmitter implements IRecorderApp {
await this._page._setServerRequestInterceptor(async route => { await this._page._setServerRequestInterceptor(async route => {
if (route.request().url().startsWith('https://playwright/')) { if (route.request().url().startsWith('https://playwright/')) {
const uri = route.request().url().substring('https://playwright/'.length); const uri = route.request().url().substring('https://playwright/'.length);
const file = require.resolve('../../../webpack/recorder/' + uri); const file = require.resolve('../../webpack/recorder/' + uri);
const buffer = await fs.promises.readFile(file); const buffer = await fs.promises.readFile(file);
await route.fulfill({ await route.fulfill({
status: 200, status: 200,
@ -99,7 +99,7 @@ export class RecorderApp extends EventEmitter implements IRecorderApp {
static async open(sdkLanguage: string, headed: boolean): Promise<IRecorderApp> { static async open(sdkLanguage: string, headed: boolean): Promise<IRecorderApp> {
if (process.env.PW_CODEGEN_NO_INSPECTOR) if (process.env.PW_CODEGEN_NO_INSPECTOR)
return new HeadlessRecorderApp(); return new HeadlessRecorderApp();
const recorderPlaywright = (require('../../playwright').createPlaywright as typeof import('../../playwright').createPlaywright)('javascript', true); const recorderPlaywright = (require('../playwright').createPlaywright as typeof import('../playwright').createPlaywright)('javascript', true);
const args = [ const args = [
'--app=data:text/html,', '--app=data:text/html,',
'--window-size=600,600', '--window-size=600,600',

View file

@ -14,8 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
import type { Point } from '../../../common/types'; import type { Point } from '../../common/types';
import type { SerializedError } from '../../../protocol/channels'; import type { SerializedError } from '../../protocol/channels';
export type Mode = 'inspecting' | 'recording' | 'none'; export type Mode = 'inspecting' | 'recording' | 'none';

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import type { CallMetadata } from '../../instrumentation'; import type { CallMetadata } from '../instrumentation';
import type { CallLog, CallLogStatus } from './recorderTypes'; import type { CallLog, CallLogStatus } from './recorderTypes';
export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus): CallLog { export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus): CallLog {

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import type { Frame } from '../../frames'; import type { Frame } from '../frames';
import type * as actions from './recorderActions'; import type * as actions from './recorderActions';
export type MouseClickOptions = Parameters<Frame['click']>[2]; export type MouseClickOptions = Parameters<Frame['click']>[2];

View file

@ -20,7 +20,7 @@ import fs from 'fs';
import os from 'os'; import os from 'os';
import path from 'path'; import path from 'path';
import { getUserAgent } from '../../common/userAgent'; import { getUserAgent } from '../../common/userAgent';
import { existsAsync } from '../../utils'; import { existsAsync } from '../../utils/fileUtils';
import { debugLogger } from '../../common/debugLogger'; import { debugLogger } from '../../common/debugLogger';
import { download } from './download'; import { download } from './download';

View file

@ -19,9 +19,10 @@ import path from 'path';
import * as os from 'os'; import * as os from 'os';
import childProcess from 'child_process'; import childProcess from 'child_process';
import * as utils from '../../utils'; import * as utils from '../../utils';
import { spawnAsync } from '../../utils/spawnAsync';
import { hostPlatform } from '../../utils/hostPlatform';
import { buildPlaywrightCLICommand } from '.'; import { buildPlaywrightCLICommand } from '.';
import { deps } from './nativeDeps'; import { deps } from './nativeDeps';
import { getUbuntuVersion } from '../../utils/ubuntuVersion';
import { getPlaywrightVersion } from '../../common/userAgent'; import { getPlaywrightVersion } from '../../common/userAgent';
const BIN_DIRECTORY = path.join(__dirname, '..', '..', '..', 'bin'); const BIN_DIRECTORY = path.join(__dirname, '..', '..', '..', 'bin');
@ -66,18 +67,16 @@ export async function installDependenciesWindows(targets: Set<DependencyGroup>,
console.log(`${command} ${quoteProcessArgs(args).join(' ')}`); // eslint-disable-line no-console console.log(`${command} ${quoteProcessArgs(args).join(' ')}`); // eslint-disable-line no-console
return; return;
} }
const { code } = await utils.spawnAsync(command, args, { cwd: BIN_DIRECTORY, stdio: 'inherit' }); const { code } = await spawnAsync(command, args, { cwd: BIN_DIRECTORY, stdio: 'inherit' });
if (code !== 0) if (code !== 0)
throw new Error('Failed to install windows dependencies!'); throw new Error('Failed to install windows dependencies!');
} }
} }
export async function installDependenciesLinux(targets: Set<DependencyGroup>, dryRun: boolean) { export async function installDependenciesLinux(targets: Set<DependencyGroup>, dryRun: boolean) {
if (await getUbuntuVersion() === '')
throw new Error(`Unsupported Linux distribution, only Ubuntu is supported!`);
const libraries: string[] = []; const libraries: string[] = [];
for (const target of targets) { for (const target of targets) {
const info = deps[utils.hostPlatform]; const info = deps[hostPlatform];
if (!info) { if (!info) {
console.warn('Cannot install dependencies for this linux distribution!'); // eslint-disable-line no-console console.warn('Cannot install dependencies for this linux distribution!'); // eslint-disable-line no-console
return; return;
@ -92,7 +91,7 @@ export async function installDependenciesLinux(targets: Set<DependencyGroup>, dr
commands.push(['apt-get', 'install', '-y', '--no-install-recommends', commands.push(['apt-get', 'install', '-y', '--no-install-recommends',
...uniqueLibraries, ...uniqueLibraries,
].join(' ')); ].join(' '));
const { command, args, elevatedPermissions } = await utils.transformCommandsForRoot(commands); const { command, args, elevatedPermissions } = await transformCommandsForRoot(commands);
if (dryRun) { if (dryRun) {
console.log(`${command} ${quoteProcessArgs(args).join(' ')}`); // eslint-disable-line no-console console.log(`${command} ${quoteProcessArgs(args).join(' ')}`); // eslint-disable-line no-console
return; return;
@ -189,7 +188,7 @@ export async function validateDependenciesLinux(sdkLanguage: string, linuxLddDir
const missingPackages = new Set(); const missingPackages = new Set();
const libraryToPackageNameMapping = { const libraryToPackageNameMapping = {
...(deps[utils.hostPlatform]?.lib2package || {}), ...(deps[hostPlatform]?.lib2package || {}),
...MANUAL_LIBRARY_TO_PACKAGE_NAME_UBUNTU, ...MANUAL_LIBRARY_TO_PACKAGE_NAME_UBUNTU,
}; };
// Translate missing dependencies to package names to install with apt. // Translate missing dependencies to package names to install with apt.
@ -268,6 +267,8 @@ function isSharedLib(basename: string) {
} }
async function executablesOrSharedLibraries(directoryPath: string): Promise<string[]> { async function executablesOrSharedLibraries(directoryPath: string): Promise<string[]> {
if (!fs.existsSync(directoryPath))
return [];
const allPaths = (await fs.promises.readdir(directoryPath)).map(file => path.resolve(directoryPath, file)); const allPaths = (await fs.promises.readdir(directoryPath)).map(file => path.resolve(directoryPath, file));
const allStats = await Promise.all(allPaths.map(aPath => fs.promises.stat(aPath))); const allStats = await Promise.all(allPaths.map(aPath => fs.promises.stat(aPath)));
const filePaths = allPaths.filter((aPath, index) => (allStats[index] as any).isFile()); const filePaths = allPaths.filter((aPath, index) => (allStats[index] as any).isFile());
@ -287,7 +288,7 @@ async function executablesOrSharedLibraries(directoryPath: string): Promise<stri
async function missingFileDependenciesWindows(filePath: string): Promise<Array<string>> { async function missingFileDependenciesWindows(filePath: string): Promise<Array<string>> {
const executable = path.join(__dirname, '..', '..', '..', 'bin', 'PrintDeps.exe'); const executable = path.join(__dirname, '..', '..', '..', 'bin', 'PrintDeps.exe');
const dirname = path.dirname(filePath); const dirname = path.dirname(filePath);
const { stdout, code } = await utils.spawnAsync(executable, [filePath], { const { stdout, code } = await spawnAsync(executable, [filePath], {
cwd: dirname, cwd: dirname,
env: { env: {
...process.env, ...process.env,
@ -305,7 +306,7 @@ async function missingFileDependencies(filePath: string, extraLDPaths: string[])
let LD_LIBRARY_PATH = extraLDPaths.join(':'); let LD_LIBRARY_PATH = extraLDPaths.join(':');
if (process.env.LD_LIBRARY_PATH) if (process.env.LD_LIBRARY_PATH)
LD_LIBRARY_PATH = `${process.env.LD_LIBRARY_PATH}:${LD_LIBRARY_PATH}`; LD_LIBRARY_PATH = `${process.env.LD_LIBRARY_PATH}:${LD_LIBRARY_PATH}`;
const { stdout, code } = await utils.spawnAsync('ldd', [filePath], { const { stdout, code } = await spawnAsync('ldd', [filePath], {
cwd: dirname, cwd: dirname,
env: { env: {
...process.env, ...process.env,
@ -324,7 +325,7 @@ async function missingDLOPENLibraries(libraries: string[]): Promise<string[]> {
// NOTE: Using full-qualified path to `ldconfig` since `/sbin` is not part of the // NOTE: Using full-qualified path to `ldconfig` since `/sbin` is not part of the
// default PATH in CRON. // default PATH in CRON.
// @see https://github.com/microsoft/playwright/issues/3397 // @see https://github.com/microsoft/playwright/issues/3397
const { stdout, code, error } = await utils.spawnAsync('/sbin/ldconfig', ['-p'], {}); const { stdout, code, error } = await spawnAsync('/sbin/ldconfig', ['-p'], {});
if (code !== 0 || error) if (code !== 0 || error)
return []; return [];
const isLibraryAvailable = (library: string) => stdout.toLowerCase().includes(library.toLowerCase()); const isLibraryAvailable = (library: string) => stdout.toLowerCase().includes(library.toLowerCase());
@ -346,3 +347,13 @@ function quoteProcessArgs(args: string[]): string[] {
return arg; return arg;
}); });
} }
export async function transformCommandsForRoot(commands: string[]): Promise<{ command: string, args: string[], elevatedPermissions: boolean}> {
const isRoot = process.getuid() === 0;
if (isRoot)
return { command: 'sh', args: ['-c', `${commands.join('&& ')}`], elevatedPermissions: false };
const sudoExists = await spawnAsync('which', ['sudo']);
if (sudoExists.code === 0)
return { command: 'sudo', args: ['--', 'sh', '-c', `${commands.join('&& ')}`], elevatedPermissions: true };
return { command: 'su', args: ['root', '-c', `${commands.join('&& ')}`], elevatedPermissions: true };
}

View file

@ -23,8 +23,12 @@ import lockfile from 'proper-lockfile';
import { getUbuntuVersion } from '../../utils/ubuntuVersion'; import { getUbuntuVersion } from '../../utils/ubuntuVersion';
import { fetchData } from '../../common/netUtils'; import { fetchData } from '../../common/netUtils';
import { getClientLanguage } from '../../common/userAgent'; import { getClientLanguage } from '../../common/userAgent';
import { getFromENV, getAsBooleanFromENV, calculateSha1, removeFolders, existsAsync, hostPlatform, canAccessFile, spawnAsync, wrapInASCIIBox, transformCommandsForRoot } from '../../utils'; import { getFromENV, getAsBooleanFromENV, calculateSha1, wrapInASCIIBox } from '../../utils';
import { removeFolders, existsAsync, canAccessFile } from '../../utils/fileUtils';
import { hostPlatform } from '../../utils/hostPlatform';
import { spawnAsync } from '../../utils/spawnAsync';
import type { DependencyGroup } from './dependencies'; import type { DependencyGroup } from './dependencies';
import { transformCommandsForRoot } from './dependencies';
import { installDependenciesLinux, installDependenciesWindows, validateDependenciesLinux, validateDependenciesWindows } from './dependencies'; import { installDependenciesLinux, installDependenciesWindows, validateDependenciesLinux, validateDependenciesWindows } from './dependencies';
import { downloadBrowserWithProgressBar, logPolitely } from './browserFetcher'; import { downloadBrowserWithProgressBar, logPolitely } from './browserFetcher';
export { writeDockerVersion } from './dependencies'; export { writeDockerVersion } from './dependencies';
@ -433,9 +437,11 @@ export class Registry {
path.join('minibrowser-gtk'), path.join('minibrowser-gtk'),
path.join('minibrowser-gtk', 'bin'), path.join('minibrowser-gtk', 'bin'),
path.join('minibrowser-gtk', 'lib'), path.join('minibrowser-gtk', 'lib'),
path.join('minibrowser-gtk', 'sys', 'lib'),
path.join('minibrowser-wpe'), path.join('minibrowser-wpe'),
path.join('minibrowser-wpe', 'bin'), path.join('minibrowser-wpe', 'bin'),
path.join('minibrowser-wpe', 'lib'), path.join('minibrowser-wpe', 'lib'),
path.join('minibrowser-wpe', 'sys', 'lib'),
]; ];
this._executables.push({ this._executables.push({
type: 'browser', type: 'browser',
@ -594,7 +600,7 @@ export class Registry {
throw new Error(`ERROR: Playwright does not support installing ${executable.name}`); throw new Error(`ERROR: Playwright does not support installing ${executable.name}`);
const { langName } = getClientLanguage(); const { langName } = getClientLanguage();
if (!executable._isHermeticInstallation && !forceReinstall && executable.executablePath(langName)) { if (!getAsBooleanFromENV('CI') && !executable._isHermeticInstallation && !forceReinstall && executable.executablePath(langName)) {
const command = buildPlaywrightCLICommand(langName, 'install --force ' + executable.name); const command = buildPlaywrightCLICommand(langName, 'install --force ' + executable.name);
throw new Error('\n' + wrapInASCIIBox([ throw new Error('\n' + wrapInASCIIBox([
`ATTENTION: "${executable.name}" is already installed on the system!`, `ATTENTION: "${executable.name}" is already installed on the system!`,

View file

@ -20,11 +20,11 @@ import type { Rect } from '../common/types';
import { helper } from './helper'; import { helper } from './helper';
import type { Page } from './page'; import type { Page } from './page';
import type { Frame } from './frames'; import type { Frame } from './frames';
import type { ParsedSelector } from './common/selectorParser'; import type { ParsedSelector } from './isomorphic/selectorParser';
import type * as types from './types'; import type * as types from './types';
import type { Progress } from './progress'; import type { Progress } from './progress';
import { assert } from '../utils'; import { assert } from '../utils';
import { MultiMap } from '../utils'; import { MultiMap } from '../utils/multimap';
declare global { declare global {
interface Window { interface Window {

View file

@ -18,8 +18,8 @@ import type * as dom from './dom';
import type * as frames from './frames'; import type * as frames from './frames';
import type * as js from './javascript'; import type * as js from './javascript';
import type * as types from './types'; import type * as types from './types';
import type { ParsedSelector } from './common/selectorParser'; import type { ParsedSelector } from './isomorphic/selectorParser';
import { allEngineNames, InvalidSelectorError, parseSelector, stringifySelector } from './common/selectorParser'; import { allEngineNames, InvalidSelectorError, parseSelector, stringifySelector } from './isomorphic/selectorParser';
import { createGuid, experimentalFeaturesEnabled } from '../utils'; import { createGuid, experimentalFeaturesEnabled } from '../utils';
export type SelectorInfo = { export type SelectorInfo = {

View file

@ -1,10 +0,0 @@
[*]
../
../../utils/
[debugger.ts]
../../protocol/
[recorderSupplement.ts]
../../generated/
./recorder/

View file

@ -1,3 +0,0 @@
[*]
../../injected/
../../../utils/

View file

@ -1,8 +0,0 @@
[*]
../../
../../../common/
../../../protocol/
../../../utils/
[recorderApp.ts]
../../chromium/crApp.ts

View file

@ -1,3 +1,3 @@
[*] [*]
../../supplements/har/ ../../har/

View file

@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
import type { Entry as HAREntry } from '../../supplements/har/har'; import type { Entry as HAREntry } from '../../har/har';
export type ResourceSnapshot = HAREntry; export type ResourceSnapshot = HAREntry;

View file

@ -1,7 +1,7 @@
[*] [*]
../../ ../../
../../har/
../../../common/ ../../../common/
../../../protocol/ ../../../protocol/
../../../utils/ ../../../utils/
../../supplements/har/
../common/ ../common/

View file

@ -16,8 +16,8 @@
import { BrowserContext } from '../../browserContext'; import { BrowserContext } from '../../browserContext';
import { Page } from '../../page'; import { Page } from '../../page';
import type { RegisteredListener } from '../../../utils'; import type { RegisteredListener } from '../../../utils/eventsHelper';
import { eventsHelper } from '../../../utils'; import { eventsHelper } from '../../../utils/eventsHelper';
import { debugLogger } from '../../../common/debugLogger'; import { debugLogger } from '../../../common/debugLogger';
import type { Frame } from '../../frames'; import type { Frame } from '../../frames';
import type { SnapshotData } from './snapshotterInjected'; import type { SnapshotData } from './snapshotterInjected';

View file

@ -22,10 +22,11 @@ import yazl from 'yazl';
import type { NameValue } from '../../../common/types'; import type { NameValue } from '../../../common/types';
import type { TracingTracingStopChunkParams } from '../../../protocol/channels'; import type { TracingTracingStopChunkParams } from '../../../protocol/channels';
import { commandsWithTracingSnapshots } from '../../../protocol/channels'; import { commandsWithTracingSnapshots } from '../../../protocol/channels';
import { ManualPromise } from '../../../utils'; import { ManualPromise } from '../../../utils/manualPromise';
import type { RegisteredListener } from '../../../utils'; import type { RegisteredListener } from '../../../utils/eventsHelper';
import { eventsHelper } from '../../../utils'; import { eventsHelper } from '../../../utils/eventsHelper';
import { assert, calculateSha1, createGuid, mkdirIfNeeded, monotonicTime, removeFolders } from '../../../utils'; import { assert, calculateSha1, createGuid, monotonicTime } from '../../../utils';
import { mkdirIfNeeded, removeFolders } from '../../../utils/fileUtils';
import { Artifact } from '../../artifact'; import { Artifact } from '../../artifact';
import { BrowserContext } from '../../browserContext'; import { BrowserContext } from '../../browserContext';
import { ElementHandle } from '../../dom'; import { ElementHandle } from '../../dom';
@ -33,9 +34,9 @@ import type { APIRequestContext } from '../../fetch';
import type { CallMetadata, InstrumentationListener } from '../../instrumentation'; import type { CallMetadata, InstrumentationListener } from '../../instrumentation';
import { SdkObject } from '../../instrumentation'; import { SdkObject } from '../../instrumentation';
import { Page } from '../../page'; import { Page } from '../../page';
import type * as har from '../../supplements/har/har'; import type * as har from '../../har/har';
import type { HarTracerDelegate } from '../../supplements/har/harTracer'; import type { HarTracerDelegate } from '../../har/harTracer';
import { HarTracer } from '../../supplements/har/harTracer'; import { HarTracer } from '../../har/harTracer';
import type { FrameSnapshot } from '../common/snapshotTypes'; import type { FrameSnapshot } from '../common/snapshotTypes';
import type * as trace from '../common/traceEvents'; import type * as trace from '../common/traceEvents';
import { VERSION } from '../common/traceEvents'; import { VERSION } from '../common/traceEvents';
@ -390,10 +391,9 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
private _startScreencastInPage(page: Page) { private _startScreencastInPage(page: Page) {
page.setScreencastOptions(kScreencastOptions); page.setScreencastOptions(kScreencastOptions);
const prefix = page.guid; const prefix = page.guid;
let frameSeq = 0;
this._screencastListeners.push( this._screencastListeners.push(
eventsHelper.addEventListener(page, Page.Events.ScreencastFrame, params => { eventsHelper.addEventListener(page, Page.Events.ScreencastFrame, params => {
const suffix = String(++frameSeq).padStart(10, '0'); const suffix = params.timestamp || Date.now();
const sha1 = `${prefix}-${suffix}.jpeg`; const sha1 = `${prefix}-${suffix}.jpeg`;
const event: trace.ScreencastFrameTraceEvent = { const event: trace.ScreencastFrameTraceEvent = {
type: 'screencast-frame', type: 'screencast-frame',

View file

@ -22,9 +22,9 @@ import { BaseSnapshotStorage } from '../../../../../trace-viewer/src/snapshotSto
import type { SnapshotterBlob, SnapshotterDelegate } from '../recorder/snapshotter'; import type { SnapshotterBlob, SnapshotterDelegate } from '../recorder/snapshotter';
import { Snapshotter } from '../recorder/snapshotter'; import { Snapshotter } from '../recorder/snapshotter';
import type { ElementHandle } from '../../dom'; import type { ElementHandle } from '../../dom';
import type { HarTracerDelegate } from '../../supplements/har/harTracer'; import type { HarTracerDelegate } from '../../har/harTracer';
import { HarTracer } from '../../supplements/har/harTracer'; import { HarTracer } from '../../har/harTracer';
import type * as har from '../../supplements/har/har'; import type * as har from '../../har/har';
export class InMemorySnapshotter extends BaseSnapshotStorage implements SnapshotterDelegate, HarTracerDelegate { export class InMemorySnapshotter extends BaseSnapshotStorage implements SnapshotterDelegate, HarTracerDelegate {
private _blobs = new Map<string, Buffer>(); private _blobs = new Map<string, Buffer>();

View file

@ -1,5 +1,6 @@
[*] [*]
../../ ../../
../../registry/
../../../generated/ ../../../generated/
../../../utils/ ../../../utils/
../../chromium/crApp.ts ../../chromium/crApp.ts

View file

@ -853,11 +853,11 @@ export module Protocol {
*/ */
export interface Grouping { export interface Grouping {
/** /**
* Source of the media query: "media-rule" if specified by a @media rule, "media-import-rule" if specified by an @import rule, "media-link-node" if specified by a "media" attribute in a linked style sheet's LINK tag, "media-style-node" if specified by a "media" attribute in an inline style sheet's STYLE tag, "supports-rule" if specified by an @supports rule, "layer-rule" if specified by an @layer rule. * Source of the media query: "media-rule" if specified by a @media rule, "media-import-rule" if specified by an @import rule, "media-link-node" if specified by a "media" attribute in a linked style sheet's LINK tag, "media-style-node" if specified by a "media" attribute in an inline style sheet's STYLE tag, "supports-rule" if specified by an @supports rule, "layer-rule" if specified by an @layer rule, "container-rule" if specified by an @container rule.
*/ */
type: "media-rule"|"media-import-rule"|"media-link-node"|"media-style-node"|"supports-rule"|"layer-rule"|"layer-import-rule"; type: "media-rule"|"media-import-rule"|"media-link-node"|"media-style-node"|"supports-rule"|"layer-rule"|"layer-import-rule"|"container-rule";
/** /**
* Query text if specified by a @media or @supports rule. Layer name (or not present for anonymous layers) for @layer rules. * Query text if specified by a @media, @supports, or @container rule. Layer name (or not present for anonymous layers) for @layer rules.
*/ */
text?: string; text?: string;
/** /**
@ -5345,6 +5345,10 @@ the top of the viewport and Y increases as it proceeds towards the bottom of the
* Connection information for the completed request. * Connection information for the completed request.
*/ */
securityConnection?: Security.Connection; securityConnection?: Security.Connection;
/**
* Whether or not the connection was proxied through a server. If <code>true</code>, the <code>remoteAddress</code> will be for the proxy server, not the server that provided the resource to the proxy server.
*/
isProxyConnection?: boolean;
} }
/** /**
* WebSocket request data. * WebSocket request data.

Some files were not shown because too many files have changed in this diff Show more