Compare commits

...

21 commits

Author SHA1 Message Date
Playwright Service a51d4a39b8
cherry-pick(#23315): docs: fix since version for the methods new in 1.34 (#23316) 2023-05-26 11:12:25 -07:00
Andrey Lushnikov f11873a895
chore: mark 1.34.3 (#23268) 2023-05-24 16:07:04 -07:00
Andrey Lushnikov b5ff463779 cherry-pick(#23213): devops: do not use NPX during docker container build
The postmortem of the broken docker image:
- We use `playwright-core` package when bootstrapping our docker
container.
- Due to accidental removal of `bin` entry in the `playwright-core` (see
  https://github.com/microsoft/playwright/issues/23206), the `npx
  playwright install --with-deps` was using the last published
  Playwright version instead of a locally-built playwright binary

This patch starts using `npx exec --no` to make sure that we never call
remote commands during docker image build.
2023-05-24 15:20:45 -07:00
Pavel Feldman 4417a97482 cherry-pick(#23260): chore: move to npx playwright-core install when using core 2023-05-24 12:20:29 -07:00
Dmitry Gozman 2f3d88f3d5
chore: mark 1.34.2 (#23242) 2023-05-23 13:55:41 -07:00
Pavel Feldman ba85623f40 cherry-pick(#23241): chore: keep @playwright/test/cli exposed 2023-05-23 12:33:43 -07:00
Andrey Lushnikov fe024dd5e3
chore: mark 1.34.1 (#23214) 2023-05-22 20:21:08 -07:00
Andrey Lushnikov ac4127d6cf cherry-pick(#23209): fix: add missing webkit deps on ubuntu 20.04
Fixes https://github.com/microsoft/playwright/issues/23186
2023-05-22 16:44:06 -07:00
Pavel Feldman b8761ef8de cherry-pick(#23208): fix(html): fix the filter to respect status 2023-05-22 15:40:19 -07:00
Pavel Feldman 55eebadfa2 cherry-pick(#23211): chore: allow stub JSX instances in type module 2023-05-22 15:37:26 -07:00
Pavel Feldman bc9f9b79c8 cherry-pick(#23203): chore: fix playwright-core cli 2023-05-22 12:38:23 -07:00
Andrey Lushnikov 4aee014caf
chore: mark 1.34.0 (#23123) 2023-05-19 17:28:10 -07:00
Dmitry Gozman bf25a93617
cherry-pick(#23171): fix(tracing): when zipping remotely, use correct file name (#23173)
Fixes #23108.
2023-05-19 15:20:42 -07:00
Pavel Feldman 7effaf4f22 cherry-pick(#23166): chore: fix file view padding 2023-05-19 11:08:13 -07:00
Andrey Lushnikov ef18287266 cherry-pick(#23165): docs: update release notes
Signed-off-by: Andrey Lushnikov <aslushnikov@gmail.com>
Signed-off-by: Max Schmitt <max@schmitt.mx>
Co-authored-by: Max Schmitt <max@schmitt.mx>
2023-05-19 10:42:59 -07:00
Pavel Feldman 552cba8c05 cherry-pick(#23148): chore: do not annotate actions after failed ones as timed out 2023-05-18 15:54:00 -07:00
Dmitry Gozman cd94a3f01d
cherry-pick(#23144): feat(connect): pw:server:channel and pw:socks debug logs (#23149)
Fixes #23081.
2023-05-18 15:04:39 -07:00
Playwright Service 3543a741fd
cherry-pick(#23140): docs: add dialog/consoleMessage helper methods for language bindings (#23150)
This PR cherry-picks the following commits:

- 9b4f9b1136

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-18 23:59:10 +02:00
Pavel Feldman 7196f82e52 cherry-pick(#23143): test: add attachment tests 2023-05-18 11:49:58 -07:00
Pavel Feldman 32c247b815 cherry-pick(#23139): chore: opt out of trace attachments 2023-05-18 11:35:56 -07:00
Playwright Service c551cce74e
cherry-pick(#23130): feat(chromium): roll to r1064 (#23132)
This PR cherry-picks the following commits:

- 5957d8f306

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-18 16:28:11 +02:00
60 changed files with 507 additions and 238 deletions

View file

@ -1,6 +1,6 @@
# 🎭 Playwright
[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[![Chromium version](https://img.shields.io/badge/chromium-114.0.5735.26-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-113.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[![WebKit version](https://img.shields.io/badge/webkit-16.4-blue.svg?logo=safari)](https://webkit.org/)<!-- GEN:stop -->
[![npm version](https://img.shields.io/npm/v/playwright.svg)](https://www.npmjs.com/package/playwright) <!-- GEN:chromium-version-badge -->[![Chromium version](https://img.shields.io/badge/chromium-114.0.5735.35-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-113.0-blue.svg?logo=firefoxbrowser)](https://www.mozilla.org/en-US/firefox/new/)<!-- GEN:stop --> <!-- GEN:webkit-version-badge -->[![WebKit version](https://img.shields.io/badge/webkit-16.4-blue.svg?logo=safari)](https://webkit.org/)<!-- GEN:stop -->
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright)
@ -8,7 +8,7 @@ Playwright is a framework for Web Testing and Automation. It allows testing [Chr
| | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->114.0.5735.26<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->114.0.5735.35<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->16.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Firefox <!-- GEN:firefox-version -->113.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |

View file

@ -95,7 +95,7 @@ Emitted when Browser context gets closed. This might happen because of one of th
* The [`method: Browser.close`] method was called.
## event: BrowserContext.console
* since: v1.33
* since: v1.34
* langs:
- alias-java: consoleMessage
- argument: <[ConsoleMessage]>
@ -156,7 +156,7 @@ await page.EvaluateAsync("console.log('hello', 5, { foo: 'bar' })");
## event: BrowserContext.dialog
* since: v1.33
* since: v1.34
- argument: <[Dialog]>
Emitted when a JavaScript dialog appears, such as `alert`, `prompt`, `confirm` or `beforeunload`. Listener **must** either [`method: Dialog.accept`] or [`method: Dialog.dismiss`] the dialog - otherwise the page will [freeze](https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#never_blocking) waiting for the dialog, and actions like click will never finish.
@ -180,10 +180,7 @@ context.on("dialog", lambda dialog: dialog.accept())
```
```csharp
context.RequestFailed += (_, request) =>
{
Console.WriteLine(request.Url + " " + request.Failure);
};
context.Dialog += (_, dialog) => dialog.AcceptAsync();
```
:::note
@ -1456,6 +1453,37 @@ Condition to wait for.
### option: BrowserContext.waitForCondition.timeout = %%-wait-for-function-timeout-%%
* since: v1.32
## async method: BrowserContext.waitForConsoleMessage
* since: v1.34
* langs: java, python, csharp
- alias-python: expect_console_message
- alias-csharp: RunAndWaitForConsoleMessage
- returns: <[ConsoleMessage]>
Performs action and waits for a [ConsoleMessage] to be logged by in the pages in the context. If predicate is provided, it passes
[ConsoleMessage] value into the `predicate` function and waits for `predicate(message)` to return a truthy value.
Will throw an error if the page is closed before the [`event: BrowserContext.console`] event is fired.
## async method: BrowserContext.waitForConsoleMessage
* since: v1.34
* langs: python
- returns: <[EventContextManager]<[ConsoleMessage]>>
### param: BrowserContext.waitForConsoleMessage.action = %%-csharp-wait-for-event-action-%%
* since: v1.34
### option: BrowserContext.waitForConsoleMessage.predicate
* since: v1.34
- `predicate` <[function]\([ConsoleMessage]\):[boolean]>
Receives the [ConsoleMessage] object and resolves to truthy value when the waiting should resolve.
### option: BrowserContext.waitForConsoleMessage.timeout = %%-wait-for-event-timeout-%%
* since: v1.34
### param: BrowserContext.waitForConsoleMessage.callback = %%-java-wait-for-event-callback-%%
* since: v1.34
## async method: BrowserContext.waitForEvent
* since: v1.8
* langs: js, python

View file

@ -126,8 +126,8 @@ List of arguments passed to a `console` function call. See also [`event: Page.co
URL of the resource followed by 0-based line and column numbers in the resource formatted as `URL:line:column`.
## method: ConsoleMessage.page
* since: v1.33
- returns: <[Page]|[null]>
* since: v1.34
- returns: <[null]|[Page]>
The page that produced this console message, if any.

View file

@ -138,8 +138,8 @@ Returns when the dialog has been dismissed.
A message displayed in the dialog.
## method: Dialog.page
* since: v1.33
- returns: <[Page]|[null]>
* since: v1.34
- returns: <[null]|[Page]>
The page that initiated this dialog, if available.

View file

@ -232,7 +232,7 @@ jobs:
name: 'Playwright Tests'
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.34.0-jammy
image: mcr.microsoft.com/playwright:v1.34.3-jammy
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
@ -256,7 +256,7 @@ jobs:
name: 'Playwright Tests'
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright/python:v1.34.0-jammy
image: mcr.microsoft.com/playwright/python:v1.34.3-jammy
steps:
- uses: actions/checkout@v3
- name: Set up Python
@ -284,7 +284,7 @@ jobs:
name: 'Playwright Tests'
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright/java:v1.34.0-jammy
image: mcr.microsoft.com/playwright/java:v1.34.3-jammy
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
@ -309,7 +309,7 @@ jobs:
name: 'Playwright Tests'
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright/dotnet:v1.34.0-jammy
image: mcr.microsoft.com/playwright/dotnet:v1.34.3-jammy
steps:
- uses: actions/checkout@v3
- name: Setup dotnet
@ -338,7 +338,7 @@ jobs:
name: 'Playwright Tests - ${{ matrix.project }} - Shard ${{ matrix.shardIndex }} of ${{ matrix.shardTotal }}'
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.34.0-jammy
image: mcr.microsoft.com/playwright:v1.34.3-jammy
strategy:
fail-fast: false
matrix:
@ -413,7 +413,7 @@ jobs:
- deployment: Run_E2E_Tests
pool:
vmImage: ubuntu-22.04
container: mcr.microsoft.com/playwright:v1.34.0-jammy
container: mcr.microsoft.com/playwright:v1.34.3-jammy
environment: testing
strategy:
runOnce:
@ -439,7 +439,7 @@ jobs:
- deployment: Run_E2E_Tests
pool:
vmImage: ubuntu-22.04
container: mcr.microsoft.com/playwright:v1.34.0-jammy
container: mcr.microsoft.com/playwright:v1.34.3-jammy
environment: testing
strategy:
runOnce:
@ -483,7 +483,7 @@ Running Playwright on CircleCI is very similar to running on GitHub Actions. In
executors:
pw-jammy-development:
docker:
- image: mcr.microsoft.com/playwright:v1.34.0-jammy
- image: mcr.microsoft.com/playwright:v1.34.3-jammy
```
Note: When using the docker agent definition, you are specifying the resource class of where playwright runs to the 'medium' tier [here](https://circleci.com/docs/configuration-reference?#docker-execution-environment). The default behavior of Playwright is to set the number of workers to the detected core count (2 in the case of the medium tier). Overriding the number of workers to greater than this number will cause unnecessary timeouts and failures.
@ -507,7 +507,7 @@ to run tests on Jenkins.
```groovy
pipeline {
agent { docker { image 'mcr.microsoft.com/playwright:v1.34.0-jammy' } }
agent { docker { image 'mcr.microsoft.com/playwright:v1.34.3-jammy' } }
stages {
stage('e2e-tests') {
steps {
@ -525,7 +525,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)).
```yml
image: mcr.microsoft.com/playwright:v1.34.0-jammy
image: mcr.microsoft.com/playwright:v1.34.3-jammy
```
### GitLab CI
@ -538,7 +538,7 @@ stages:
tests:
stage: test
image: mcr.microsoft.com/playwright:v1.34.0-jammy
image: mcr.microsoft.com/playwright:v1.34.3-jammy
script:
...
```
@ -554,7 +554,7 @@ stages:
tests:
stage: test
image: mcr.microsoft.com/playwright:v1.34.0-jammy
image: mcr.microsoft.com/playwright:v1.34.3-jammy
parallel: 7
script:
- npm ci
@ -569,7 +569,7 @@ stages:
tests:
stage: test
image: mcr.microsoft.com/playwright:v1.34.0-jammy
image: mcr.microsoft.com/playwright:v1.34.3-jammy
parallel:
matrix:
- PROJECT: ['chromium', 'webkit']

View file

@ -18,19 +18,19 @@ This Docker image is intended to be used for testing and development purposes on
### Pull the image
```bash js
docker pull mcr.microsoft.com/playwright:v1.34.0-jammy
docker pull mcr.microsoft.com/playwright:v1.34.3-jammy
```
```bash python
docker pull mcr.microsoft.com/playwright/python:v1.34.0-jammy
docker pull mcr.microsoft.com/playwright/python:v1.34.3-jammy
```
```bash csharp
docker pull mcr.microsoft.com/playwright/dotnet:v1.34.0-jammy
docker pull mcr.microsoft.com/playwright/dotnet:v1.34.3-jammy
```
```bash java
docker pull mcr.microsoft.com/playwright/java:v1.34.0-jammy
docker pull mcr.microsoft.com/playwright/java:v1.34.3-jammy
```
### Run the image
@ -42,19 +42,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.
```bash js
docker run -it --rm --ipc=host mcr.microsoft.com/playwright:v1.34.0-jammy /bin/bash
docker run -it --rm --ipc=host mcr.microsoft.com/playwright:v1.34.3-jammy /bin/bash
```
```bash python
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/python:v1.34.0-jammy /bin/bash
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/python:v1.34.3-jammy /bin/bash
```
```bash csharp
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/dotnet:v1.34.0-jammy /bin/bash
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/dotnet:v1.34.3-jammy /bin/bash
```
```bash java
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/java:v1.34.0-jammy /bin/bash
docker run -it --rm --ipc=host mcr.microsoft.com/playwright/java:v1.34.3-jammy /bin/bash
```
#### Crawling and scraping
@ -62,19 +62,19 @@ docker run -it --rm --ipc=host mcr.microsoft.com/playwright/java:v1.34.0-jammy /
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
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright:v1.34.0-jammy /bin/bash
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright:v1.34.3-jammy /bin/bash
```
```bash python
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/python:v1.34.0-jammy /bin/bash
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/python:v1.34.3-jammy /bin/bash
```
```bash csharp
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/dotnet:v1.34.0-jammy /bin/bash
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/dotnet:v1.34.3-jammy /bin/bash
```
```bash java
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/java:v1.34.0-jammy /bin/bash
docker run -it --rm --ipc=host --user pwuser --security-opt seccomp=seccomp_profile.json mcr.microsoft.com/playwright/java:v1.34.3-jammy /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:

View file

@ -8,8 +8,10 @@ import LiteYouTube from '@site/src/components/LiteYouTube';
## Version 1.34
### New APIs
### Highlights
* UI Mode now shows steps, fixtures and attachments:
![UI Mode attachments](https://github.com/microsoft/playwright/assets/746130/1d280419-d79a-4a56-b2dc-54d631281d56)
* New property [`property: TestProject.teardown`] to specify a project that needs to run after this
and all dependent projects have finished. Teardown is useful to cleanup any resources acquired by this project.
@ -46,6 +48,18 @@ import LiteYouTube from '@site/src/components/LiteYouTube';
],
});
```
* New method [`expect.configure`](./test-assertions.md#expectconfigure) to
create pre-configured expect instance with its own defaults such as `timeout`
and `soft`.
```js
const slowExpect = expect.configure({ timeout: 10000 });
await slowExpect(locator).toHaveText('Submit');
// Always do soft assertions.
const softExpect = expect.configure({ soft: true });
```
* New options `stderr` and `stdout` in [`property: TestConfig.webServer`] to configure output handling:
```js title="playwright.config.ts"

View file

@ -526,6 +526,7 @@ export default defineConfig({
* since: v1.10
- type: <[Object]|[TraceMode]<"off"|"on"|"retain-on-failure"|"on-first-retry">>
- `mode` <[TraceMode]<"off"|"on"|"retain-on-failure"|"on-first-retry"|"on-all-retries">> Trace recording mode.
- `attachments` ?<[boolean]> Whether to include test attachments. Defaults to true. Optional.
- `screenshots` ?<[boolean]> Whether to capture screenshots during tracing. Screenshots are used to build a timeline preview. Defaults to true. Optional.
- `snapshots` ?<[boolean]> Whether to capture DOM snapshot on every action. Defaults to true. Optional.
- `sources` ?<[boolean]> Whether to include source files for trace actions. Defaults to true. Optional.

View file

@ -128,7 +128,10 @@ defaults such as `timeout` and `soft`.
```js
const slowExpect = expect.configure({ timeout: 10000 });
await slowExpect(locator).toHaveText('Submit);
await slowExpect(locator).toHaveText('Submit');
// Always do soft assertions.
const softExpect = expect.configure({ soft: true });
```
## expect.poll

View file

@ -42,7 +42,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:
```bash
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.34.0-jammy /bin/bash
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.34.3-jammy /bin/bash
npm install
npx playwright test --update-snapshots
```

78
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "playwright-internal",
"version": "1.34.0-next",
"version": "1.34.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "playwright-internal",
"version": "1.34.0-next",
"version": "1.34.3",
"license": "Apache-2.0",
"workspaces": [
"packages/*"
@ -6197,11 +6197,11 @@
}
},
"packages/playwright": {
"version": "1.34.0-next",
"version": "1.34.3",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.34.0-next"
"playwright-core": "1.34.3"
},
"bin": {
"playwright": "cli.js"
@ -6211,11 +6211,11 @@
}
},
"packages/playwright-chromium": {
"version": "1.34.0-next",
"version": "1.34.3",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.34.0-next"
"playwright-core": "1.34.3"
},
"bin": {
"playwright": "cli.js"
@ -6225,7 +6225,7 @@
}
},
"packages/playwright-core": {
"version": "1.34.0-next",
"version": "1.34.3",
"license": "Apache-2.0",
"engines": {
"node": ">=14"
@ -6233,10 +6233,10 @@
},
"packages/playwright-ct-core": {
"name": "@playwright/experimental-ct-core",
"version": "1.34.0-next",
"version": "1.34.3",
"license": "Apache-2.0",
"dependencies": {
"@playwright/test": "1.34.0-next",
"@playwright/test": "1.34.3",
"vite": "^4.3.3"
},
"bin": {
@ -6248,10 +6248,10 @@
},
"packages/playwright-ct-react": {
"name": "@playwright/experimental-ct-react",
"version": "1.34.0-next",
"version": "1.34.3",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@vitejs/plugin-react": "^4.0.0"
},
"bin": {
@ -6280,10 +6280,10 @@
},
"packages/playwright-ct-react17": {
"name": "@playwright/experimental-ct-react17",
"version": "1.34.0-next",
"version": "1.34.3",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@vitejs/plugin-react": "^4.0.0"
},
"bin": {
@ -6312,10 +6312,10 @@
},
"packages/playwright-ct-solid": {
"name": "@playwright/experimental-ct-solid",
"version": "1.34.0-next",
"version": "1.34.3",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"vite-plugin-solid": "^2.7.0"
},
"bin": {
@ -6330,10 +6330,10 @@
},
"packages/playwright-ct-svelte": {
"name": "@playwright/experimental-ct-svelte",
"version": "1.34.0-next",
"version": "1.34.3",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@sveltejs/vite-plugin-svelte": "^2.1.1"
},
"bin": {
@ -6348,10 +6348,10 @@
},
"packages/playwright-ct-vue": {
"name": "@playwright/experimental-ct-vue",
"version": "1.34.0-next",
"version": "1.34.3",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@vitejs/plugin-vue": "^4.2.1"
},
"bin": {
@ -6399,10 +6399,10 @@
},
"packages/playwright-ct-vue2": {
"name": "@playwright/experimental-ct-vue2",
"version": "1.34.0-next",
"version": "1.34.3",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@vitejs/plugin-vue2": "^2.2.0"
},
"bin": {
@ -6416,11 +6416,11 @@
}
},
"packages/playwright-firefox": {
"version": "1.34.0-next",
"version": "1.34.3",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.34.0-next"
"playwright-core": "1.34.3"
},
"bin": {
"playwright": "cli.js"
@ -6431,11 +6431,11 @@
},
"packages/playwright-test": {
"name": "@playwright/test",
"version": "1.34.0-next",
"version": "1.34.3",
"license": "Apache-2.0",
"dependencies": {
"@types/node": "*",
"playwright-core": "1.34.0-next"
"playwright-core": "1.34.3"
},
"bin": {
"playwright": "cli.js"
@ -6448,11 +6448,11 @@
}
},
"packages/playwright-webkit": {
"version": "1.34.0-next",
"version": "1.34.3",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.34.0-next"
"playwright-core": "1.34.3"
},
"bin": {
"playwright": "cli.js"
@ -7262,14 +7262,14 @@
"@playwright/experimental-ct-core": {
"version": "file:packages/playwright-ct-core",
"requires": {
"@playwright/test": "1.34.0-next",
"@playwright/test": "1.34.3",
"vite": "^4.3.3"
}
},
"@playwright/experimental-ct-react": {
"version": "file:packages/playwright-ct-react",
"requires": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@vitejs/plugin-react": "^4.0.0"
},
"dependencies": {
@ -7289,7 +7289,7 @@
"@playwright/experimental-ct-react17": {
"version": "file:packages/playwright-ct-react17",
"requires": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@vitejs/plugin-react": "^4.0.0"
},
"dependencies": {
@ -7309,7 +7309,7 @@
"@playwright/experimental-ct-solid": {
"version": "file:packages/playwright-ct-solid",
"requires": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"solid-js": "^1.7.0",
"vite-plugin-solid": "^2.7.0"
}
@ -7317,7 +7317,7 @@
"@playwright/experimental-ct-svelte": {
"version": "file:packages/playwright-ct-svelte",
"requires": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@sveltejs/vite-plugin-svelte": "^2.1.1",
"svelte": "^3.55.1"
}
@ -7325,7 +7325,7 @@
"@playwright/experimental-ct-vue": {
"version": "file:packages/playwright-ct-vue",
"requires": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@vitejs/plugin-vue": "^4.2.1"
},
"dependencies": {
@ -7359,7 +7359,7 @@
"@playwright/experimental-ct-vue2": {
"version": "file:packages/playwright-ct-vue2",
"requires": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@vitejs/plugin-vue2": "^2.2.0",
"vue": "^2.7.14"
}
@ -7369,7 +7369,7 @@
"requires": {
"@types/node": "*",
"fsevents": "2.3.2",
"playwright-core": "1.34.0-next"
"playwright-core": "1.34.3"
}
},
"@sindresorhus/is": {
@ -9663,13 +9663,13 @@
"playwright": {
"version": "file:packages/playwright",
"requires": {
"playwright-core": "1.34.0-next"
"playwright-core": "1.34.3"
}
},
"playwright-chromium": {
"version": "file:packages/playwright-chromium",
"requires": {
"playwright-core": "1.34.0-next"
"playwright-core": "1.34.3"
}
},
"playwright-core": {
@ -9678,13 +9678,13 @@
"playwright-firefox": {
"version": "file:packages/playwright-firefox",
"requires": {
"playwright-core": "1.34.0-next"
"playwright-core": "1.34.3"
}
},
"playwright-webkit": {
"version": "file:packages/playwright-webkit",
"requires": {
"playwright-core": "1.34.0-next"
"playwright-core": "1.34.3"
}
},
"postcss": {

View file

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

View file

@ -21,6 +21,7 @@
background-color: var(--color-canvas-subtle);
padding: 0 8px;
border-bottom: none;
margin-top: 24px;
font-weight: 600;
line-height: 38px;
white-space: nowrap;

View file

@ -108,9 +108,11 @@ export class Filter {
if (test.outcome === 'skipped')
status = 'skipped';
const searchValues: SearchValues = {
text: (status + ' ' + test.projectName + ' ' + test.path.join(' ') + ' ' + test.title).toLowerCase(),
text: (status + ' ' + test.projectName + ' ' + test.location.file + ' ' + test.path.join(' ') + ' ' + test.title).toLowerCase(),
project: test.projectName.toLowerCase(),
status: status as any,
file: test.location.file,
line: String(test.location.line),
};
(test as any).searchValues = searchValues;
}
@ -127,9 +129,14 @@ export class Filter {
return false;
}
if (this.text.length) {
const matches = this.text.filter(t => searchValues.text.includes(t)).length === this.text.length;
if (!matches)
for (const text of this.text) {
if (searchValues.text.includes(text))
continue;
const location = text.split(':');
if (location.length === 2 && searchValues.file.includes(location[0]) && searchValues.line.includes(location[1]))
continue;
return false;
}
}
if (this.labels.length) {
const matches = this.labels.every(l => searchValues.text?.match(new RegExp(`(\\s|^)${escapeRegExp(l)}(\\s|$)`, 'g')));
@ -145,5 +152,7 @@ type SearchValues = {
text: string;
project: string;
status: 'passed' | 'failed' | 'flaky' | 'skipped';
file: string;
line: string;
};

View file

@ -55,7 +55,7 @@ test('should switch to actual', async ({ mount }) => {
for (let i = 0; i < imageCount; ++i) {
const image = images.nth(i);
const box = await image.boundingBox();
expect(box).toEqual({ x: 400, y: 124, width: 200, height: 200 });
expect(box).toEqual({ x: 400, y: 108, width: 200, height: 200 });
}
});
@ -70,7 +70,7 @@ test('should switch to expected', async ({ mount }) => {
for (let i = 0; i < imageCount; ++i) {
const image = images.nth(i);
const box = await image.boundingBox();
expect(box).toEqual({ x: 400, y: 124, width: 200, height: 200 });
expect(box).toEqual({ x: 400, y: 108, width: 200, height: 200 });
}
});
@ -79,5 +79,5 @@ test('should show diff by default', async ({ mount }) => {
const image = component.locator('img');
const box = await image.boundingBox();
expect(box).toEqual({ x: 400, y: 124, width: 200, height: 200 });
expect(box).toEqual({ x: 400, y: 108, width: 200, height: 200 });
});

View file

@ -33,7 +33,6 @@
height: 48px;
min-width: 70px;
box-shadow: inset 0 -1px 0 var(--color-border-muted) !important;
margin-bottom: 16px;
}
.tabbed-pane-tab-strip:focus {

View file

@ -46,7 +46,7 @@
.test-case-duration {
flex: none;
align-items: center;
padding: 0 8px 24px;
padding: 0 8px 8px;
}
.test-case-path {

View file

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

View file

@ -3,15 +3,15 @@
"browsers": [
{
"name": "chromium",
"revision": "1063",
"revision": "1064",
"installByDefault": true,
"browserVersion": "114.0.5735.26"
"browserVersion": "114.0.5735.35"
},
{
"name": "chromium-with-symbols",
"revision": "1063",
"revision": "1064",
"installByDefault": false,
"browserVersion": "114.0.5735.26"
"browserVersion": "114.0.5735.35"
},
{
"name": "chromium-tip-of-tree",

17
packages/playwright-core/cli.js Executable file
View file

@ -0,0 +1,17 @@
#!/usr/bin/env node
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = require('playwright-core/lib/cli/cli');

View file

@ -1,6 +1,6 @@
{
"name": "playwright-core",
"version": "1.34.0-next",
"version": "1.34.3",
"description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
@ -35,5 +35,8 @@
"./types/protocol": "./types/protocol.d.ts",
"./types/structs": "./types/structs.d.ts"
},
"bin": {
"playwright-core": "./cli.js"
},
"types": "types/types.d.ts"
}

View file

@ -23,11 +23,13 @@ const debugLoggerColorMap = {
'install': 34, // green
'download': 34, // green
'browser': 0, // reset
'proxy': 92, // purple
'socks': 92, // purple
'error': 160, // red,
'channel:command': 33, // blue
'channel:response': 202, // orange
'channel:event': 207, // magenta
'server': 45, // cyan
'server:channel': 34, // green
};
export type LogName = keyof typeof debugLoggerColorMap;

View file

@ -436,7 +436,6 @@ export class SocksProxy extends EventEmitter implements SocksConnectionClient {
this._server.listen(port, () => {
const port = (this._server.address() as AddressInfo).port;
this._port = port;
debugLogger.log('proxy', `Starting socks proxy server on port ${port}`);
f(port);
});
});
@ -525,8 +524,10 @@ export class SocksProxyHandler extends EventEmitter {
}
async socketRequested({ uid, host, port }: SocksSocketRequestedPayload): Promise<void> {
debugLogger.log('socks', `[${uid}] => request ${host}:${port}`);
if (!this._patternMatcher(host, port)) {
const payload: SocksSocketFailedPayload = { uid, errorCode: 'ERULESET' };
debugLogger.log('socks', `[${uid}] <= pattern error ${payload.errorCode}`);
this.emit(SocksProxyHandler.Events.SocksFailed, payload);
return;
}
@ -543,11 +544,13 @@ export class SocksProxyHandler extends EventEmitter {
});
socket.on('error', error => {
const payload: SocksSocketErrorPayload = { uid, error: error.message };
debugLogger.log('socks', `[${uid}] <= network socket error ${payload.error}`);
this.emit(SocksProxyHandler.Events.SocksError, payload);
this._sockets.delete(uid);
});
socket.on('end', () => {
const payload: SocksSocketEndPayload = { uid };
debugLogger.log('socks', `[${uid}] <= network socket closed`);
this.emit(SocksProxyHandler.Events.SocksEnd, payload);
this._sockets.delete(uid);
});
@ -555,9 +558,11 @@ export class SocksProxyHandler extends EventEmitter {
const localPort = socket.localPort;
this._sockets.set(uid, socket);
const payload: SocksSocketConnectedPayload = { uid, host: localAddress, port: localPort };
debugLogger.log('socks', `[${uid}] <= connected to network ${payload.host}:${payload.port}`);
this.emit(SocksProxyHandler.Events.SocksConnected, payload);
} catch (error) {
const payload: SocksSocketFailedPayload = { uid, errorCode: error.code };
debugLogger.log('socks', `[${uid}] <= connect error ${payload.errorCode}`);
this.emit(SocksProxyHandler.Events.SocksFailed, payload);
}
}
@ -567,6 +572,7 @@ export class SocksProxyHandler extends EventEmitter {
}
socketClosed({ uid }: SocksSocketClosedPayload): void {
debugLogger.log('socks', `[${uid}] <= browser socket closed`);
this._sockets.get(uid)?.destroy();
this._sockets.delete(uid);
}

View file

@ -25,6 +25,8 @@ import type { LaunchOptions } from '../server/types';
import { AndroidDevice } from '../server/android/android';
import { DebugControllerDispatcher } from '../server/dispatchers/debugControllerDispatcher';
import { startProfiling, stopProfiling } from '../utils';
import { monotonicTime } from '../utils';
import { debugLogger } from '../common/debugLogger';
export type ClientType = 'controller' | 'playwright' | 'launch-browser' | 'reuse-browser' | 'pre-launched-browser-or-android';
@ -46,14 +48,14 @@ export class PlaywrightConnection {
private _onClose: () => void;
private _dispatcherConnection: DispatcherConnection;
private _cleanups: (() => Promise<void>)[] = [];
private _debugLog: (m: string) => void;
private _id: string;
private _disconnected = false;
private _preLaunched: PreLaunched;
private _options: Options;
private _root: DispatcherScope;
private _profileName: string;
constructor(lock: Promise<void>, clientType: ClientType, ws: WebSocket, options: Options, preLaunched: PreLaunched, log: (m: string) => void, onClose: () => void) {
constructor(lock: Promise<void>, clientType: ClientType, ws: WebSocket, options: Options, preLaunched: PreLaunched, id: string, onClose: () => void) {
this._ws = ws;
this._preLaunched = preLaunched;
this._options = options;
@ -63,18 +65,25 @@ export class PlaywrightConnection {
if (clientType === 'pre-launched-browser-or-android')
assert(preLaunched.browser || preLaunched.androidDevice);
this._onClose = onClose;
this._debugLog = log;
this._id = id;
this._profileName = `${new Date().toISOString()}-${clientType}`;
this._dispatcherConnection = new DispatcherConnection();
this._dispatcherConnection.onmessage = async message => {
await lock;
if (ws.readyState !== ws.CLOSING)
ws.send(JSON.stringify(message));
if (ws.readyState !== ws.CLOSING) {
const messageString = JSON.stringify(message);
if (debugLogger.isEnabled('server:channel'))
debugLogger.log('server:channel', `[${this._id}] ${monotonicTime() * 1000} SEND ► ${messageString}`);
ws.send(messageString);
}
};
ws.on('message', async (message: string) => {
await lock;
this._dispatcherConnection.dispatch(JSON.parse(Buffer.from(message).toString()));
const messageString = Buffer.from(message).toString();
if (debugLogger.isEnabled('server:channel'))
debugLogger.log('server:channel', `[${this._id}] ${monotonicTime() * 1000} ◀ RECV ${messageString}`);
this._dispatcherConnection.dispatch(JSON.parse(messageString));
});
ws.on('close', () => this._onDisconnect());
@ -100,7 +109,7 @@ export class PlaywrightConnection {
}
private async _initPlaywrightConnectMode(scope: RootDispatcher) {
this._debugLog(`engaged playwright.connect mode`);
debugLogger.log('server', `[${this._id}] engaged playwright.connect mode`);
const playwright = createPlaywright('javascript');
// Close all launched browsers on disconnect.
this._cleanups.push(async () => {
@ -112,7 +121,7 @@ export class PlaywrightConnection {
}
private async _initLaunchBrowserMode(scope: RootDispatcher) {
this._debugLog(`engaged launch mode for "${this._options.browserName}"`);
debugLogger.log('server', `[${this._id}] engaged launch mode for "${this._options.browserName}"`);
const playwright = createPlaywright('javascript');
const ownedSocksProxy = await this._createOwnedSocksProxy(playwright);
@ -131,7 +140,7 @@ export class PlaywrightConnection {
}
private async _initPreLaunchedBrowserMode(scope: RootDispatcher) {
this._debugLog(`engaged pre-launched (browser) mode`);
debugLogger.log('server', `[${this._id}] engaged pre-launched (browser) mode`);
const playwright = this._preLaunched.playwright!;
// Note: connected client owns the socks proxy and configures the pattern.
@ -154,7 +163,7 @@ export class PlaywrightConnection {
}
private async _initPreLaunchedAndroidMode(scope: RootDispatcher) {
this._debugLog(`engaged pre-launched (Android) mode`);
debugLogger.log('server', `[${this._id}] engaged pre-launched (Android) mode`);
const playwright = this._preLaunched.playwright!;
const androidDevice = this._preLaunched.androidDevice!;
androidDevice.on(AndroidDevice.Events.Close, () => {
@ -167,7 +176,7 @@ export class PlaywrightConnection {
}
private _initDebugControllerMode(): DebugControllerDispatcher {
this._debugLog(`engaged reuse controller mode`);
debugLogger.log('server', `[${this._id}] engaged reuse controller mode`);
const playwright = this._preLaunched.playwright!;
// Always create new instance based on the reused Playwright instance.
return new DebugControllerDispatcher(this._dispatcherConnection, playwright.debugController);
@ -177,7 +186,7 @@ export class PlaywrightConnection {
// Note: reuse browser mode does not support socks proxy, because
// clients come and go, while the browser stays the same.
this._debugLog(`engaged reuse browsers mode for ${this._options.browserName}`);
debugLogger.log('server', `[${this._id}] engaged reuse browsers mode for ${this._options.browserName}`);
const playwright = this._preLaunched.playwright!;
const requestedOptions = launchOptionsHash(this._options.launchOptions);
@ -232,27 +241,27 @@ export class PlaywrightConnection {
const socksProxy = new SocksProxy();
socksProxy.setPattern(this._options.socksProxyPattern);
playwright.options.socksProxyPort = await socksProxy.listen(0);
this._debugLog(`started socks proxy on port ${playwright.options.socksProxyPort}`);
debugLogger.log('server', `[${this._id}] started socks proxy on port ${playwright.options.socksProxyPort}`);
this._cleanups.push(() => socksProxy.close());
return socksProxy;
}
private async _onDisconnect(error?: Error) {
this._disconnected = true;
this._debugLog(`disconnected. error: ${error}`);
debugLogger.log('server', `[${this._id}] disconnected. error: ${error}`);
this._root._dispose();
this._debugLog(`starting cleanup`);
debugLogger.log('server', `[${this._id}] starting cleanup`);
for (const cleanup of this._cleanups)
await cleanup().catch(() => {});
await stopProfiling(this._profileName);
this._onClose();
this._debugLog(`finished cleanup`);
debugLogger.log('server', `[${this._id}] finished cleanup`);
}
async close(reason?: { code: number, reason: string }) {
if (this._disconnected)
return;
this._debugLog(`force closing connection: ${reason?.reason || ''} (${reason?.code || 0})`);
debugLogger.log('server', `[${this._id}] force closing connection: ${reason?.reason || ''} (${reason?.code || 0})`);
try {
this._ws.close(reason?.code, reason?.reason);
} catch (e) {

View file

@ -14,7 +14,7 @@
* limitations under the License.
*/
import { debug, wsServer } from '../utilsBundle';
import { wsServer } from '../utilsBundle';
import type { WebSocketServer } from '../utilsBundle';
import http from 'http';
import type { Browser } from '../server/browser';
@ -26,17 +26,11 @@ import type { LaunchOptions } from '../server/types';
import { ManualPromise } from '../utils/manualPromise';
import type { AndroidDevice } from '../server/android/android';
import { type SocksProxy } from '../common/socksProxy';
const debugLog = debug('pw:server');
import { debugLogger } from '../common/debugLogger';
let lastConnectionId = 0;
const kConnectionSymbol = Symbol('kConnection');
function newLogger() {
const id = ++lastConnectionId;
return (message: string) => debugLog(`[id=${id}] ${message}`);
}
type ServerOptions = {
path: string;
maxConnections: number;
@ -59,6 +53,8 @@ export class PlaywrightServer {
}
async listen(port: number = 0): Promise<string> {
debugLogger.log('server', `Server started at ${new Date()}`);
const server = http.createServer((request: http.IncomingMessage, response: http.ServerResponse) => {
if (request.method === 'GET' && request.url === '/json') {
response.setHeader('Content-Type', 'application/json');
@ -69,7 +65,7 @@ export class PlaywrightServer {
}
response.end('Running');
});
server.on('error', error => debugLog(error));
server.on('error', error => debugLogger.log('server', String(error)));
const wsEndpoint = await new Promise<string>((resolve, reject) => {
server.listen(port, () => {
@ -83,7 +79,7 @@ export class PlaywrightServer {
}).on('error', reject);
});
debugLog('Listening at ' + wsEndpoint);
debugLogger.log('server', 'Listening at ' + wsEndpoint);
this._wsServer = new wsServer({ server, path: this._options.path });
const browserSemaphore = new Semaphore(this._options.maxConnections);
const controllerSemaphore = new Semaphore(1);
@ -107,8 +103,8 @@ export class PlaywrightServer {
} catch (e) {
}
const log = newLogger();
log(`serving connection: ${request.url}`);
const id = String(++lastConnectionId);
debugLogger.log('server', `[${id}] serving connection: ${request.url}`);
const isDebugControllerClient = !!request.headers['x-playwright-debug-controller'];
const shouldReuseBrowser = !!request.headers['x-playwright-reuse-context'];
@ -145,7 +141,7 @@ export class PlaywrightServer {
androidDevice: this._options.preLaunchedAndroidDevice,
socksProxy: this._options.preLaunchedSocksProxy,
},
log, () => semaphore.release());
id, () => semaphore.release());
(ws as any)[kConnectionSymbol] = connection;
});
@ -156,7 +152,7 @@ export class PlaywrightServer {
const server = this._wsServer;
if (!server)
return;
debugLog('closing websocket server');
debugLogger.log('server', 'closing websocket server');
const waitForClose = new Promise(f => server.close(f));
// First disconnect all remaining clients.
await Promise.all(Array.from(server.clients).map(async ws => {
@ -169,15 +165,15 @@ export class PlaywrightServer {
}
}));
await waitForClose;
debugLog('closing http server');
debugLogger.log('server', 'closing http server');
await new Promise(f => server.options.server!.close(f));
this._wsServer = undefined;
debugLog('closed server');
debugLogger.log('server', 'closed server');
debugLog('closing browsers');
debugLogger.log('server', 'closing browsers');
if (this._preLaunchedPlaywright)
await Promise.all(this._preLaunchedPlaywright.allBrowsers().map(browser => browser.close()));
debugLog('closed browsers');
debugLogger.log('server', 'closed browsers');
}
}

View file

@ -110,7 +110,7 @@
"defaultBrowserType": "webkit"
},
"Galaxy S5": {
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 360,
"height": 640
@ -121,7 +121,7 @@
"defaultBrowserType": "chromium"
},
"Galaxy S5 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 640,
"height": 360
@ -132,7 +132,7 @@
"defaultBrowserType": "chromium"
},
"Galaxy S8": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 360,
"height": 740
@ -143,7 +143,7 @@
"defaultBrowserType": "chromium"
},
"Galaxy S8 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 740,
"height": 360
@ -154,7 +154,7 @@
"defaultBrowserType": "chromium"
},
"Galaxy S9+": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 320,
"height": 658
@ -165,7 +165,7 @@
"defaultBrowserType": "chromium"
},
"Galaxy S9+ landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 658,
"height": 320
@ -176,7 +176,7 @@
"defaultBrowserType": "chromium"
},
"Galaxy Tab S4": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
"viewport": {
"width": 712,
"height": 1138
@ -187,7 +187,7 @@
"defaultBrowserType": "chromium"
},
"Galaxy Tab S4 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
"viewport": {
"width": 1138,
"height": 712
@ -836,7 +836,7 @@
"defaultBrowserType": "webkit"
},
"LG Optimus L70": {
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 384,
"height": 640
@ -847,7 +847,7 @@
"defaultBrowserType": "chromium"
},
"LG Optimus L70 landscape": {
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; U; Android 4.4.2; en-us; LGMS323 Build/KOT49I.MS32310c) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 640,
"height": 384
@ -858,7 +858,7 @@
"defaultBrowserType": "chromium"
},
"Microsoft Lumia 550": {
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36 Edge/14.14263",
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36 Edge/14.14263",
"viewport": {
"width": 640,
"height": 360
@ -869,7 +869,7 @@
"defaultBrowserType": "chromium"
},
"Microsoft Lumia 550 landscape": {
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36 Edge/14.14263",
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 550) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36 Edge/14.14263",
"viewport": {
"width": 360,
"height": 640
@ -880,7 +880,7 @@
"defaultBrowserType": "chromium"
},
"Microsoft Lumia 950": {
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36 Edge/14.14263",
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36 Edge/14.14263",
"viewport": {
"width": 360,
"height": 640
@ -891,7 +891,7 @@
"defaultBrowserType": "chromium"
},
"Microsoft Lumia 950 landscape": {
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36 Edge/14.14263",
"userAgent": "Mozilla/5.0 (Windows Phone 10.0; Android 4.2.1; Microsoft; Lumia 950) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36 Edge/14.14263",
"viewport": {
"width": 640,
"height": 360
@ -902,7 +902,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 10": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
"viewport": {
"width": 800,
"height": 1280
@ -913,7 +913,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 10 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
"viewport": {
"width": 1280,
"height": 800
@ -924,7 +924,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 4": {
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 384,
"height": 640
@ -935,7 +935,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 4 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 640,
"height": 384
@ -946,7 +946,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 5": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 360,
"height": 640
@ -957,7 +957,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 5 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 640,
"height": 360
@ -968,7 +968,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 5X": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 412,
"height": 732
@ -979,7 +979,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 5X landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 732,
"height": 412
@ -990,7 +990,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 6": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 412,
"height": 732
@ -1001,7 +1001,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 6 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 732,
"height": 412
@ -1012,7 +1012,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 6P": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 412,
"height": 732
@ -1023,7 +1023,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 6P landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 732,
"height": 412
@ -1034,7 +1034,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 7": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
"viewport": {
"width": 600,
"height": 960
@ -1045,7 +1045,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 7 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
"viewport": {
"width": 960,
"height": 600
@ -1100,7 +1100,7 @@
"defaultBrowserType": "webkit"
},
"Pixel 2": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 411,
"height": 731
@ -1111,7 +1111,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 2 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 731,
"height": 411
@ -1122,7 +1122,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 2 XL": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 411,
"height": 823
@ -1133,7 +1133,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 2 XL landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 823,
"height": 411
@ -1144,7 +1144,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 3": {
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 393,
"height": 786
@ -1155,7 +1155,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 3 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 786,
"height": 393
@ -1166,7 +1166,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 4": {
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 353,
"height": 745
@ -1177,7 +1177,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 4 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 745,
"height": 353
@ -1188,7 +1188,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 4a (5G)": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"screen": {
"width": 412,
"height": 892
@ -1203,7 +1203,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 4a (5G) landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"screen": {
"height": 892,
"width": 412
@ -1218,7 +1218,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 5": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"screen": {
"width": 393,
"height": 851
@ -1233,7 +1233,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 5 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"screen": {
"width": 851,
"height": 393
@ -1248,7 +1248,7 @@
"defaultBrowserType": "chromium"
},
"Moto G4": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 360,
"height": 640
@ -1259,7 +1259,7 @@
"defaultBrowserType": "chromium"
},
"Moto G4 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Mobile Safari/537.36",
"viewport": {
"width": 640,
"height": 360
@ -1270,7 +1270,7 @@
"defaultBrowserType": "chromium"
},
"Desktop Chrome HiDPI": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
"screen": {
"width": 1792,
"height": 1120
@ -1285,7 +1285,7 @@
"defaultBrowserType": "chromium"
},
"Desktop Edge HiDPI": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36 Edg/114.0.5735.26",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36 Edg/114.0.5735.35",
"screen": {
"width": 1792,
"height": 1120
@ -1330,7 +1330,7 @@
"defaultBrowserType": "webkit"
},
"Desktop Chrome": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36",
"screen": {
"width": 1920,
"height": 1080
@ -1345,7 +1345,7 @@
"defaultBrowserType": "chromium"
},
"Desktop Edge": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.26 Safari/537.36 Edg/114.0.5735.26",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.35 Safari/537.36 Edg/114.0.5735.35",
"screen": {
"width": 1920,
"height": 1080

View file

@ -270,6 +270,7 @@ export const deps: any = {
'libgles2',
'libglib2.0-0',
'libgtk-3-0',
'libgudev-1.0-0',
'libharfbuzz-icu0',
'libharfbuzz0b',
'libhyphen0',
@ -343,6 +344,7 @@ export const deps: any = {
'libgthread-2.0.so.0': 'libglib2.0-0',
'libgtk-3.so.0': 'libgtk-3-0',
'libgtk-x11-2.0.so.0': 'libgtk2.0-0',
'libgudev-1.0.so.0': 'libgudev-1.0-0',
'libharfbuzz-icu.so.0': 'libharfbuzz-icu0',
'libharfbuzz.so.0': 'libharfbuzz0b',
'libhyphen.so.0': 'libhyphen0',

View file

@ -324,7 +324,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps
for (const entry of entries)
zipFile.addFile(entry.value, entry.name);
zipFile.end();
const zipFileName = state.traceFile + '.zip';
const zipFileName = state.traceFile.file + '.zip';
zipFile.outputStream.pipe(fs.createWriteStream(zipFileName)).on('close', () => {
const artifact = new Artifact(this._context, zipFileName);
artifact.reportFinished();

View file

@ -16435,7 +16435,7 @@ export interface ConsoleMessage {
/**
* The page that produced this console message, if any.
*/
page(): Page|null;
page(): null|Page;
/**
* The text of the console message.
@ -16637,7 +16637,7 @@ export interface Dialog {
/**
* The page that initiated this dialog, if available.
*/
page(): Page|null;
page(): null|Page;
/**
* Returns dialog's type, can be one of `alert`, `beforeunload`, `confirm` or `prompt`.

View file

@ -14,4 +14,4 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = require('@playwright/test/cli');
module.exports = require('@playwright/test/lib/cli');

View file

@ -1,6 +1,6 @@
{
"name": "@playwright/experimental-ct-core",
"version": "1.34.0-next",
"version": "1.34.3",
"description": "Playwright Component Testing Helpers",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
@ -18,7 +18,7 @@
},
"dependencies": {
"vite": "^4.3.3",
"@playwright/test": "1.34.0-next"
"@playwright/test": "1.34.3"
},
"bin": {
"playwright": "./cli.js"

View file

@ -1,6 +1,6 @@
{
"name": "@playwright/experimental-ct-react",
"version": "1.34.0-next",
"version": "1.34.3",
"description": "Playwright Component Testing for React",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
@ -26,7 +26,7 @@
}
},
"dependencies": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@vitejs/plugin-react": "^4.0.0"
},
"bin": {

View file

@ -1,6 +1,6 @@
{
"name": "@playwright/experimental-ct-react17",
"version": "1.34.0-next",
"version": "1.34.3",
"description": "Playwright Component Testing for React",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
@ -26,7 +26,7 @@
}
},
"dependencies": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@vitejs/plugin-react": "^4.0.0"
},
"bin": {

View file

@ -1,6 +1,6 @@
{
"name": "@playwright/experimental-ct-solid",
"version": "1.34.0-next",
"version": "1.34.3",
"description": "Playwright Component Testing for Solid",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
@ -26,7 +26,7 @@
}
},
"dependencies": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"vite-plugin-solid": "^2.7.0"
},
"devDependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@playwright/experimental-ct-svelte",
"version": "1.34.0-next",
"version": "1.34.3",
"description": "Playwright Component Testing for Svelte",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
@ -26,7 +26,7 @@
}
},
"dependencies": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@sveltejs/vite-plugin-svelte": "^2.1.1"
},
"devDependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "@playwright/experimental-ct-vue",
"version": "1.34.0-next",
"version": "1.34.3",
"description": "Playwright Component Testing for Vue",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
@ -26,7 +26,7 @@
}
},
"dependencies": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@vitejs/plugin-vue": "^4.2.1"
},
"bin": {

View file

@ -1,6 +1,6 @@
{
"name": "@playwright/experimental-ct-vue2",
"version": "1.34.0-next",
"version": "1.34.3",
"description": "Playwright Component Testing for Vue2",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
@ -26,7 +26,7 @@
}
},
"dependencies": {
"@playwright/experimental-ct-core": "1.34.0-next",
"@playwright/experimental-ct-core": "1.34.3",
"@vitejs/plugin-vue2": "^2.2.0"
},
"devDependencies": {

View file

@ -1,6 +1,6 @@
{
"name": "playwright-firefox",
"version": "1.34.0-next",
"version": "1.34.3",
"description": "A high-level API to automate Firefox",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
@ -27,6 +27,6 @@
"install": "node install.js"
},
"dependencies": {
"playwright-core": "1.34.0-next"
"playwright-core": "1.34.3"
}
}

View file

@ -14,4 +14,4 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module.exports = require('./lib/cli');
module.exports = require('@playwright/test/lib/cli');

View file

@ -28,7 +28,10 @@ function jsxs(type, props) {
};
}
const Fragment = {};
module.exports = {
Fragment,
jsx,
jsxs,
};

View file

@ -0,0 +1,21 @@
/**
* Copyright (c) Microsoft Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import jsxRuntime from './jsx-runtime.js';
export const jsx = jsxRuntime.jsx;
export const jsxs = jsxRuntime.jsxs;
export const Fragment = jsxRuntime.Fragment;

View file

@ -1,6 +1,6 @@
{
"name": "@playwright/test",
"version": "1.34.0-next",
"version": "1.34.3",
"description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
@ -23,7 +23,11 @@
"./lib/internalsForTest": "./lib/internalsForTest.js",
"./lib/experimentalLoader": "./lib/experimentalLoader.js",
"./lib/plugins": "./lib/plugins/index.js",
"./jsx-runtime": "./jsx-runtime.js",
"./jsx-runtime": {
"import": "./jsx-runtime.mjs",
"require": "./jsx-runtime.js",
"default": "./jsx-runtime.js"
},
"./lib/util": "./lib/util.js",
"./lib/utilsBundle": "./lib/utilsBundle.js",
"./reporter": "./reporter.js"
@ -37,7 +41,7 @@
"license": "Apache-2.0",
"dependencies": {
"@types/node": "*",
"playwright-core": "1.34.0-next"
"playwright-core": "1.34.3"
},
"optionalDependencies": {
"fsevents": "2.3.2"

View file

@ -558,7 +558,7 @@ class ArtifactsRecorder {
private _traceMode: TraceMode;
private _captureTrace = false;
private _screenshotOptions: { mode: ScreenshotMode } & Pick<playwrightLibrary.PageScreenshotOptions, 'fullPage' | 'omitBackground'> | undefined;
private _traceOptions: { screenshots: boolean, snapshots: boolean, sources: boolean, mode?: TraceMode };
private _traceOptions: { screenshots: boolean, snapshots: boolean, sources: boolean, attachments: boolean, mode?: TraceMode };
private _temporaryTraceFiles: string[] = [];
private _temporaryScreenshots: string[] = [];
private _reusedContexts = new Set<BrowserContext>();
@ -572,7 +572,7 @@ class ArtifactsRecorder {
this._screenshotMode = normalizeScreenshotMode(screenshot);
this._screenshotOptions = typeof screenshot === 'string' ? undefined : screenshot;
this._traceMode = normalizeTraceMode(trace);
const defaultTraceOptions = { screenshots: true, snapshots: true, sources: true };
const defaultTraceOptions = { screenshots: true, snapshots: true, sources: true, attachments: true };
this._traceOptions = typeof trace === 'string' ? defaultTraceOptions : { ...defaultTraceOptions, ...trace, mode: undefined };
this._screenshottedSymbol = Symbol('screenshotted');
this._startedCollectingArtifacts = Symbol('startedCollectingArtifacts');
@ -661,6 +661,12 @@ class ArtifactsRecorder {
if (this._preserveTrace()) {
const events = this._testInfo._traceEvents;
if (events.length) {
if (!this._traceOptions.attachments) {
for (const event of events) {
if (event.type === 'after')
delete event.attachments;
}
}
const tracePath = path.join(this._artifactsDir, createGuid() + '.zip');
this._temporaryTraceFiles.push(tracePath);
await saveTraceFile(tracePath, events, this._traceOptions.sources);

View file

@ -332,7 +332,7 @@ export class TestInfoImpl implements TestInfo {
async attach(name: string, options: { path?: string, body?: string | Buffer, contentType?: string } = {}) {
const step = this._addStep({
title: `attach "${name}"`,
title: `attach "${name}"`,
category: 'attach',
wallTime: Date.now(),
});

View file

@ -3554,7 +3554,7 @@ export interface PlaywrightWorkerOptions {
*
* Learn more about [recording trace](https://playwright.dev/docs/test-configuration#record-test-trace).
*/
trace: TraceMode | /** deprecated */ 'retry-with-trace' | { mode: TraceMode, snapshots?: boolean, screenshots?: boolean, sources?: boolean };
trace: TraceMode | /** deprecated */ 'retry-with-trace' | { mode: TraceMode, snapshots?: boolean, screenshots?: boolean, sources?: boolean, attachments?: boolean };
/**
* Whether to record video for each test. Defaults to `'off'`.
* - `'off'`: Do not record video.

View file

@ -1,6 +1,6 @@
{
"name": "playwright-webkit",
"version": "1.34.0-next",
"version": "1.34.3",
"description": "A high-level API to automate WebKit",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
@ -27,6 +27,6 @@
"install": "node install.js"
},
"dependencies": {
"playwright-core": "1.34.0-next"
"playwright-core": "1.34.3"
}
}

View file

@ -1,6 +1,6 @@
{
"name": "playwright",
"version": "1.34.0-next",
"version": "1.34.3",
"description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
@ -28,6 +28,6 @@
"install": "node install.js"
},
"dependencies": {
"playwright-core": "1.34.0-next"
"playwright-core": "1.34.3"
}
}

View file

@ -76,13 +76,6 @@ export class TraceModel {
unzipProgress(++done, total);
contextEntry.actions = [...actionMap.values()].sort((a1, a2) => a1.startTime - a2.startTime);
if (!backend.isLive()) {
for (const action of contextEntry.actions) {
if (!action.endTime && !action.error)
action.error = { name: 'Error', message: 'Timed out' };
}
}
const stacks = await this._backend.readText(ordinal + '.stacks');
if (stacks) {
const callMetadata = parseClientSideCallMetadata(JSON.parse(stacks));

View file

@ -31,6 +31,7 @@ export interface ActionListProps {
onSelected: (action: ActionTraceEvent) => void,
onHighlighted: (action: ActionTraceEvent | undefined) => void,
revealConsole: () => void,
isLive?: boolean,
}
type ActionTreeItem = {
@ -49,6 +50,7 @@ export const ActionList: React.FC<ActionListProps> = ({
onSelected,
onHighlighted,
revealConsole,
isLive,
}) => {
const [treeState, setTreeState] = React.useState<TreeState>({ expandedItems: new Map() });
const { rootItem, itemMap } = React.useMemo(() => {
@ -86,14 +88,15 @@ export const ActionList: React.FC<ActionListProps> = ({
onSelected={item => onSelected(item.action!)}
onHighlighted={item => onHighlighted(item?.action)}
isError={item => !!item.action?.error?.message}
render={item => renderAction(item.action!, sdkLanguage, revealConsole)}
render={item => renderAction(item.action!, sdkLanguage, revealConsole, isLive || false)}
/>;
};
const renderAction = (
action: ActionTraceEvent,
sdkLanguage: Language | undefined,
revealConsole: () => void
revealConsole: () => void,
isLive: boolean,
) => {
const { errors, warnings } = modelUtil.stats(action);
const locator = action.params.selector ? asLocator(sdkLanguage || 'javascript', action.params.selector, false /* isFrameLocator */, true /* playSafe */) : undefined;
@ -103,6 +106,8 @@ const renderAction = (
time = msToString(action.endTime - action.startTime);
else if (action.error)
time = 'Timed out';
else if (!isLive)
time = '-';
return <>
<div className='action-title'>
<span>{action.apiName}</span>

View file

@ -492,7 +492,7 @@ const TraceView: React.FC<{
item: { testFile?: SourceLocation, testCase?: TestCase },
rootDir?: string,
}> = ({ item, rootDir }) => {
const [model, setModel] = React.useState<MultiTraceModel | undefined>();
const [model, setModel] = React.useState<{ model: MultiTraceModel, isLive: boolean } | undefined>();
const [counter, setCounter] = React.useState(0);
const pollTimer = React.useRef<NodeJS.Timeout | null>(null);
@ -505,7 +505,7 @@ const TraceView: React.FC<{
// This avoids auto-selection of the last action every time we reload the model.
const [selectedActionId, setSelectedActionId] = React.useState<string | undefined>();
const onSelectionChanged = React.useCallback((action: ActionTraceEvent) => setSelectedActionId(idForAction(action)), [setSelectedActionId]);
const initialSelection = selectedActionId ? model?.actions.find(a => idForAction(a) === selectedActionId) : undefined;
const initialSelection = selectedActionId ? model?.model.actions.find(a => idForAction(a) === selectedActionId) : undefined;
React.useEffect(() => {
if (pollTimer.current)
@ -520,7 +520,7 @@ const TraceView: React.FC<{
// Test finished.
const attachment = result && result.duration >= 0 && result.attachments.find(a => a.name === 'trace');
if (attachment && attachment.path) {
loadSingleTraceFile(attachment.path).then(model => setModel(model));
loadSingleTraceFile(attachment.path).then(model => setModel({ model, isLive: false }));
return;
}
@ -534,7 +534,7 @@ const TraceView: React.FC<{
pollTimer.current = setTimeout(async () => {
try {
const model = await loadSingleTraceFile(traceLocation);
setModel(model);
setModel({ model, isLive: true });
} catch {
setModel(undefined);
} finally {
@ -549,14 +549,15 @@ const TraceView: React.FC<{
return <Workbench
key='workbench'
model={model}
model={model?.model}
hideTimelineBars={true}
hideStackFrames={true}
showSourcesFirst={true}
rootDir={rootDir}
initialSelection={initialSelection}
onSelectionChanged={onSelectionChanged}
fallbackLocation={item.testFile} />;
fallbackLocation={item.testFile}
isLive={model?.isLive} />;
};
declare global {

View file

@ -41,7 +41,8 @@ export const Workbench: React.FunctionComponent<{
fallbackLocation?: modelUtil.SourceLocation,
initialSelection?: ActionTraceEvent,
onSelectionChanged?: (action: ActionTraceEvent) => void,
}> = ({ model, hideTimelineBars, hideStackFrames, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged }) => {
isLive?: boolean,
}> = ({ model, hideTimelineBars, hideStackFrames, showSourcesFirst, rootDir, fallbackLocation, initialSelection, onSelectionChanged, isLive }) => {
const [selectedAction, setSelectedAction] = React.useState<ActionTraceEvent | undefined>(undefined);
const [highlightedAction, setHighlightedAction] = React.useState<ActionTraceEvent | undefined>();
const [selectedNavigatorTab, setSelectedNavigatorTab] = React.useState<string>('actions');
@ -142,6 +143,7 @@ export const Workbench: React.FunctionComponent<{
onSelected={onActionSelected}
onHighlighted={setHighlightedAction}
revealConsole={() => setSelectedPropertiesTab('console')}
isLive={isLive}
/>
},
{

View file

@ -578,7 +578,6 @@ test('should record global request trace', async ({ request, context, server },
});
test('should store global request traces separately', async ({ request, server, playwright, browserName, mode }, testInfo) => {
test.fixme(browserName === 'chromium' && mode === 'driver', 'https://github.com/microsoft/playwright/issues/23108');
const request2 = await playwright.request.newContext();
await Promise.all([
(request as any)._tracing.start({ snapshots: true }),

View file

@ -488,6 +488,34 @@ test('should load a jsx/tsx files', async ({ runInlineTest }) => {
expect(exitCode).toBe(0);
});
test('should load a jsx/tsx files in ESM mode', async ({ runInlineTest }) => {
const { exitCode, passed } = await runInlineTest({
'package.json': JSON.stringify({
type: 'module'
}),
'playwright.config.ts': `
import { defineConfig } from '@playwright/test';
export default defineConfig({ projects: [{name: 'foo'}] });
`,
'a.spec.tsx': `
import { test, expect } from '@playwright/test';
const component = () => <div></div>;
test('succeeds', () => {
expect(1 + 1).toBe(2);
});
`,
'b.spec.jsx': `
import { test, expect } from '@playwright/test';
const component = () => <div></div>;
test('succeeds', () => {
expect(1 + 1).toBe(2);
});
`
});
expect(passed).toBe(2);
expect(exitCode).toBe(0);
});
test('should load jsx with top-level component', async ({ runInlineTest }) => {
const { exitCode, passed } = await runInlineTest({
'a.spec.tsx': `

View file

@ -526,3 +526,59 @@ test(`trace:retain-on-failure should create trace if request context is disposed
expect(trace.apiNames).toContain('apiRequestContext.get');
expect(result.failed).toBe(1);
});
test('should include attachments by default', async ({ runInlineTest, server }, testInfo) => {
const result = await runInlineTest({
'playwright.config.ts': `
module.exports = { use: { trace: 'on' } };
`,
'a.spec.ts': `
import { test, expect } from '@playwright/test';
test('pass', async ({}, testInfo) => {
testInfo.attach('foo', { body: 'bar' });
});
`,
}, { workers: 1 });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
const trace = await parseTrace(testInfo.outputPath('test-results', 'a-pass', 'trace.zip'));
expect(trace.apiNames).toEqual([
'Before Hooks',
`attach "foo"`,
'After Hooks',
]);
expect(trace.actions[1].attachments).toEqual([{
name: 'foo',
contentType: 'text/plain',
sha1: expect.any(String),
}]);
expect([...trace.resources.keys()].filter(f => f.startsWith('resources/'))).toHaveLength(1);
});
test('should opt out of attachments', async ({ runInlineTest, server }, testInfo) => {
const result = await runInlineTest({
'playwright.config.ts': `
module.exports = { use: { trace: { mode: 'on', attachments: false } } };
`,
'a.spec.ts': `
import { test, expect } from '@playwright/test';
test('pass', async ({}, testInfo) => {
testInfo.attach('foo', { body: 'bar' });
});
`,
}, { workers: 1 });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(1);
const trace = await parseTrace(testInfo.outputPath('test-results', 'a-pass', 'trace.zip'));
expect(trace.apiNames).toEqual([
'Before Hooks',
`attach "foo"`,
'After Hooks',
]);
expect(trace.actions[1].attachments).toEqual(undefined);
expect([...trace.resources.keys()].filter(f => f.startsWith('resources/'))).toHaveLength(0);
});

View file

@ -1348,7 +1348,7 @@ test.describe('labels', () => {
const result = await runInlineTest({
'a.test.js': `
const { expect, test } = require('@playwright/test');
const tags = ['@smoke:p1', '@issue[123]', '@issue#123', '@$$$', '@tl/dr'];
const tags = ['@smoke-p1', '@issue[123]', '@issue#123', '@$$$', '@tl/dr'];
test.describe('Error Pages', () => {
tags.forEach(tag => {
@ -1364,7 +1364,7 @@ test.describe('labels', () => {
expect(result.passed).toBe(5);
await showReport();
const tags = ['smoke:p1', 'issue[123]', 'issue#123', '$$$', 'tl/dr'];
const tags = ['smoke-p1', 'issue[123]', 'issue#123', '$$$', 'tl/dr'];
const searchInput = page.locator('.subnav-search-input');
for (const tag of tags) {
@ -1881,3 +1881,61 @@ test('should list tests in the right order', async ({ runInlineTest, showReport,
/main second passes\d+m?ssecond.ts:5/,
]);
});
test('tests should filter by file', async ({ runInlineTest, showReport, page }) => {
const result = await runInlineTest({
'file-a.test.js': `
const { test } = require('@playwright/test');
test('a test 1', async ({}) => {});
test('a test 2', async ({}) => {});
`,
'file-b.test.js': `
const { test } = require('@playwright/test');
test('b test 1', async ({}) => {});
test('b test 2', async ({}) => {});
`,
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
expect(result.exitCode).toBe(0);
expect(result.passed).toBe(4);
expect(result.failed).toBe(0);
await showReport();
const searchInput = page.locator('.subnav-search-input');
await searchInput.fill('file-a');
await expect(page.getByText('file-a.test.js', { exact: true })).toBeVisible();
await expect(page.getByText('a test 1')).toBeVisible();
await expect(page.getByText('a test 2')).toBeVisible();
await expect(page.getByText('file-b.test.js', { exact: true })).not.toBeVisible();
await expect(page.getByText('b test 1')).not.toBeVisible();
await expect(page.getByText('b test 2')).not.toBeVisible();
await searchInput.fill('file-a:3');
await expect(page.getByText('a test 1')).toBeVisible();
await expect(page.getByText('a test 2')).not.toBeVisible();
});
test('tests should filter by status', async ({ runInlineTest, showReport, page }) => {
const result = await runInlineTest({
'a.test.js': `
const { test, expect } = require('@playwright/test');
test('failed title', async ({}) => { expect(1).toBe(1); });
test('passes title', async ({}) => { expect(1).toBe(2); });
`,
}, { reporter: 'dot,html' }, { PW_TEST_HTML_REPORT_OPEN: 'never' });
expect(result.exitCode).toBe(1);
expect(result.passed).toBe(1);
expect(result.failed).toBe(1);
await showReport();
const searchInput = page.locator('.subnav-search-input');
await searchInput.fill('s:failed');
await expect(page.getByText('a.test.js', { exact: true })).toBeVisible();
await expect(page.getByText('failed title')).not.toBeVisible();
await expect(page.getByText('passes title')).toBeVisible();
});

View file

@ -98,9 +98,12 @@ test('should merge screenshot assertions', async ({ runUITest }, testInfo) => {
'action list'
).toHaveText([
/Before Hooks[\d.]+m?s/,
/page\.setContent[\d.]+m?s/,
/expect\.toHaveScreenshot[\d.]+m?s/,
/After Hooks/,
/page.setContent[\d.]+m?s/,
/expect.toHaveScreenshot[\d.]+m?s/,
/After Hooks-/,
/fixture: page[\d.]+m?s/,
/fixture: context[\d.]+m?s/,
/fixture: browser[\d.]+m?s/,
]);
});

View file

@ -37,7 +37,7 @@ RUN mkdir /ms-playwright && \
mkdir /ms-playwright-agent && \
cd /ms-playwright-agent && npm init -y && \
npm i /tmp/playwright-core.tar.gz && \
npx playwright mark-docker-image "${DOCKER_IMAGE_NAME_TEMPLATE}" && \
npx playwright install --with-deps && rm -rf /var/lib/apt/lists/* && \
npm exec --no -- playwright-core mark-docker-image "${DOCKER_IMAGE_NAME_TEMPLATE}" && \
npm exec --no -- playwright-core install --with-deps && rm -rf /var/lib/apt/lists/* && \
rm /tmp/playwright-core.tar.gz && \
chmod -R 777 /ms-playwright

View file

@ -35,8 +35,8 @@ RUN mkdir /ms-playwright && \
mkdir /ms-playwright-agent && \
cd /ms-playwright-agent && npm init -y && \
npm i /tmp/playwright-core.tar.gz && \
npx playwright mark-docker-image "${DOCKER_IMAGE_NAME_TEMPLATE}" && \
npx playwright install --with-deps && rm -rf /var/lib/apt/lists/* && \
npm exec --no -- playwright-core mark-docker-image "${DOCKER_IMAGE_NAME_TEMPLATE}" && \
npm exec --no -- playwright-core install --with-deps && rm -rf /var/lib/apt/lists/* && \
rm /tmp/playwright-core.tar.gz && \
rm -rf /ms-playwright-agent && \
chmod -R 777 /ms-playwright

View file

@ -206,7 +206,7 @@ export interface PlaywrightWorkerOptions {
launchOptions: LaunchOptions;
connectOptions: ConnectOptions | undefined;
screenshot: ScreenshotMode | { mode: ScreenshotMode } & Pick<PageScreenshotOptions, 'fullPage' | 'omitBackground'>;
trace: TraceMode | /** deprecated */ 'retry-with-trace' | { mode: TraceMode, snapshots?: boolean, screenshots?: boolean, sources?: boolean };
trace: TraceMode | /** deprecated */ 'retry-with-trace' | { mode: TraceMode, snapshots?: boolean, screenshots?: boolean, sources?: boolean, attachments?: boolean };
video: VideoMode | /** deprecated */ 'retry-with-video' | { mode: VideoMode, size?: ViewportSize };
}

View file

@ -12,7 +12,7 @@ apt-get update && apt-get install -y curl && \
apt-get update && apt-get install -y apt-file && apt-file update
# Install tip-of-tree playwright-core and browsers
mkdir /root/tmp && cd /root/tmp && npm init -y && npm i /root/hostfolder/playwright-core.tar.gz && npx playwright install
mkdir /root/tmp && cd /root/tmp && npm init -y && npm i /root/hostfolder/playwright-core.tar.gz && npx playwright-core install
cp /root/hostfolder/inside_docker/list_dependencies.js /root/tmp/list_dependencies.js