Compare commits

...

7 commits

Author SHA1 Message Date
Andrey Lushnikov 2c9e02a800
chore: mark 1.37.0 (#26410) 2023-08-10 15:35:25 -07:00
Andrey Lushnikov 37ba0b657d cherry-pick(#26411): docs: add release notes for js v1.37 2023-08-10 23:49:35 +02:00
Yury Semikhatsky 9a5172e6c1
cherry-pick(#26413): fix(merge): allow reports with same name as input (#26417) 2023-08-10 14:08:01 -07:00
Playwright Service eed73de573
cherry-pick(#26400): feat(chromium): roll to r1076 (#26403)
This PR cherry-picks the following commits:

- 167165d179

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2023-08-10 16:18:39 +02:00
Pavel Feldman 7999841a19 cherry-pick(#26390): chore: allow merging defineConfig 2023-08-09 17:25:13 -07:00
Yury Semikhatsky 56b22fa0b1
cherry-pick(#26380): devops(merge): authorize service principal by us… (#26384)
…ing a client secret

Pass all credentials via environment variable as described on [this
page](https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-authorize-azure-active-directory#authorize-a-service-principal-by-using-a-client-secret-1)
instead of using `azcopy login`.
2023-08-09 15:31:08 -07:00
Yury Semikhatsky 421bffb7d7
cherry-pick(#26382): docs(merge): use azcopy for uploads (#26383) 2023-08-09 12:17:57 -07:00
29 changed files with 387 additions and 131 deletions

View file

@ -36,11 +36,13 @@ jobs:
- name: Upload HTML report to Azure
run: |
REPORT_DIR='run-${{ github.event.workflow_run.id }}-${{ github.event.workflow_run.run_attempt }}-${{ github.sha }}'
azcopy login --service-principal --application-id ${{ secrets.AZURE_CLIENT_ID }} --tenant-id ${{ secrets.AZURE_TENANT_ID }}
azcopy cp --recursive "./playwright-report/*" "https://mspwblobreport.blob.core.windows.net/\$web/$REPORT_DIR"
echo "Report url: https://mspwblobreport.z1.web.core.windows.net/$REPORT_DIR/index.html"
env:
AZCOPY_SPA_CLIENT_SECRET: '${{ secrets.AZURE_CLIENT_SECRET }}'
AZCOPY_AUTO_LOGIN_TYPE: SPN
AZCOPY_SPA_APPLICATION_ID: '${{ secrets.AZCOPY_SPA_APPLICATION_ID }}'
AZCOPY_SPA_CLIENT_SECRET: '${{ secrets.AZCOPY_SPA_CLIENT_SECRET }}'
AZCOPY_TENANT_ID: '${{ secrets.AZCOPY_TENANT_ID }}'
- name: Read pull request number
uses: ./.github/actions/download-artifact

View file

@ -60,8 +60,10 @@ jobs:
- name: Upload HTML report to Azure
run: |
REPORT_DIR='run-service-${{ github.run_id }}-${{ github.run_attempt }}-${{ github.sha }}'
azcopy login --service-principal --application-id ${{ secrets.AZURE_CLIENT_ID }} --tenant-id ${{ secrets.AZURE_TENANT_ID }}
azcopy cp --recursive "./playwright-report/*" "https://mspwblobreport.blob.core.windows.net/\$web/$REPORT_DIR"
echo "Report url: https://mspwblobreport.z1.web.core.windows.net/$REPORT_DIR/index.html#?q=s:failed"
env:
AZCOPY_SPA_CLIENT_SECRET: '${{ secrets.AZURE_CLIENT_SECRET }}'
AZCOPY_AUTO_LOGIN_TYPE: SPN
AZCOPY_SPA_APPLICATION_ID: '${{ secrets.AZCOPY_SPA_APPLICATION_ID }}'
AZCOPY_SPA_CLIENT_SECRET: '${{ secrets.AZCOPY_SPA_CLIENT_SECRET }}'
AZCOPY_TENANT_ID: '${{ secrets.AZCOPY_TENANT_ID }}'

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-116.0.5845.62-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-115.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-17.0-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-116.0.5845.82-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-115.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-17.0-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 -->116.0.5845.62<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Chromium <!-- GEN:chromium-version -->116.0.5845.82<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->17.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Firefox <!-- GEN:firefox-version -->115.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |

View file

@ -6,6 +6,64 @@ toc_max_heading_level: 2
import LiteYouTube from '@site/src/components/LiteYouTube';
## Version 1.37
### New `npx playwright merge-reports` tool
If you run tests on multiple shards, you can now merge all reports in a single HTML report (or any other report)
using the new `merge-reports` CLI tool.
Using `merge-reports` tool requires the following steps:
1. Adding a new "blob" reporter to the config when running on CI:
```js title="playwright.config.ts"
export default defineConfig({
testDir: './tests',
reporter: process.env.CI ? 'blob' : 'html',
});
```
The "blob" reporter will produce ".zip" files that contain all the information
about the test run.
2. Copying all "blob" reports in a single shared location and running `npx playwright merge-reports`:
```bash
npx playwright merge-reports --reporter html ./all-blob-reports
```
Read more in [our documentation](./test-sharding.md).
### 📚 Debian 12 Bookworm Support
Playwright now supports Debian 12 Bookworm on both x86_64 and arm64 for Chromium, Firefox and WebKit.
Let us know if you encounter any issues!
Linux support looks like this:
| | Ubuntu 20.04 | Ubuntu 22.04 | Debian 11 | Debian 12
| :--- | :---: | :---: | :---: | :---: | :---: |
| Chromium | ✅ | ✅ | ✅ | ✅ |
| WebKit | ✅ | ✅ | ✅ | ✅ |
| Firefox | ✅ | ✅ | ✅ | ✅ |
### UI Mode Updates
- UI Mode now respects project dependencies. You can control which dependencies to respect by checking/unchecking them in a projects list.
- Console logs from the test are now displayed in the Console tab.
### Browser Versions
* Chromium 116.0.5845.82
* Mozilla Firefox 115.0
* WebKit 17.0
This version was also tested against the following stable channels:
* Google Chrome 115
* Microsoft Edge 115
## Version 1.36
🏝️ Summer maintenance release.

View file

@ -126,7 +126,18 @@ We can utilize Azure Storage's static websites hosting capabilities to easily an
1. Create an [Azure Storage account](https://learn.microsoft.com/en-us/azure/storage/common/storage-account-create).
1. Enable [Static website hosting](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website-how-to#enable-static-website-hosting) for the storage account.
1. Add the Azure connection string as a [GitHub Actions secret](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository) called `AZURE_CONNECTION_STRING`.
1. Create a Service Principal in Azure and grant it access to Azure Blob storage. Upon successful execution, the command will display the credentials which will be used in the next step.
```bash
az ad sp create-for-rbac --name "github-actions" --role "Storage Blob Data Contributor" --scopes /subscriptions/<SUBSCRIPTION_ID>/resourceGroups/<RESOURCE_GROUP_NAME>/providers/Microsoft.Storage/storageAccounts/<STORAGE_ACCOUNT_NAME>
```
1. Use the credentials from the previous step to set up encrypted secrets in your GitHub repository. Go to your repository's settings, under [GitHub Actions secrets](https://docs.github.com/en/actions/security-guides/encrypted-secrets#creating-encrypted-secrets-for-a-repository), and add the following secrets:
- `AZCOPY_SPA_APPLICATION_ID`
- `AZCOPY_SPA_CLIENT_SECRET`
- `AZCOPY_TENANT_ID`
For a detailed guide on how to authorize a service principal using a client secret, refer to [this Microsoft documentation](https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-authorize-azure-active-directory#authorize-a-service-principal-by-using-a-client-secret-1).
1. Add a step that uploads HTML report to Azure Storage.
```yaml
@ -135,7 +146,12 @@ We can utilize Azure Storage's static websites hosting capabilities to easily an
shell: bash
run: |
REPORT_DIR='run-${{ github.run_id }}-${{ github.run_attempt }}'
az storage blob upload-batch -s playwright-report -d "\$web/$REPORT_DIR" --connection-string "${{ secrets.AZURE_CONNECTION_STRING }}"
azcopy cp --recursive "./playwright-report/*" "https://<STORAGE_ACCOUNT_NAME>.blob.core.windows.net/\$web/$REPORT_DIR"
env:
AZCOPY_AUTO_LOGIN_TYPE: SPN
AZCOPY_SPA_APPLICATION_ID: '${{ secrets.AZCOPY_SPA_APPLICATION_ID }}'
AZCOPY_SPA_CLIENT_SECRET: '${{ secrets.AZCOPY_SPA_CLIENT_SECRET }}'
AZCOPY_TENANT_ID: '${{ secrets.AZCOPY_TENANT_ID }}'
```
The contents of `$web` storage container can be accessed from a browser by using the [public URL](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blob-static-website-how-to?tabs=azure-portal#portal-find-url) of the website.

82
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "playwright-internal",
"version": "1.37.0-next",
"version": "1.37.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "playwright-internal",
"version": "1.37.0-next",
"version": "1.37.0",
"license": "Apache-2.0",
"workspaces": [
"packages/*"
@ -6325,11 +6325,11 @@
}
},
"packages/playwright": {
"version": "1.37.0-next",
"version": "1.37.0",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.37.0-next"
"playwright-core": "1.37.0"
},
"bin": {
"playwright": "cli.js"
@ -6339,11 +6339,11 @@
}
},
"packages/playwright-chromium": {
"version": "1.37.0-next",
"version": "1.37.0",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.37.0-next"
"playwright-core": "1.37.0"
},
"bin": {
"playwright": "cli.js"
@ -6353,7 +6353,7 @@
}
},
"packages/playwright-core": {
"version": "1.37.0-next",
"version": "1.37.0",
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
@ -6364,11 +6364,11 @@
},
"packages/playwright-ct-core": {
"name": "@playwright/experimental-ct-core",
"version": "1.37.0-next",
"version": "1.37.0",
"license": "Apache-2.0",
"dependencies": {
"@playwright/test": "1.37.0-next",
"playwright-core": "1.37.0-next",
"@playwright/test": "1.37.0",
"playwright-core": "1.37.0",
"vite": "^4.3.9"
},
"bin": {
@ -6380,10 +6380,10 @@
},
"packages/playwright-ct-react": {
"name": "@playwright/experimental-ct-react",
"version": "1.37.0-next",
"version": "1.37.0",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.37.0-next",
"@playwright/experimental-ct-core": "1.37.0",
"@vitejs/plugin-react": "^4.0.0"
},
"bin": {
@ -6412,10 +6412,10 @@
},
"packages/playwright-ct-react17": {
"name": "@playwright/experimental-ct-react17",
"version": "1.37.0-next",
"version": "1.37.0",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.37.0-next",
"@playwright/experimental-ct-core": "1.37.0",
"@vitejs/plugin-react": "^4.0.0"
},
"bin": {
@ -6444,10 +6444,10 @@
},
"packages/playwright-ct-solid": {
"name": "@playwright/experimental-ct-solid",
"version": "1.37.0-next",
"version": "1.37.0",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.37.0-next",
"@playwright/experimental-ct-core": "1.37.0",
"vite-plugin-solid": "^2.7.0"
},
"bin": {
@ -6462,10 +6462,10 @@
},
"packages/playwright-ct-svelte": {
"name": "@playwright/experimental-ct-svelte",
"version": "1.37.0-next",
"version": "1.37.0",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.37.0-next",
"@playwright/experimental-ct-core": "1.37.0",
"@sveltejs/vite-plugin-svelte": "^2.1.1"
},
"bin": {
@ -6480,10 +6480,10 @@
},
"packages/playwright-ct-vue": {
"name": "@playwright/experimental-ct-vue",
"version": "1.37.0-next",
"version": "1.37.0",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.37.0-next",
"@playwright/experimental-ct-core": "1.37.0",
"@vitejs/plugin-vue": "^4.2.1"
},
"bin": {
@ -6531,10 +6531,10 @@
},
"packages/playwright-ct-vue2": {
"name": "@playwright/experimental-ct-vue2",
"version": "1.37.0-next",
"version": "1.37.0",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.37.0-next",
"@playwright/experimental-ct-core": "1.37.0",
"@vitejs/plugin-vue2": "^2.2.0"
},
"bin": {
@ -6548,11 +6548,11 @@
}
},
"packages/playwright-firefox": {
"version": "1.37.0-next",
"version": "1.37.0",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.37.0-next"
"playwright-core": "1.37.0"
},
"bin": {
"playwright": "cli.js"
@ -6604,11 +6604,11 @@
},
"packages/playwright-test": {
"name": "@playwright/test",
"version": "1.37.0-next",
"version": "1.37.0",
"license": "Apache-2.0",
"dependencies": {
"@types/node": "*",
"playwright-core": "1.37.0-next"
"playwright-core": "1.37.0"
},
"bin": {
"playwright": "cli.js"
@ -6621,11 +6621,11 @@
}
},
"packages/playwright-webkit": {
"version": "1.37.0-next",
"version": "1.37.0",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.37.0-next"
"playwright-core": "1.37.0"
},
"bin": {
"playwright": "cli.js"
@ -7471,15 +7471,15 @@
"@playwright/experimental-ct-core": {
"version": "file:packages/playwright-ct-core",
"requires": {
"@playwright/test": "1.37.0-next",
"playwright-core": "1.37.0-next",
"@playwright/test": "1.37.0",
"playwright-core": "1.37.0",
"vite": "^4.3.9"
}
},
"@playwright/experimental-ct-react": {
"version": "file:packages/playwright-ct-react",
"requires": {
"@playwright/experimental-ct-core": "1.37.0-next",
"@playwright/experimental-ct-core": "1.37.0",
"@vitejs/plugin-react": "^4.0.0"
},
"dependencies": {
@ -7499,7 +7499,7 @@
"@playwright/experimental-ct-react17": {
"version": "file:packages/playwright-ct-react17",
"requires": {
"@playwright/experimental-ct-core": "1.37.0-next",
"@playwright/experimental-ct-core": "1.37.0",
"@vitejs/plugin-react": "^4.0.0"
},
"dependencies": {
@ -7519,7 +7519,7 @@
"@playwright/experimental-ct-solid": {
"version": "file:packages/playwright-ct-solid",
"requires": {
"@playwright/experimental-ct-core": "1.37.0-next",
"@playwright/experimental-ct-core": "1.37.0",
"solid-js": "^1.7.0",
"vite-plugin-solid": "^2.7.0"
}
@ -7527,7 +7527,7 @@
"@playwright/experimental-ct-svelte": {
"version": "file:packages/playwright-ct-svelte",
"requires": {
"@playwright/experimental-ct-core": "1.37.0-next",
"@playwright/experimental-ct-core": "1.37.0",
"@sveltejs/vite-plugin-svelte": "^2.1.1",
"svelte": "^3.55.1"
}
@ -7535,7 +7535,7 @@
"@playwright/experimental-ct-vue": {
"version": "file:packages/playwright-ct-vue",
"requires": {
"@playwright/experimental-ct-core": "1.37.0-next",
"@playwright/experimental-ct-core": "1.37.0",
"@vitejs/plugin-vue": "^4.2.1"
},
"dependencies": {
@ -7569,7 +7569,7 @@
"@playwright/experimental-ct-vue2": {
"version": "file:packages/playwright-ct-vue2",
"requires": {
"@playwright/experimental-ct-core": "1.37.0-next",
"@playwright/experimental-ct-core": "1.37.0",
"@vitejs/plugin-vue2": "^2.2.0",
"vue": "^2.7.14"
}
@ -7603,7 +7603,7 @@
"requires": {
"@types/node": "*",
"fsevents": "2.3.2",
"playwright-core": "1.37.0-next"
"playwright-core": "1.37.0"
}
},
"@sindresorhus/is": {
@ -9942,13 +9942,13 @@
"playwright": {
"version": "file:packages/playwright",
"requires": {
"playwright-core": "1.37.0-next"
"playwright-core": "1.37.0"
}
},
"playwright-chromium": {
"version": "file:packages/playwright-chromium",
"requires": {
"playwright-core": "1.37.0-next"
"playwright-core": "1.37.0"
}
},
"playwright-core": {
@ -9957,13 +9957,13 @@
"playwright-firefox": {
"version": "file:packages/playwright-firefox",
"requires": {
"playwright-core": "1.37.0-next"
"playwright-core": "1.37.0"
}
},
"playwright-webkit": {
"version": "file:packages/playwright-webkit",
"requires": {
"playwright-core": "1.37.0-next"
"playwright-core": "1.37.0"
}
},
"postcss": {

View file

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

View file

@ -1,6 +1,6 @@
{
"name": "playwright-chromium",
"version": "1.37.0-next",
"version": "1.37.0",
"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.37.0-next"
"playwright-core": "1.37.0"
}
}

View file

@ -3,15 +3,15 @@
"browsers": [
{
"name": "chromium",
"revision": "1075",
"revision": "1076",
"installByDefault": true,
"browserVersion": "116.0.5845.62"
"browserVersion": "116.0.5845.82"
},
{
"name": "chromium-with-symbols",
"revision": "1075",
"revision": "1076",
"installByDefault": false,
"browserVersion": "116.0.5845.62"
"browserVersion": "116.0.5845.82"
},
{
"name": "chromium-tip-of-tree",

View file

@ -1,6 +1,6 @@
{
"name": "playwright-core",
"version": "1.37.0-next",
"version": "1.37.0",
"description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",

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/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 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/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 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/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 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/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 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/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 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/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 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/116.0.5845.62 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 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/116.0.5845.62 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Safari/537.36",
"viewport": {
"width": 1138,
"height": 712
@ -858,7 +858,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 384,
"height": 640
@ -869,7 +869,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 640,
"height": 384
@ -880,7 +880,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36 Edge/14.14263",
"viewport": {
"width": 640,
"height": 360
@ -891,7 +891,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36 Edge/14.14263",
"viewport": {
"width": 360,
"height": 640
@ -902,7 +902,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36 Edge/14.14263",
"viewport": {
"width": 360,
"height": 640
@ -913,7 +913,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36 Edge/14.14263",
"viewport": {
"width": 640,
"height": 360
@ -924,7 +924,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/116.0.5845.62 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Safari/537.36",
"viewport": {
"width": 800,
"height": 1280
@ -935,7 +935,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/116.0.5845.62 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Safari/537.36",
"viewport": {
"width": 1280,
"height": 800
@ -946,7 +946,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/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 384,
"height": 640
@ -957,7 +957,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/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 640,
"height": 384
@ -968,7 +968,7 @@
"defaultBrowserType": "chromium"
},
"Nexus 5": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 360,
"height": 640
@ -979,7 +979,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/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 640,
"height": 360
@ -990,7 +990,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 412,
"height": 732
@ -1001,7 +1001,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 732,
"height": 412
@ -1012,7 +1012,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/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 412,
"height": 732
@ -1023,7 +1023,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/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 732,
"height": 412
@ -1034,7 +1034,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 412,
"height": 732
@ -1045,7 +1045,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 732,
"height": 412
@ -1056,7 +1056,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/116.0.5845.62 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Safari/537.36",
"viewport": {
"width": 600,
"height": 960
@ -1067,7 +1067,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/116.0.5845.62 Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Safari/537.36",
"viewport": {
"width": 960,
"height": 600
@ -1122,7 +1122,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 411,
"height": 731
@ -1133,7 +1133,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 731,
"height": 411
@ -1144,7 +1144,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 411,
"height": 823
@ -1155,7 +1155,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 823,
"height": 411
@ -1166,7 +1166,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 393,
"height": 786
@ -1177,7 +1177,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/116.0.5845.62 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/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 786,
"height": 393
@ -1188,7 +1188,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 4": {
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 353,
"height": 745
@ -1199,7 +1199,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 4 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 745,
"height": 353
@ -1210,7 +1210,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 4a (5G)": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Mobile Safari/537.36",
"screen": {
"width": 412,
"height": 892
@ -1225,7 +1225,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 4a (5G) landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Mobile Safari/537.36",
"screen": {
"height": 892,
"width": 412
@ -1240,7 +1240,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 5": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Mobile Safari/537.36",
"screen": {
"width": 393,
"height": 851
@ -1255,7 +1255,7 @@
"defaultBrowserType": "chromium"
},
"Pixel 5 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Mobile Safari/537.36",
"screen": {
"width": 851,
"height": 393
@ -1270,7 +1270,7 @@
"defaultBrowserType": "chromium"
},
"Moto G4": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 360,
"height": 640
@ -1281,7 +1281,7 @@
"defaultBrowserType": "chromium"
},
"Moto G4 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.62 Mobile Safari/537.36",
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Mobile Safari/537.36",
"viewport": {
"width": 640,
"height": 360
@ -1292,7 +1292,7 @@
"defaultBrowserType": "chromium"
},
"Desktop Chrome HiDPI": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.62 Safari/537.36",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Safari/537.36",
"screen": {
"width": 1792,
"height": 1120
@ -1307,7 +1307,7 @@
"defaultBrowserType": "chromium"
},
"Desktop Edge HiDPI": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.62 Safari/537.36 Edg/116.0.5845.62",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Safari/537.36 Edg/116.0.5845.82",
"screen": {
"width": 1792,
"height": 1120
@ -1352,7 +1352,7 @@
"defaultBrowserType": "webkit"
},
"Desktop Chrome": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.62 Safari/537.36",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Safari/537.36",
"screen": {
"width": 1920,
"height": 1080
@ -1367,7 +1367,7 @@
"defaultBrowserType": "chromium"
},
"Desktop Edge": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.62 Safari/537.36 Edg/116.0.5845.62",
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.82 Safari/537.36 Edg/116.0.5845.82",
"screen": {
"width": 1920,
"height": 1080

View file

@ -1,6 +1,6 @@
{
"name": "@playwright/experimental-ct-core",
"version": "1.37.0-next",
"version": "1.37.0",
"description": "Playwright Component Testing Helpers",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
@ -21,9 +21,9 @@
"./lib/vitePlugin": "./lib/vitePlugin.js"
},
"dependencies": {
"playwright-core": "1.37.0-next",
"playwright-core": "1.37.0",
"vite": "^4.3.9",
"@playwright/test": "1.37.0-next"
"@playwright/test": "1.37.0"
},
"bin": {
"playwright": "./cli.js"

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,6 +1,6 @@
{
"name": "playwright-firefox",
"version": "1.37.0-next",
"version": "1.37.0",
"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.37.0-next"
"playwright-core": "1.37.0"
}
}

View file

@ -1,6 +1,6 @@
{
"name": "@playwright/test",
"version": "1.37.0-next",
"version": "1.37.0",
"description": "A high-level API to automate web browsers",
"repository": "github:Microsoft/playwright",
"homepage": "https://playwright.dev",
@ -41,7 +41,7 @@
"license": "Apache-2.0",
"dependencies": {
"@types/node": "*",
"playwright-core": "1.37.0-next"
"playwright-core": "1.37.0"
},
"optionalDependencies": {
"fsevents": "2.3.2"

View file

@ -27,9 +27,53 @@ import { addToCompilationCache } from '../transform/compilationCache';
import { initializeEsmLoader } from './esmLoaderHost';
const kDefineConfigWasUsed = Symbol('defineConfigWasUsed');
export const defineConfig = (config: any) => {
config[kDefineConfigWasUsed] = true;
return config;
export const defineConfig = (...configs: any[]) => {
let result = configs[0];
for (let i = 1; i < configs.length; ++i) {
const config = configs[i];
result = {
...result,
...config,
expect: {
...result.expect,
...config.expect,
},
use: {
...result.use,
...config.use,
},
webServer: [
...(Array.isArray(result.webServer) ? result.webServer : (result.webServer ? [result.webServer] : [])),
...(Array.isArray(config.webServer) ? config.webServer : (config.webServer ? [config.webServer] : [])),
]
};
const projectOverrides = new Map<string, any>();
for (const project of config.projects || [])
projectOverrides.set(project.name, project);
const projects = [];
for (const project of result.projects || []) {
const projectOverride = projectOverrides.get(project.name);
if (projectOverride) {
projects.push({
...project,
...projectOverride,
use: {
...project.use,
...projectOverride.use,
}
});
projectOverrides.delete(project.name);
} else {
projects.push(project);
}
}
projects.push(...projectOverrides.values());
result.projects = projects;
}
result[kDefineConfigWasUsed] = true;
return result;
};
export class ConfigLoader {

View file

@ -110,15 +110,17 @@ async function extractAndParseReports(dir: string, shardFiles: string[], interna
const shardEvents: { file: string, localPath: string, metadata: BlobReportMetadata, parsedEvents: JsonEvent[] }[] = [];
await fs.promises.mkdir(path.join(dir, 'resources'), { recursive: true });
const reportNames = new UniqueFileNameGenerator();
for (const file of shardFiles) {
const absolutePath = path.join(dir, file);
printStatus(`extracting: ${relativeFilePath(absolutePath)}`);
const zipFile = new ZipFile(absolutePath);
const entryNames = await zipFile.entries();
for (const entryName of entryNames.sort()) {
const fileName = path.join(dir, entryName);
let fileName = path.join(dir, entryName);
const content = await zipFile.read(entryName);
if (entryName.endsWith('.jsonl')) {
fileName = reportNames.makeUnique(fileName);
const parsedEvents = parseCommonEvents(content);
// Passing reviver to JSON.parse doesn't work, as the original strings
// keep beeing used. To work around that we traverse the parsed events
@ -285,6 +287,27 @@ function printStatusToStdout(message: string) {
process.stdout.write(`${message}\n`);
}
class UniqueFileNameGenerator {
private _usedNames = new Set<string>();
makeUnique(name: string): string {
if (!this._usedNames.has(name)) {
this._usedNames.add(name);
return name;
}
const extension = path.extname(name);
name = name.substring(0, name.length - extension.length);
let index = 0;
while (true) {
const candidate = `${name}-${++index}${extension}`;
if (!this._usedNames.has(candidate)) {
this._usedNames.add(candidate);
return candidate;
}
}
}
}
class IdsPatcher {
constructor(private _stringPool: StringInternPool, private _reportName: string | undefined, private _salt: string) {
}

View file

@ -5022,6 +5022,9 @@ export const expect: Expect;
export function defineConfig(config: PlaywrightTestConfig): PlaywrightTestConfig;
export function defineConfig<T>(config: PlaywrightTestConfig<T>): PlaywrightTestConfig<T>;
export function defineConfig<T, W>(config: PlaywrightTestConfig<T, W>): PlaywrightTestConfig<T, W>;
export function defineConfig(config: PlaywrightTestConfig, ...configs: PlaywrightTestConfig[]): PlaywrightTestConfig;
export function defineConfig<T>(config: PlaywrightTestConfig<T>, ...configs: PlaywrightTestConfig[]): PlaywrightTestConfig<T>;
export function defineConfig<T, W>(config: PlaywrightTestConfig<T, W>, ...configs: PlaywrightTestConfig[]): PlaywrightTestConfig<T, W>;
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
export {};

View file

@ -1,6 +1,6 @@
{
"name": "playwright-webkit",
"version": "1.37.0-next",
"version": "1.37.0",
"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.37.0-next"
"playwright-core": "1.37.0"
}
}

View file

@ -1,6 +1,6 @@
{
"name": "playwright",
"version": "1.37.0-next",
"version": "1.37.0",
"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.37.0-next"
"playwright-core": "1.37.0"
}
}

View file

@ -17,7 +17,7 @@
import { config as loadEnv } from 'dotenv';
loadEnv({ path: path.join(__dirname, '..', '..', '.env'), override: true });
import type { Config, PlaywrightTestOptions, PlaywrightWorkerOptions, ReporterDescription } from '@playwright/test';
import { type Config, type PlaywrightTestOptions, type PlaywrightWorkerOptions, type ReporterDescription } from '@playwright/test';
import * as path from 'path';
import type { TestModeWorkerOptions } from '../config/testModeFixtures';
import type { TestModeName } from '../config/testMode';

View file

@ -497,3 +497,60 @@ test('should not allow tracesDir in launchOptions', async ({ runTSC }) => {
});
expect(result.exitCode).not.toBe(0);
});
test('should merge configs', async ({ runInlineTest }) => {
const result = await runInlineTest({
'playwright.config.ts': `
import { defineConfig, expect } from '@playwright/test';
const baseConfig = defineConfig({
timeout: 10,
use: {
foo: 1,
},
expect: {
timeout: 11,
},
projects: [
{
name: 'A',
timeout: 20,
}
],
});
const derivedConfig = defineConfig(baseConfig, {
timeout: 30,
use: {
bar: 2,
},
expect: {
timeout: 12,
},
projects: [
{ name: 'B', timeout: 40 },
{ name: 'A', timeout: 50 },
],
webServer: {
command: 'echo 123',
}
});
expect(derivedConfig).toEqual(expect.objectContaining({
timeout: 30,
use: { foo: 1, bar: 2 },
expect: { timeout: 12 },
projects: [
{ name: 'B', timeout: 40, use: {} },
{ name: 'A', timeout: 50, use: {} }
],
webServer: [{
command: 'echo 123',
}]
}));
`,
'a.test.ts': `
import { test } from '@playwright/test';
test('pass', async ({}) => {});
`
});
expect(result.exitCode).toBe(0);
});

View file

@ -1324,3 +1324,51 @@ test('merge-reports should throw if report version is from the future', async ({
expect(output).toContain(`Error: Blob report report-2.zip was created with a newer version of Playwright.`);
});
test('should merge blob reports with same name', async ({ runInlineTest, mergeReports, showReport, page }) => {
const files = {
'playwright.config.ts': `
module.exports = {
retries: 1,
reporter: 'blob'
};
`,
'a.test.js': `
import { test, expect } from '@playwright/test';
test('math 1', async ({}) => {
expect(1 + 1).toBe(2);
});
test('failing 1', async ({}) => {
expect(1).toBe(2);
});
test('flaky 1', async ({}) => {
expect(test.info().retry).toBe(1);
});
test.skip('skipped 1', async ({}) => {});
`,
'b.test.js': `
import { test, expect } from '@playwright/test';
test('math 2', async ({}) => {
expect(1 + 1).toBe(2);
});
test('failing 2', async ({}) => {
expect(1).toBe(2);
});
test.skip('skipped 2', async ({}) => {});
`
};
await runInlineTest(files);
const reportZip = test.info().outputPath('blob-report', 'report.zip');
const allReportsDir = test.info().outputPath('all-blob-reports');
await fs.promises.cp(reportZip, path.join(allReportsDir, 'report-1.zip'));
await fs.promises.cp(reportZip, path.join(allReportsDir, 'report-2.zip'));
const { exitCode } = await mergeReports(allReportsDir, { 'PW_TEST_HTML_REPORT_OPEN': 'never' }, { additionalArgs: ['--reporter', 'html'] });
expect(exitCode).toBe(0);
await showReport();
await expect(page.locator('.subnav-item:has-text("All") .counter')).toHaveText('14');
await expect(page.locator('.subnav-item:has-text("Passed") .counter')).toHaveText('4');
await expect(page.locator('.subnav-item:has-text("Failed") .counter')).toHaveText('4');
await expect(page.locator('.subnav-item:has-text("Flaky") .counter')).toHaveText('2');
await expect(page.locator('.subnav-item:has-text("Skipped") .counter')).toHaveText('4');
});

View file

@ -400,6 +400,9 @@ export const expect: Expect;
export function defineConfig(config: PlaywrightTestConfig): PlaywrightTestConfig;
export function defineConfig<T>(config: PlaywrightTestConfig<T>): PlaywrightTestConfig<T>;
export function defineConfig<T, W>(config: PlaywrightTestConfig<T, W>): PlaywrightTestConfig<T, W>;
export function defineConfig(config: PlaywrightTestConfig, ...configs: PlaywrightTestConfig[]): PlaywrightTestConfig;
export function defineConfig<T>(config: PlaywrightTestConfig<T>, ...configs: PlaywrightTestConfig[]): PlaywrightTestConfig<T>;
export function defineConfig<T, W>(config: PlaywrightTestConfig<T, W>, ...configs: PlaywrightTestConfig[]): PlaywrightTestConfig<T, W>;
// This is required to not export everything by default. See https://github.com/Microsoft/TypeScript/issues/19545#issuecomment-340490459
export {};