Compare commits

...

9 commits

Author SHA1 Message Date
Dmitry Gozman 7c7f8ac9fa
cherry-pick(#30227): chore(deps): bump vite from 5.0.12 to 5.0.13 (#30254) 2024-04-04 12:25:32 -07:00
Max Schmitt 62d4dc9ebb cherry-pick(#30235): feat(chromium): roll to r1112 2024-04-04 09:59:36 +02:00
Pavel Feldman 79d477666f cherry-pick(#30226): chore: migrate to the testserver.initialize 2024-04-03 12:52:29 -07:00
Pavel Feldman 6b94231dcf cherry-pick(#30202): do not run setup tasks on test run via server 2024-04-01 15:32:58 -07:00
Pavel Feldman 0889736332 cherry-pick(#30185): chore: opt into stdio forwarding 2024-03-30 18:48:34 -07:00
Dmitry Gozman a01db3ffd7
chore: mark version 1.43.0 (#30183) 2024-03-29 12:54:53 -07:00
Pavel Feldman 1cd1239d62 cherry-pick(#30170): chore: hide internal commands 2024-03-28 12:19:58 -07:00
Max Schmitt 122ab67e34 cherry-pick(#30138): fix: UI Mode tags without theme applied
Trace Viewer theming is override based. By default you should have
something applied - an optional `dark-mode` class might be there too.
2024-03-27 16:47:16 +01:00
Pavel Feldman 75776dbc3e cherry-pick(#30135): chore: do not exit UI mode upon page reload 2024-03-26 15:51:25 -07:00
32 changed files with 227 additions and 219 deletions

View file

@ -1,6 +1,6 @@
# 🎭 Playwright # 🎭 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-124.0.6367.8-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-124.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.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-124.0.6367.29-blue.svg?logo=google-chrome)](https://www.chromium.org/Home)<!-- GEN:stop --> <!-- GEN:firefox-version-badge -->[![Firefox version](https://img.shields.io/badge/firefox-124.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.4-blue.svg?logo=safari)](https://webkit.org/)<!-- GEN:stop -->
## [Documentation](https://playwright.dev) | [API reference](https://playwright.dev/docs/api/class-playwright) ## [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 | | | Linux | macOS | Windows |
| :--- | :---: | :---: | :---: | | :--- | :---: | :---: | :---: |
| Chromium <!-- GEN:chromium-version -->124.0.6367.8<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: | | Chromium <!-- GEN:chromium-version -->124.0.6367.29<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| WebKit <!-- GEN:webkit-version -->17.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: | | WebKit <!-- GEN:webkit-version -->17.4<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |
| Firefox <!-- GEN:firefox-version -->124.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: | | Firefox <!-- GEN:firefox-version -->124.0<!-- GEN:stop --> | :white_check_mark: | :white_check_mark: | :white_check_mark: |

78
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "playwright-internal", "name": "playwright-internal",
"version": "1.43.0-next", "version": "1.43.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "playwright-internal", "name": "playwright-internal",
"version": "1.43.0-next", "version": "1.43.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"workspaces": [ "workspaces": [
"packages/*" "packages/*"
@ -61,7 +61,7 @@
"socksv5": "0.0.6", "socksv5": "0.0.6",
"ssim.js": "^3.5.0", "ssim.js": "^3.5.0",
"typescript": "^5.3.2", "typescript": "^5.3.2",
"vite": "^5.0.12", "vite": "^5.0.13",
"ws": "^8.5.0", "ws": "^8.5.0",
"xml2js": "^0.5.0", "xml2js": "^0.5.0",
"yaml": "^2.2.2" "yaml": "^2.2.2"
@ -7345,9 +7345,9 @@
} }
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "5.0.12", "version": "5.0.13",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.0.12.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.13.tgz",
"integrity": "sha512-4hsnEkG3q0N4Tzf1+t6NdN9dg/L3BM+q8SWgbSPnJvrgH2kgdyzfVJwbR1ic69/4uMJJ/3dqDZZE5/WwqW8U1w==", "integrity": "sha512-/9ovhv2M2dGTuA+dY93B9trfyWMDRQw2jdVBhHNP6wr0oF34wG2i/N55801iZIpgUpnHDm4F/FabGQLyc+eOgg==",
"dependencies": { "dependencies": {
"esbuild": "^0.19.3", "esbuild": "^0.19.3",
"postcss": "^8.4.32", "postcss": "^8.4.32",
@ -8136,10 +8136,10 @@
} }
}, },
"packages/playwright": { "packages/playwright": {
"version": "1.43.0-next", "version": "1.43.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next" "playwright-core": "1.43.0"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -8153,11 +8153,11 @@
}, },
"packages/playwright-browser-chromium": { "packages/playwright-browser-chromium": {
"name": "@playwright/browser-chromium", "name": "@playwright/browser-chromium",
"version": "1.43.0-next", "version": "1.43.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next" "playwright-core": "1.43.0"
}, },
"engines": { "engines": {
"node": ">=16" "node": ">=16"
@ -8165,11 +8165,11 @@
}, },
"packages/playwright-browser-firefox": { "packages/playwright-browser-firefox": {
"name": "@playwright/browser-firefox", "name": "@playwright/browser-firefox",
"version": "1.43.0-next", "version": "1.43.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next" "playwright-core": "1.43.0"
}, },
"engines": { "engines": {
"node": ">=16" "node": ">=16"
@ -8177,22 +8177,22 @@
}, },
"packages/playwright-browser-webkit": { "packages/playwright-browser-webkit": {
"name": "@playwright/browser-webkit", "name": "@playwright/browser-webkit",
"version": "1.43.0-next", "version": "1.43.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next" "playwright-core": "1.43.0"
}, },
"engines": { "engines": {
"node": ">=16" "node": ">=16"
} }
}, },
"packages/playwright-chromium": { "packages/playwright-chromium": {
"version": "1.43.0-next", "version": "1.43.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next" "playwright-core": "1.43.0"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -8202,7 +8202,7 @@
} }
}, },
"packages/playwright-core": { "packages/playwright-core": {
"version": "1.43.0-next", "version": "1.43.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"bin": { "bin": {
"playwright-core": "cli.js" "playwright-core": "cli.js"
@ -8213,12 +8213,12 @@
}, },
"packages/playwright-ct-core": { "packages/playwright-ct-core": {
"name": "@playwright/experimental-ct-core", "name": "@playwright/experimental-ct-core",
"version": "1.43.0-next", "version": "1.43.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright": "1.43.0-next", "playwright": "1.43.0",
"playwright-core": "1.43.0-next", "playwright-core": "1.43.0",
"vite": "^5.0.12" "vite": "^5.0.13"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -8229,10 +8229,10 @@
}, },
"packages/playwright-ct-react": { "packages/playwright-ct-react": {
"name": "@playwright/experimental-ct-react", "name": "@playwright/experimental-ct-react",
"version": "1.43.0-next", "version": "1.43.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next", "@playwright/experimental-ct-core": "1.43.0",
"@vitejs/plugin-react": "^4.2.1" "@vitejs/plugin-react": "^4.2.1"
}, },
"bin": { "bin": {
@ -8244,10 +8244,10 @@
}, },
"packages/playwright-ct-react17": { "packages/playwright-ct-react17": {
"name": "@playwright/experimental-ct-react17", "name": "@playwright/experimental-ct-react17",
"version": "1.43.0-next", "version": "1.43.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next", "@playwright/experimental-ct-core": "1.43.0",
"@vitejs/plugin-react": "^4.2.1" "@vitejs/plugin-react": "^4.2.1"
}, },
"bin": { "bin": {
@ -8259,10 +8259,10 @@
}, },
"packages/playwright-ct-solid": { "packages/playwright-ct-solid": {
"name": "@playwright/experimental-ct-solid", "name": "@playwright/experimental-ct-solid",
"version": "1.43.0-next", "version": "1.43.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next", "@playwright/experimental-ct-core": "1.43.0",
"vite-plugin-solid": "^2.7.0" "vite-plugin-solid": "^2.7.0"
}, },
"bin": { "bin": {
@ -8277,10 +8277,10 @@
}, },
"packages/playwright-ct-svelte": { "packages/playwright-ct-svelte": {
"name": "@playwright/experimental-ct-svelte", "name": "@playwright/experimental-ct-svelte",
"version": "1.43.0-next", "version": "1.43.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next", "@playwright/experimental-ct-core": "1.43.0",
"@sveltejs/vite-plugin-svelte": "^3.0.1" "@sveltejs/vite-plugin-svelte": "^3.0.1"
}, },
"bin": { "bin": {
@ -8295,10 +8295,10 @@
}, },
"packages/playwright-ct-vue": { "packages/playwright-ct-vue": {
"name": "@playwright/experimental-ct-vue", "name": "@playwright/experimental-ct-vue",
"version": "1.43.0-next", "version": "1.43.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next", "@playwright/experimental-ct-core": "1.43.0",
"@vitejs/plugin-vue": "^4.2.1" "@vitejs/plugin-vue": "^4.2.1"
}, },
"bin": { "bin": {
@ -8310,10 +8310,10 @@
}, },
"packages/playwright-ct-vue2": { "packages/playwright-ct-vue2": {
"name": "@playwright/experimental-ct-vue2", "name": "@playwright/experimental-ct-vue2",
"version": "1.43.0-next", "version": "1.43.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next", "@playwright/experimental-ct-core": "1.43.0",
"@vitejs/plugin-vue2": "^2.2.0" "@vitejs/plugin-vue2": "^2.2.0"
}, },
"bin": { "bin": {
@ -8362,11 +8362,11 @@
} }
}, },
"packages/playwright-firefox": { "packages/playwright-firefox": {
"version": "1.43.0-next", "version": "1.43.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next" "playwright-core": "1.43.0"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -8377,10 +8377,10 @@
}, },
"packages/playwright-test": { "packages/playwright-test": {
"name": "@playwright/test", "name": "@playwright/test",
"version": "1.43.0-next", "version": "1.43.0",
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright": "1.43.0-next" "playwright": "1.43.0"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@ -8390,11 +8390,11 @@
} }
}, },
"packages/playwright-webkit": { "packages/playwright-webkit": {
"version": "1.43.0-next", "version": "1.43.0",
"hasInstallScript": true, "hasInstallScript": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next" "playwright-core": "1.43.0"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"

View file

@ -1,7 +1,7 @@
{ {
"name": "playwright-internal", "name": "playwright-internal",
"private": true, "private": true,
"version": "1.43.0-next", "version": "1.43.0",
"description": "A high-level API to automate web browsers", "description": "A high-level API to automate web browsers",
"repository": { "repository": {
"type": "git", "type": "git",
@ -99,7 +99,7 @@
"socksv5": "0.0.6", "socksv5": "0.0.6",
"ssim.js": "^3.5.0", "ssim.js": "^3.5.0",
"typescript": "^5.3.2", "typescript": "^5.3.2",
"vite": "^5.0.12", "vite": "^5.0.13",
"ws": "^8.5.0", "ws": "^8.5.0",
"xml2js": "^0.5.0", "xml2js": "^0.5.0",
"yaml": "^2.2.2" "yaml": "^2.2.2"

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/browser-chromium", "name": "@playwright/browser-chromium",
"version": "1.43.0-next", "version": "1.43.0",
"description": "Playwright package that automatically installs Chromium", "description": "Playwright package that automatically installs Chromium",
"repository": { "repository": {
"type": "git", "type": "git",
@ -27,6 +27,6 @@
"install": "node install.js" "install": "node install.js"
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next" "playwright-core": "1.43.0"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/browser-firefox", "name": "@playwright/browser-firefox",
"version": "1.43.0-next", "version": "1.43.0",
"description": "Playwright package that automatically installs Firefox", "description": "Playwright package that automatically installs Firefox",
"repository": { "repository": {
"type": "git", "type": "git",
@ -27,6 +27,6 @@
"install": "node install.js" "install": "node install.js"
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next" "playwright-core": "1.43.0"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/browser-webkit", "name": "@playwright/browser-webkit",
"version": "1.43.0-next", "version": "1.43.0",
"description": "Playwright package that automatically installs WebKit", "description": "Playwright package that automatically installs WebKit",
"repository": { "repository": {
"type": "git", "type": "git",
@ -27,6 +27,6 @@
"install": "node install.js" "install": "node install.js"
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next" "playwright-core": "1.43.0"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "playwright-chromium", "name": "playwright-chromium",
"version": "1.43.0-next", "version": "1.43.0",
"description": "A high-level API to automate Chromium", "description": "A high-level API to automate Chromium",
"repository": { "repository": {
"type": "git", "type": "git",
@ -30,6 +30,6 @@
"install": "node install.js" "install": "node install.js"
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next" "playwright-core": "1.43.0"
} }
} }

View file

@ -3,9 +3,9 @@
"browsers": [ "browsers": [
{ {
"name": "chromium", "name": "chromium",
"revision": "1110", "revision": "1112",
"installByDefault": true, "installByDefault": true,
"browserVersion": "124.0.6367.8" "browserVersion": "124.0.6367.29"
}, },
{ {
"name": "chromium-tip-of-tree", "name": "chromium-tip-of-tree",

View file

@ -1,6 +1,6 @@
{ {
"name": "playwright-core", "name": "playwright-core",
"version": "1.43.0-next", "version": "1.43.0",
"description": "A high-level API to automate web browsers", "description": "A high-level API to automate web browsers",
"repository": { "repository": {
"type": "git", "type": "git",

View file

@ -110,7 +110,7 @@
"defaultBrowserType": "webkit" "defaultBrowserType": "webkit"
}, },
"Galaxy S5": { "Galaxy S5": {
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 360, "width": 360,
"height": 640 "height": 640
@ -121,7 +121,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Galaxy S5 landscape": { "Galaxy S5 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 640, "width": 640,
"height": 360 "height": 360
@ -132,7 +132,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Galaxy S8": { "Galaxy S8": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 360, "width": 360,
"height": 740 "height": 740
@ -143,7 +143,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Galaxy S8 landscape": { "Galaxy S8 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 7.0; SM-G950U Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 740, "width": 740,
"height": 360 "height": 360
@ -154,7 +154,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Galaxy S9+": { "Galaxy S9+": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 320, "width": 320,
"height": 658 "height": 658
@ -165,7 +165,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Galaxy S9+ landscape": { "Galaxy S9+ landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G965U Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 658, "width": 658,
"height": 320 "height": 320
@ -176,7 +176,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Galaxy Tab S4": { "Galaxy Tab S4": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Safari/537.36",
"viewport": { "viewport": {
"width": 712, "width": 712,
"height": 1138 "height": 1138
@ -187,7 +187,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Galaxy Tab S4 landscape": { "Galaxy Tab S4 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 8.1.0; SM-T837A) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Safari/537.36",
"viewport": { "viewport": {
"width": 1138, "width": 1138,
"height": 712 "height": 712
@ -978,7 +978,7 @@
"defaultBrowserType": "webkit" "defaultBrowserType": "webkit"
}, },
"LG Optimus L70": { "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/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 384, "width": 384,
"height": 640 "height": 640
@ -989,7 +989,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"LG Optimus L70 landscape": { "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/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 640, "width": 640,
"height": 384 "height": 384
@ -1000,7 +1000,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Microsoft Lumia 550": { "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/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36 Edge/14.14263",
"viewport": { "viewport": {
"width": 640, "width": 640,
"height": 360 "height": 360
@ -1011,7 +1011,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Microsoft Lumia 550 landscape": { "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/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36 Edge/14.14263",
"viewport": { "viewport": {
"width": 360, "width": 360,
"height": 640 "height": 640
@ -1022,7 +1022,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Microsoft Lumia 950": { "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/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36 Edge/14.14263",
"viewport": { "viewport": {
"width": 360, "width": 360,
"height": 640 "height": 640
@ -1033,7 +1033,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Microsoft Lumia 950 landscape": { "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/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36 Edge/14.14263",
"viewport": { "viewport": {
"width": 640, "width": 640,
"height": 360 "height": 360
@ -1044,7 +1044,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 10": { "Nexus 10": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Safari/537.36",
"viewport": { "viewport": {
"width": 800, "width": 800,
"height": 1280 "height": 1280
@ -1055,7 +1055,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 10 landscape": { "Nexus 10 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 10 Build/MOB31T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Safari/537.36",
"viewport": { "viewport": {
"width": 1280, "width": 1280,
"height": 800 "height": 800
@ -1066,7 +1066,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 4": { "Nexus 4": {
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 384, "width": 384,
"height": 640 "height": 640
@ -1077,7 +1077,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 4 landscape": { "Nexus 4 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 4.4.2; Nexus 4 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 640, "width": 640,
"height": 384 "height": 384
@ -1088,7 +1088,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 5": { "Nexus 5": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 360, "width": 360,
"height": 640 "height": 640
@ -1099,7 +1099,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 5 landscape": { "Nexus 5 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 640, "width": 640,
"height": 360 "height": 360
@ -1110,7 +1110,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 5X": { "Nexus 5X": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 5X Build/OPR4.170623.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 412, "width": 412,
"height": 732 "height": 732
@ -1121,7 +1121,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 5X landscape": { "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/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 732, "width": 732,
"height": 412 "height": 412
@ -1132,7 +1132,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 6": { "Nexus 6": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 412, "width": 412,
"height": 732 "height": 732
@ -1143,7 +1143,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 6 landscape": { "Nexus 6 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 7.1.1; Nexus 6 Build/N6F26U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 732, "width": 732,
"height": 412 "height": 412
@ -1154,7 +1154,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 6P": { "Nexus 6P": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0.0; Nexus 6P Build/OPP3.170518.006) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 412, "width": 412,
"height": 732 "height": 732
@ -1165,7 +1165,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 6P landscape": { "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/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 732, "width": 732,
"height": 412 "height": 412
@ -1176,7 +1176,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 7": { "Nexus 7": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Safari/537.36",
"viewport": { "viewport": {
"width": 600, "width": 600,
"height": 960 "height": 960
@ -1187,7 +1187,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Nexus 7 landscape": { "Nexus 7 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 7 Build/MOB30X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Safari/537.36",
"viewport": { "viewport": {
"width": 960, "width": 960,
"height": 600 "height": 600
@ -1242,7 +1242,7 @@
"defaultBrowserType": "webkit" "defaultBrowserType": "webkit"
}, },
"Pixel 2": { "Pixel 2": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 411, "width": 411,
"height": 731 "height": 731
@ -1253,7 +1253,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 2 landscape": { "Pixel 2 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 8.0; Pixel 2 Build/OPD3.170816.012) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 731, "width": 731,
"height": 411 "height": 411
@ -1264,7 +1264,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 2 XL": { "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/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 411, "width": 411,
"height": 823 "height": 823
@ -1275,7 +1275,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 2 XL landscape": { "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/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 823, "width": 823,
"height": 411 "height": 411
@ -1286,7 +1286,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 3": { "Pixel 3": {
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 393, "width": 393,
"height": 786 "height": 786
@ -1297,7 +1297,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 3 landscape": { "Pixel 3 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 9; Pixel 3 Build/PQ1A.181105.017.A1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 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/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 786, "width": 786,
"height": 393 "height": 393
@ -1308,7 +1308,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 4": { "Pixel 4": {
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 353, "width": 353,
"height": 745 "height": 745
@ -1319,7 +1319,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 4 landscape": { "Pixel 4 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 10; Pixel 4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 745, "width": 745,
"height": 353 "height": 353
@ -1330,7 +1330,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 4a (5G)": { "Pixel 4a (5G)": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"screen": { "screen": {
"width": 412, "width": 412,
"height": 892 "height": 892
@ -1345,7 +1345,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 4a (5G) landscape": { "Pixel 4a (5G) landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 4a (5G)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"screen": { "screen": {
"height": 892, "height": 892,
"width": 412 "width": 412
@ -1360,7 +1360,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 5": { "Pixel 5": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"screen": { "screen": {
"width": 393, "width": 393,
"height": 851 "height": 851
@ -1375,7 +1375,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 5 landscape": { "Pixel 5 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"screen": { "screen": {
"width": 851, "width": 851,
"height": 393 "height": 393
@ -1390,7 +1390,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 7": { "Pixel 7": {
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"screen": { "screen": {
"width": 412, "width": 412,
"height": 915 "height": 915
@ -1405,7 +1405,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Pixel 7 landscape": { "Pixel 7 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"screen": { "screen": {
"width": 915, "width": 915,
"height": 412 "height": 412
@ -1420,7 +1420,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Moto G4": { "Moto G4": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 360, "width": 360,
"height": 640 "height": 640
@ -1431,7 +1431,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Moto G4 landscape": { "Moto G4 landscape": {
"userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Mobile Safari/537.36", "userAgent": "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Mobile Safari/537.36",
"viewport": { "viewport": {
"width": 640, "width": 640,
"height": 360 "height": 360
@ -1442,7 +1442,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Desktop Chrome HiDPI": { "Desktop Chrome HiDPI": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Safari/537.36", "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Safari/537.36",
"screen": { "screen": {
"width": 1792, "width": 1792,
"height": 1120 "height": 1120
@ -1457,7 +1457,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Desktop Edge HiDPI": { "Desktop Edge HiDPI": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Safari/537.36 Edg/124.0.6367.8", "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Safari/537.36 Edg/124.0.6367.29",
"screen": { "screen": {
"width": 1792, "width": 1792,
"height": 1120 "height": 1120
@ -1502,7 +1502,7 @@
"defaultBrowserType": "webkit" "defaultBrowserType": "webkit"
}, },
"Desktop Chrome": { "Desktop Chrome": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Safari/537.36", "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Safari/537.36",
"screen": { "screen": {
"width": 1920, "width": 1920,
"height": 1080 "height": 1080
@ -1517,7 +1517,7 @@
"defaultBrowserType": "chromium" "defaultBrowserType": "chromium"
}, },
"Desktop Edge": { "Desktop Edge": {
"userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.8 Safari/537.36 Edg/124.0.6367.8", "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.29 Safari/537.36 Edg/124.0.6367.29",
"screen": { "screen": {
"width": 1920, "width": 1920,
"height": 1080 "height": 1080

View file

@ -222,14 +222,13 @@ class StdinServer implements Transport {
} }
async dispatch(method: string, params: any) { async dispatch(method: string, params: any) {
if (method === 'ready') { if (method === 'initialize') {
if (this._traceUrl) if (this._traceUrl)
this._loadTrace(this._traceUrl); this._loadTrace(this._traceUrl);
} }
} }
onclose() { onclose() {
gracefullyProcessExitDoNotHang(0);
} }
sendEvent?: (method: string, params: any) => void; sendEvent?: (method: string, params: any) => void;

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/experimental-ct-core", "name": "@playwright/experimental-ct-core",
"version": "1.43.0-next", "version": "1.43.0",
"description": "Playwright Component Testing Helpers", "description": "Playwright Component Testing Helpers",
"repository": { "repository": {
"type": "git", "type": "git",
@ -26,9 +26,9 @@
} }
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next", "playwright-core": "1.43.0",
"vite": "^5.0.12", "vite": "^5.0.13",
"playwright": "1.43.0-next" "playwright": "1.43.0"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"

View file

@ -21,7 +21,7 @@ import { runDevServer } from './devServer';
export { program } from 'playwright/lib/program'; export { program } from 'playwright/lib/program';
function addDevServerCommand(program: Command) { function addDevServerCommand(program: Command) {
const command = program.command('dev-server'); const command = program.command('dev-server', { hidden: true });
command.description('start dev server'); command.description('start dev server');
command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`); command.option('-c, --config <file>', `Configuration file, or a test directory with optional "playwright.config.{m,c}?{js,ts}"`);
command.action(options => { command.action(options => {

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/experimental-ct-react", "name": "@playwright/experimental-ct-react",
"version": "1.43.0-next", "version": "1.43.0",
"description": "Playwright Component Testing for React", "description": "Playwright Component Testing for React",
"repository": { "repository": {
"type": "git", "type": "git",
@ -29,7 +29,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next", "@playwright/experimental-ct-core": "1.43.0",
"@vitejs/plugin-react": "^4.2.1" "@vitejs/plugin-react": "^4.2.1"
}, },
"bin": { "bin": {

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/experimental-ct-react17", "name": "@playwright/experimental-ct-react17",
"version": "1.43.0-next", "version": "1.43.0",
"description": "Playwright Component Testing for React", "description": "Playwright Component Testing for React",
"repository": { "repository": {
"type": "git", "type": "git",
@ -29,7 +29,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next", "@playwright/experimental-ct-core": "1.43.0",
"@vitejs/plugin-react": "^4.2.1" "@vitejs/plugin-react": "^4.2.1"
}, },
"bin": { "bin": {

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/experimental-ct-solid", "name": "@playwright/experimental-ct-solid",
"version": "1.43.0-next", "version": "1.43.0",
"description": "Playwright Component Testing for Solid", "description": "Playwright Component Testing for Solid",
"repository": { "repository": {
"type": "git", "type": "git",
@ -29,7 +29,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next", "@playwright/experimental-ct-core": "1.43.0",
"vite-plugin-solid": "^2.7.0" "vite-plugin-solid": "^2.7.0"
}, },
"devDependencies": { "devDependencies": {

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/experimental-ct-svelte", "name": "@playwright/experimental-ct-svelte",
"version": "1.43.0-next", "version": "1.43.0",
"description": "Playwright Component Testing for Svelte", "description": "Playwright Component Testing for Svelte",
"repository": { "repository": {
"type": "git", "type": "git",
@ -29,7 +29,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next", "@playwright/experimental-ct-core": "1.43.0",
"@sveltejs/vite-plugin-svelte": "^3.0.1" "@sveltejs/vite-plugin-svelte": "^3.0.1"
}, },
"devDependencies": { "devDependencies": {

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/experimental-ct-vue", "name": "@playwright/experimental-ct-vue",
"version": "1.43.0-next", "version": "1.43.0",
"description": "Playwright Component Testing for Vue", "description": "Playwright Component Testing for Vue",
"repository": { "repository": {
"type": "git", "type": "git",
@ -29,7 +29,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next", "@playwright/experimental-ct-core": "1.43.0",
"@vitejs/plugin-vue": "^4.2.1" "@vitejs/plugin-vue": "^4.2.1"
}, },
"bin": { "bin": {

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/experimental-ct-vue2", "name": "@playwright/experimental-ct-vue2",
"version": "1.43.0-next", "version": "1.43.0",
"description": "Playwright Component Testing for Vue2", "description": "Playwright Component Testing for Vue2",
"repository": { "repository": {
"type": "git", "type": "git",
@ -29,7 +29,7 @@
} }
}, },
"dependencies": { "dependencies": {
"@playwright/experimental-ct-core": "1.43.0-next", "@playwright/experimental-ct-core": "1.43.0",
"@vitejs/plugin-vue2": "^2.2.0" "@vitejs/plugin-vue2": "^2.2.0"
}, },
"devDependencies": { "devDependencies": {

View file

@ -1,6 +1,6 @@
{ {
"name": "playwright-firefox", "name": "playwright-firefox",
"version": "1.43.0-next", "version": "1.43.0",
"description": "A high-level API to automate Firefox", "description": "A high-level API to automate Firefox",
"repository": { "repository": {
"type": "git", "type": "git",
@ -30,6 +30,6 @@
"install": "node install.js" "install": "node install.js"
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next" "playwright-core": "1.43.0"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "@playwright/test", "name": "@playwright/test",
"version": "1.43.0-next", "version": "1.43.0",
"description": "A high-level API to automate web browsers", "description": "A high-level API to automate web browsers",
"repository": { "repository": {
"type": "git", "type": "git",
@ -30,6 +30,6 @@
}, },
"scripts": {}, "scripts": {},
"dependencies": { "dependencies": {
"playwright": "1.43.0-next" "playwright": "1.43.0"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "playwright-webkit", "name": "playwright-webkit",
"version": "1.43.0-next", "version": "1.43.0",
"description": "A high-level API to automate WebKit", "description": "A high-level API to automate WebKit",
"repository": { "repository": {
"type": "git", "type": "git",
@ -30,6 +30,6 @@
"install": "node install.js" "install": "node install.js"
}, },
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next" "playwright-core": "1.43.0"
} }
} }

View file

@ -1,6 +1,6 @@
{ {
"name": "playwright", "name": "playwright",
"version": "1.43.0-next", "version": "1.43.0",
"description": "A high-level API to automate web browsers", "description": "A high-level API to automate web browsers",
"repository": { "repository": {
"type": "git", "type": "git",
@ -58,7 +58,7 @@
}, },
"license": "Apache-2.0", "license": "Apache-2.0",
"dependencies": { "dependencies": {
"playwright-core": "1.43.0-next" "playwright-core": "1.43.0"
}, },
"optionalDependencies": { "optionalDependencies": {
"fsevents": "2.3.2" "fsevents": "2.3.2"

View file

@ -64,10 +64,7 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
}); });
const pingInterval = setInterval(() => this._sendMessage('ping').catch(() => {}), 30000); const pingInterval = setInterval(() => this._sendMessage('ping').catch(() => {}), 30000);
this._connectedPromise = new Promise<void>((f, r) => { this._connectedPromise = new Promise<void>((f, r) => {
this._ws.addEventListener('open', () => { this._ws.addEventListener('open', () => f());
f();
this._ws.send(JSON.stringify({ id: -1, method: 'ready' }));
});
this._ws.addEventListener('error', r); this._ws.addEventListener('error', r);
}); });
this._ws.addEventListener('close', () => { this._ws.addEventListener('close', () => {
@ -76,10 +73,6 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
}); });
} }
connect() {
return this._connectedPromise;
}
private async _sendMessage(method: string, params?: any): Promise<any> { private async _sendMessage(method: string, params?: any): Promise<any> {
const logForTest = (globalThis as any).__logForTest; const logForTest = (globalThis as any).__logForTest;
logForTest?.({ method, params }); logForTest?.({ method, params });
@ -110,16 +103,16 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
this._onLoadTraceRequestedEmitter.fire(params); this._onLoadTraceRequestedEmitter.fire(params);
} }
async setSerializer(params: { serializer: string; }): Promise<void> { async initialize(params: Parameters<TestServerInterface['initialize']>[0]): ReturnType<TestServerInterface['initialize']> {
await this._sendMessage('setSerializer', params); await this._sendMessage('initialize', params);
} }
async ping(params: Parameters<TestServerInterface['ping']>[0]): ReturnType<TestServerInterface['ping']> { async ping(params: Parameters<TestServerInterface['ping']>[0]): ReturnType<TestServerInterface['ping']> {
await this._sendMessage('ping'); await this._sendMessage('ping', params);
} }
async pingNoReply(params: Parameters<TestServerInterface['ping']>[0]) { async pingNoReply(params: Parameters<TestServerInterface['ping']>[0]) {
this._sendMessageNoReply('ping'); this._sendMessageNoReply('ping', params);
} }
async watch(params: Parameters<TestServerInterface['watch']>[0]): ReturnType<TestServerInterface['watch']> { async watch(params: Parameters<TestServerInterface['watch']>[0]): ReturnType<TestServerInterface['watch']> {
@ -130,10 +123,6 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
this._sendMessageNoReply('watch', params); this._sendMessageNoReply('watch', params);
} }
async watchTestDir(params: Parameters<TestServerInterface['watchTestDir']>[0]): ReturnType<TestServerInterface['watchTestDir']> {
await this._sendMessage('watchTestDir', params);
}
async open(params: Parameters<TestServerInterface['open']>[0]): ReturnType<TestServerInterface['open']> { async open(params: Parameters<TestServerInterface['open']>[0]): ReturnType<TestServerInterface['open']> {
await this._sendMessage('open', params); await this._sendMessage('open', params);
} }
@ -151,19 +140,19 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
} }
async checkBrowsers(params: Parameters<TestServerInterface['checkBrowsers']>[0]): ReturnType<TestServerInterface['checkBrowsers']> { async checkBrowsers(params: Parameters<TestServerInterface['checkBrowsers']>[0]): ReturnType<TestServerInterface['checkBrowsers']> {
return await this._sendMessage('checkBrowsers'); return await this._sendMessage('checkBrowsers', params);
} }
async installBrowsers(params: Parameters<TestServerInterface['installBrowsers']>[0]): ReturnType<TestServerInterface['installBrowsers']> { async installBrowsers(params: Parameters<TestServerInterface['installBrowsers']>[0]): ReturnType<TestServerInterface['installBrowsers']> {
await this._sendMessage('installBrowsers'); await this._sendMessage('installBrowsers', params);
} }
async runGlobalSetup(params: Parameters<TestServerInterface['runGlobalSetup']>[0]): ReturnType<TestServerInterface['runGlobalSetup']> { async runGlobalSetup(params: Parameters<TestServerInterface['runGlobalSetup']>[0]): ReturnType<TestServerInterface['runGlobalSetup']> {
return await this._sendMessage('runGlobalSetup'); return await this._sendMessage('runGlobalSetup', params);
} }
async runGlobalTeardown(params: Parameters<TestServerInterface['runGlobalTeardown']>[0]): ReturnType<TestServerInterface['runGlobalTeardown']> { async runGlobalTeardown(params: Parameters<TestServerInterface['runGlobalTeardown']>[0]): ReturnType<TestServerInterface['runGlobalTeardown']> {
return await this._sendMessage('runGlobalTeardown'); return await this._sendMessage('runGlobalTeardown', params);
} }
async listFiles(params: Parameters<TestServerInterface['listFiles']>[0]): ReturnType<TestServerInterface['listFiles']> { async listFiles(params: Parameters<TestServerInterface['listFiles']>[0]): ReturnType<TestServerInterface['listFiles']> {
@ -183,14 +172,21 @@ export class TestServerConnection implements TestServerInterface, TestServerInte
} }
async stopTests(params: Parameters<TestServerInterface['stopTests']>[0]): ReturnType<TestServerInterface['stopTests']> { async stopTests(params: Parameters<TestServerInterface['stopTests']>[0]): ReturnType<TestServerInterface['stopTests']> {
await this._sendMessage('stopTests'); await this._sendMessage('stopTests', params);
} }
stopTestsNoReply(params: Parameters<TestServerInterface['stopTests']>[0]) { stopTestsNoReply(params: Parameters<TestServerInterface['stopTests']>[0]) {
this._sendMessageNoReply('stopTests'); this._sendMessageNoReply('stopTests', params);
} }
async closeGracefully(params: Parameters<TestServerInterface['closeGracefully']>[0]): ReturnType<TestServerInterface['closeGracefully']> { async closeGracefully(params: Parameters<TestServerInterface['closeGracefully']>[0]): ReturnType<TestServerInterface['closeGracefully']> {
await this._sendMessage('closeGracefully'); await this._sendMessage('closeGracefully', params);
}
close() {
try {
this._ws.close();
} catch {
}
} }
} }

View file

@ -21,7 +21,12 @@ import type { JsonEvent } from './teleReceiver';
export type ReportEntry = JsonEvent; export type ReportEntry = JsonEvent;
export interface TestServerInterface { export interface TestServerInterface {
setSerializer(params: { serializer: string }): Promise<void>; initialize(params: {
serializer?: string,
closeOnDisconnect?: boolean,
interceptStdio?: boolean,
watchTestDirs?: boolean,
}): Promise<void>;
ping(params: {}): Promise<void>; ping(params: {}): Promise<void>;
@ -29,8 +34,6 @@ export interface TestServerInterface {
fileNames: string[]; fileNames: string[];
}): Promise<void>; }): Promise<void>;
watchTestDir(params: {}): Promise<void>;
open(params: { location: reporterTypes.Location }): Promise<void>; open(params: { location: reporterTypes.Location }): Promise<void>;
resizeTerminal(params: { cols: number, rows: number }): Promise<void>; resizeTerminal(params: { cols: number, rows: number }): Promise<void>;

View file

@ -295,20 +295,7 @@ export class TestTree {
} }
collectTestIds(treeItem?: TreeItem): Set<string> { collectTestIds(treeItem?: TreeItem): Set<string> {
const testIds = new Set<string>(); return treeItem ? collectTestIds(treeItem) : new Set();
if (!treeItem)
return testIds;
const visit = (treeItem: TreeItem) => {
if (treeItem.kind === 'case')
treeItem.tests.map(t => t.id).forEach(id => testIds.add(id));
else if (treeItem.kind === 'test')
testIds.add(treeItem.id);
else
treeItem.children?.forEach(visit);
};
visit(treeItem);
return testIds;
} }
} }
@ -349,4 +336,18 @@ export function sortAndPropagateStatus(treeItem: TreeItem) {
treeItem.status = 'passed'; treeItem.status = 'passed';
} }
export function collectTestIds(treeItem: TreeItem): Set<string> {
const testIds = new Set<string>();
const visit = (treeItem: TreeItem) => {
if (treeItem.kind === 'case')
treeItem.tests.map(t => t.id).forEach(id => testIds.add(id));
else if (treeItem.kind === 'test')
testIds.add(treeItem.id);
else
treeItem.children?.forEach(visit);
};
visit(treeItem);
return testIds;
}
export const statusEx = Symbol('statusEx'); export const statusEx = Symbol('statusEx');

View file

@ -169,6 +169,8 @@ async function runTests(args: string[], opts: { [key: string]: any }) {
timeout: cliOverrides.timeout, timeout: cliOverrides.timeout,
}); });
await stopProfiling('runner'); await stopProfiling('runner');
if (status === 'restarted')
return;
const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1); const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1);
gracefullyProcessExitDoNotHang(exitCode); gracefullyProcessExitDoNotHang(exitCode);
return; return;
@ -200,6 +202,8 @@ async function runTestServer(opts: { [key: string]: any }) {
const host = opts.host || 'localhost'; const host = opts.host || 'localhost';
const port = opts.port ? +opts.port : 0; const port = opts.port ? +opts.port : 0;
const status = await testServer.runTestServer(opts.config, { host, port }); const status = await testServer.runTestServer(opts.config, { host, port });
if (status === 'restarted')
return;
const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1); const exitCode = status === 'interrupted' ? 130 : (status === 'passed' ? 0 : 1);
gracefullyProcessExitDoNotHang(exitCode); gracefullyProcessExitDoNotHang(exitCode);
} }

View file

@ -85,7 +85,6 @@ export function createTaskRunnerForWatch(config: FullConfigInternal, reporter: R
export function createTaskRunnerForTestServer(config: FullConfigInternal, reporter: ReporterV2): TaskRunner<TestRun> { export function createTaskRunnerForTestServer(config: FullConfigInternal, reporter: ReporterV2): TaskRunner<TestRun> {
const taskRunner = new TaskRunner<TestRun>(reporter, 0); const taskRunner = new TaskRunner<TestRun>(reporter, 0);
addGlobalSetupTasks(taskRunner, config);
taskRunner.addTask('load tests', createLoadTask('out-of-process', { filterOnly: true, failOnLoadErrors: false, doNotRunDepsOutsideProjectFilter: true })); taskRunner.addTask('load tests', createLoadTask('out-of-process', { filterOnly: true, failOnLoadErrors: false, doNotRunDepsOutsideProjectFilter: true }));
addRunTasks(taskRunner, config); addRunTasks(taskRunner, config);
return taskRunner; return taskRunner;

View file

@ -39,16 +39,15 @@ import type { TraceViewerRedirectOptions, TraceViewerServerOptions } from 'playw
import type { TestRunnerPluginRegistration } from '../plugins'; import type { TestRunnerPluginRegistration } from '../plugins';
import { serializeError } from '../util'; import { serializeError } from '../util';
const originalStdoutWrite = process.stdout.write;
const originalStderrWrite = process.stderr.write;
class TestServer { class TestServer {
private _configFile: string | undefined; private _configFile: string | undefined;
private _dispatcher: TestServerDispatcher | undefined; private _dispatcher: TestServerDispatcher | undefined;
private _originalStdoutWrite: NodeJS.WriteStream['write'];
private _originalStderrWrite: NodeJS.WriteStream['write'];
constructor(configFile: string | undefined) { constructor(configFile: string | undefined) {
this._configFile = configFile; this._configFile = configFile;
this._originalStdoutWrite = process.stdout.write;
this._originalStderrWrite = process.stderr.write;
} }
async start(options: { host?: string, port?: number }): Promise<HttpServer> { async start(options: { host?: string, port?: number }): Promise<HttpServer> {
@ -57,28 +56,9 @@ class TestServer {
} }
async stop() { async stop() {
await this._dispatcher?._setInterceptStdio(false);
await this._dispatcher?.runGlobalTeardown(); await this._dispatcher?.runGlobalTeardown();
} }
wireStdIO() {
if (!process.env.PWTEST_DEBUG) {
process.stdout.write = (chunk: string | Buffer) => {
this._dispatcher?._dispatchEvent('stdio', chunkToPayload('stdout', chunk));
return true;
};
process.stderr.write = (chunk: string | Buffer) => {
this._dispatcher?._dispatchEvent('stdio', chunkToPayload('stderr', chunk));
return true;
};
}
}
unwireStdIO() {
if (!process.env.PWTEST_DEBUG) {
process.stdout.write = this._originalStdoutWrite;
process.stderr.write = this._originalStderrWrite;
}
}
} }
class TestServerDispatcher implements TestServerInterface { class TestServerDispatcher implements TestServerInterface {
@ -92,13 +72,17 @@ class TestServerDispatcher implements TestServerInterface {
readonly _dispatchEvent: TestServerInterfaceEventEmitters['dispatchEvent']; readonly _dispatchEvent: TestServerInterfaceEventEmitters['dispatchEvent'];
private _plugins: TestRunnerPluginRegistration[] | undefined; private _plugins: TestRunnerPluginRegistration[] | undefined;
private _serializer = require.resolve('./uiModeReporter'); private _serializer = require.resolve('./uiModeReporter');
private _watchTestDir = false; private _watchTestDirs = false;
private _closeOnDisconnect = false;
constructor(configFile: string | undefined) { constructor(configFile: string | undefined) {
this._configFile = configFile; this._configFile = configFile;
this.transport = { this.transport = {
dispatch: (method, params) => (this as any)[method](params), dispatch: (method, params) => (this as any)[method](params),
onclose: () => gracefullyProcessExitDoNotHang(0), onclose: () => {
if (this._closeOnDisconnect)
gracefullyProcessExitDoNotHang(0);
},
}; };
this._globalWatcher = new Watcher('deep', () => this._dispatchEvent('listChanged', {})); this._globalWatcher = new Watcher('deep', () => this._dispatchEvent('listChanged', {}));
this._testWatcher = new Watcher('flat', events => { this._testWatcher = new Watcher('flat', events => {
@ -109,10 +93,6 @@ class TestServerDispatcher implements TestServerInterface {
this._dispatchEvent = (method, params) => this.transport.sendEvent?.(method, params); this._dispatchEvent = (method, params) => this.transport.sendEvent?.(method, params);
} }
async setSerializer(params: { serializer: string; }): Promise<void> {
this._serializer = params.serializer;
}
private async _wireReporter(messageSink: (message: any) => void) { private async _wireReporter(messageSink: (message: any) => void) {
return await createReporterForTestServer(this._serializer, messageSink); return await createReporterForTestServer(this._serializer, messageSink);
} }
@ -124,7 +104,16 @@ class TestServerDispatcher implements TestServerInterface {
return { reporter, report }; return { reporter, report };
} }
async ready() {} async initialize(params: Parameters<TestServerInterface['initialize']>[0]): ReturnType<TestServerInterface['initialize']> {
if (params.serializer)
this._serializer = params.serializer;
if (params.closeOnDisconnect)
this._closeOnDisconnect = true;
if (params.interceptStdio)
await this._setInterceptStdio(true);
if (params.watchTestDirs)
this._watchTestDirs = true;
}
async ping() {} async ping() {}
@ -246,7 +235,7 @@ class TestServerDispatcher implements TestServerInterface {
projectOutputs.add(result.outDir); projectOutputs.add(result.outDir);
} }
if (this._watchTestDir) if (this._watchTestDirs)
this._globalWatcher.update([...projectDirs], [...projectOutputs], false); this._globalWatcher.update([...projectDirs], [...projectOutputs], false);
return { report, status }; return { report, status };
} }
@ -315,10 +304,6 @@ class TestServerDispatcher implements TestServerInterface {
return { status: await run }; return { status: await run };
} }
async watchTestDir() {
this._watchTestDir = true;
}
async watch(params: { fileNames: string[]; }) { async watch(params: { fileNames: string[]; }) {
const files = new Set<string>(); const files = new Set<string>();
for (const fileName of params.fileNames) { for (const fileName of params.fileNames) {
@ -341,6 +326,24 @@ class TestServerDispatcher implements TestServerInterface {
await this._testRun?.run; await this._testRun?.run;
} }
async _setInterceptStdio(intercept: boolean) {
if (process.env.PWTEST_DEBUG)
return;
if (intercept) {
process.stdout.write = (chunk: string | Buffer) => {
this._dispatchEvent('stdio', chunkToPayload('stdout', chunk));
return true;
};
process.stderr.write = (chunk: string | Buffer) => {
this._dispatchEvent('stdio', chunkToPayload('stderr', chunk));
return true;
};
} else {
process.stdout.write = originalStdoutWrite;
process.stderr.write = originalStderrWrite;
}
}
async closeGracefully() { async closeGracefully() {
gracefullyProcessExitDoNotHang(0); gracefullyProcessExitDoNotHang(0);
} }
@ -362,7 +365,7 @@ class TestServerDispatcher implements TestServerInterface {
} }
} }
export async function runUIMode(configFile: string | undefined, options: TraceViewerServerOptions & TraceViewerRedirectOptions): Promise<reporterTypes.FullResult['status']> { export async function runUIMode(configFile: string | undefined, options: TraceViewerServerOptions & TraceViewerRedirectOptions): Promise<reporterTypes.FullResult['status'] | 'restarted'> {
return await innerRunTestServer(configFile, options, async (server: HttpServer, cancelPromise: ManualPromise<void>) => { return await innerRunTestServer(configFile, options, async (server: HttpServer, cancelPromise: ManualPromise<void>) => {
await installRootRedirect(server, [], { ...options, webApp: 'uiMode.html' }); await installRootRedirect(server, [], { ...options, webApp: 'uiMode.html' });
if (options.host !== undefined || options.port !== undefined) { if (options.host !== undefined || options.port !== undefined) {
@ -379,27 +382,26 @@ export async function runUIMode(configFile: string | undefined, options: TraceVi
}); });
} }
export async function runTestServer(configFile: string | undefined, options: { host?: string, port?: number }): Promise<reporterTypes.FullResult['status']> { export async function runTestServer(configFile: string | undefined, options: { host?: string, port?: number }): Promise<reporterTypes.FullResult['status'] | 'restarted'> {
return await innerRunTestServer(configFile, options, async server => { return await innerRunTestServer(configFile, options, async server => {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.log('Listening on ' + server.urlPrefix().replace('http:', 'ws:') + '/' + server.wsGuid()); console.log('Listening on ' + server.urlPrefix().replace('http:', 'ws:') + '/' + server.wsGuid());
}); });
} }
async function innerRunTestServer(configFile: string | undefined, options: { host?: string, port?: number }, openUI: (server: HttpServer, cancelPromise: ManualPromise<void>) => Promise<void>): Promise<reporterTypes.FullResult['status']> { async function innerRunTestServer(configFile: string | undefined, options: { host?: string, port?: number }, openUI: (server: HttpServer, cancelPromise: ManualPromise<void>) => Promise<void>): Promise<reporterTypes.FullResult['status'] | 'restarted'> {
if (restartWithExperimentalTsEsm(undefined, true)) if (restartWithExperimentalTsEsm(undefined, true))
return 'passed'; return 'restarted';
const testServer = new TestServer(configFile); const testServer = new TestServer(configFile);
const cancelPromise = new ManualPromise<void>(); const cancelPromise = new ManualPromise<void>();
const sigintWatcher = new SigIntWatcher(); const sigintWatcher = new SigIntWatcher();
process.stdin.on('close', () => gracefullyProcessExitDoNotHang(0));
void sigintWatcher.promise().then(() => cancelPromise.resolve()); void sigintWatcher.promise().then(() => cancelPromise.resolve());
try { try {
const server = await testServer.start(options); const server = await testServer.start(options);
await openUI(server, cancelPromise); await openUI(server, cancelPromise);
testServer.wireStdIO();
await cancelPromise; await cancelPromise;
} finally { } finally {
testServer.unwireStdIO();
await testServer.stop(); await testServer.stop();
sigintWatcher.disarm(); sigintWatcher.disarm();
} }

View file

@ -29,32 +29,32 @@
font-weight: 600; font-weight: 600;
} }
.light-mode .tag-color-0 { .tag-color-0 {
background-color: #ddf4ff; background-color: #ddf4ff;
color: #0550ae; color: #0550ae;
border: 1px solid #218bff; border: 1px solid #218bff;
} }
.light-mode .tag-color-1 { .tag-color-1 {
background-color: #fff8c5; background-color: #fff8c5;
color: #7d4e00; color: #7d4e00;
border: 1px solid #bf8700; border: 1px solid #bf8700;
} }
.light-mode .tag-color-2 { .tag-color-2 {
background-color: #fbefff; background-color: #fbefff;
color: #6e40c9; color: #6e40c9;
border: 1px solid #a475f9; border: 1px solid #a475f9;
} }
.light-mode .tag-color-3 { .tag-color-3 {
background-color: #ffeff7; background-color: #ffeff7;
color: #99286e; color: #99286e;
border: 1px solid #e85aad; border: 1px solid #e85aad;
} }
.light-mode .tag-color-4 { .tag-color-4 {
background-color: #FFF0EB; background-color: #FFF0EB;
color: #9E2F1C; color: #9E2F1C;
border: 1px solid #EA6045; border: 1px solid #EA6045;
} }
.light-mode .tag-color-5 { .tag-color-5 {
background-color: #fff1e5; background-color: #fff1e5;
color: #9b4215; color: #9b4215;
border: 1px solid #e16f24; border: 1px solid #e16f24;

View file

@ -172,7 +172,10 @@ export const UIModeView: React.FC<{}> = ({
setIsLoading(true); setIsLoading(true);
setWatchedTreeIds({ value: new Set() }); setWatchedTreeIds({ value: new Set() });
(async () => { (async () => {
await testServerConnection.watchTestDir({}); await testServerConnection.initialize({
interceptStdio: true,
watchTestDirs: true
});
const { status } = await testServerConnection.runGlobalSetup({}); const { status } = await testServerConnection.runGlobalSetup({});
if (status !== 'passed') if (status !== 'passed')
return; return;

View file

@ -93,6 +93,7 @@ export const WorkbenchLoader: React.FunctionComponent<{
setDragOver(false); setDragOver(false);
setProcessingErrorMessage(null); setProcessingErrorMessage(null);
}); });
testServerConnection.initialize({}).catch(() => {});
} else if (!newTraceURLs.some(url => url.startsWith('blob:'))) { } else if (!newTraceURLs.some(url => url.startsWith('blob:'))) {
// Don't re-use blob file URLs on page load (results in Fetch error) // Don't re-use blob file URLs on page load (results in Fetch error)
setTraceURLs(newTraceURLs); setTraceURLs(newTraceURLs);