diff --git a/docs/src/test-api/class-test.md b/docs/src/test-api/class-test.md index 9b56fbf2e6..b6ae7d1522 100644 --- a/docs/src/test-api/class-test.md +++ b/docs/src/test-api/class-test.md @@ -1710,6 +1710,11 @@ Step body. Whether to box the step in the report. Defaults to `false`. When the step is boxed, errors thrown from the step internals point to the step call site. See below for more details. +### option: Test.step.location +* since: v1.48 +- `location` <[Location]> +Specifies a custom location for the step to be shown in test reports. By default, location of the [`method: Test.step`] call is shown. + ## method: Test.use * since: v1.10 diff --git a/package-lock.json b/package-lock.json index b7cdad125e..efe6fafd8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,7 +61,7 @@ "react-dom": "^18.1.0", "ssim.js": "^3.5.0", "typescript": "^5.5.3", - "vite": "^5.0.13", + "vite": "^5.4.6", "ws": "^8.17.1", "xml2js": "^0.5.0", "yaml": "^2.2.2" @@ -852,9 +852,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", "cpu": [ "ppc64" ], @@ -1517,9 +1517,9 @@ "link": true }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz", - "integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.3.tgz", + "integrity": "sha512-MmKSfaB9GX+zXl6E8z4koOr/xU63AMVleLEa64v7R0QF/ZloMs5vcD1sHgM64GXXS1csaJutG+ddtzcueI/BLg==", "cpu": [ "arm" ], @@ -1529,9 +1529,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz", - "integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.3.tgz", + "integrity": "sha512-zrt8ecH07PE3sB4jPOggweBjJMzI1JG5xI2DIsUbkA+7K+Gkjys6eV7i9pOenNSDJH3eOr/jLb/PzqtmdwDq5g==", "cpu": [ "arm64" ], @@ -1541,9 +1541,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz", - "integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.3.tgz", + "integrity": "sha512-P0UxIOrKNBFTQaXTxOH4RxuEBVCgEA5UTNV6Yz7z9QHnUJ7eLX9reOd/NYMO3+XZO2cco19mXTxDMXxit4R/eQ==", "cpu": [ "arm64" ], @@ -1553,9 +1553,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz", - "integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.3.tgz", + "integrity": "sha512-L1M0vKGO5ASKntqtsFEjTq/fD91vAqnzeaF6sfNAy55aD+Hi2pBI5DKwCO+UNDQHWsDViJLqshxOahXyLSh3EA==", "cpu": [ "x64" ], @@ -1565,9 +1565,21 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz", - "integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.3.tgz", + "integrity": "sha512-btVgIsCjuYFKUjopPoWiDqmoUXQDiW2A4C3Mtmp5vACm7/GnyuprqIDPNczeyR5W8rTXEbkmrJux7cJmD99D2g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.3.tgz", + "integrity": "sha512-zmjbSphplZlau6ZTkxd3+NMtE4UKVy7U4aVFMmHcgO5CUbw17ZP6QCgyxhzGaU/wFFdTfiojjbLG3/0p9HhAqA==", "cpu": [ "arm" ], @@ -1577,9 +1589,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz", - "integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.3.tgz", + "integrity": "sha512-nSZfcZtAnQPRZmUkUQwZq2OjQciR6tEoJaZVFvLHsj0MF6QhNMg0fQ6mUOsiCUpTqxTx0/O6gX0V/nYc7LrgPw==", "cpu": [ "arm64" ], @@ -1589,9 +1601,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz", - "integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.3.tgz", + "integrity": "sha512-MnvSPGO8KJXIMGlQDYfvYS3IosFN2rKsvxRpPO2l2cum+Z3exiExLwVU+GExL96pn8IP+GdH8Tz70EpBhO0sIQ==", "cpu": [ "arm64" ], @@ -1601,11 +1613,11 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz", - "integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.3.tgz", + "integrity": "sha512-+W+p/9QNDr2vE2AXU0qIy0qQE75E8RTwTwgqS2G5CRQ11vzq0tbnfBd6brWhS9bCRjAjepJe2fvvkvS3dno+iw==", "cpu": [ - "ppc64le" + "ppc64" ], "optional": true, "os": [ @@ -1613,9 +1625,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz", - "integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.3.tgz", + "integrity": "sha512-yXH6K6KfqGXaxHrtr+Uoy+JpNlUlI46BKVyonGiaD74ravdnF9BUNC+vV+SIuB96hUMGShhKV693rF9QDfO6nQ==", "cpu": [ "riscv64" ], @@ -1625,9 +1637,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz", - "integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.3.tgz", + "integrity": "sha512-R8cwY9wcnApN/KDYWTH4gV/ypvy9yZUHlbJvfaiXSB48JO3KpwSpjOGqO4jnGkLDSk1hgjYkTbTt6Q7uvPf8eg==", "cpu": [ "s390x" ], @@ -1637,9 +1649,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz", - "integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.3.tgz", + "integrity": "sha512-kZPbX/NOPh0vhS5sI+dR8L1bU2cSO9FgxwM8r7wHzGydzfSjLRCFAT87GR5U9scj2rhzN3JPYVC7NoBbl4FZ0g==", "cpu": [ "x64" ], @@ -1649,9 +1661,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz", - "integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.3.tgz", + "integrity": "sha512-S0Yq+xA1VEH66uiMNhijsWAafffydd2X5b77eLHfRmfLsRSpbiAWiRHV6DEpz6aOToPsgid7TI9rGd6zB1rhbg==", "cpu": [ "x64" ], @@ -1661,9 +1673,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz", - "integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.3.tgz", + "integrity": "sha512-9isNzeL34yquCPyerog+IMCNxKR8XYmGd0tHSV+OVx0TmE0aJOo9uw4fZfUuk2qxobP5sug6vNdZR6u7Mw7Q+Q==", "cpu": [ "arm64" ], @@ -1673,9 +1685,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz", - "integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.3.tgz", + "integrity": "sha512-nMIdKnfZfzn1Vsk+RuOvl43ONTZXoAPUUxgcU0tXooqg4YrAqzfKzVenqqk2g5efWh46/D28cKFrOzDSW28gTA==", "cpu": [ "ia32" ], @@ -1685,9 +1697,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz", - "integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.3.tgz", + "integrity": "sha512-fOvu7PCQjAj4eWDEuD8Xz5gpzFqXzGlxHZozHP4b9Jxv9APtdxL6STqztDzMLuRXEc4UpXGGhx029Xgm91QBeA==", "cpu": [ "x64" ], @@ -5862,9 +5874,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -5917,9 +5929,9 @@ } }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "funding": [ { "type": "opencollective", @@ -5936,8 +5948,8 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -6301,9 +6313,9 @@ } }, "node_modules/rollup": { - "version": "4.14.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz", - "integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==", + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.3.tgz", + "integrity": "sha512-7sqRtBNnEbcBtMeRVc6VRsJMmpI+JU1z9VTvW8D4gXIYQFz0aLcsE6rRkyghZkLfEgUZgVvOG7A5CVz/VW5GIA==", "dependencies": { "@types/estree": "1.0.5" }, @@ -6315,21 +6327,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.14.0", - "@rollup/rollup-android-arm64": "4.14.0", - "@rollup/rollup-darwin-arm64": "4.14.0", - "@rollup/rollup-darwin-x64": "4.14.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.14.0", - "@rollup/rollup-linux-arm64-gnu": "4.14.0", - "@rollup/rollup-linux-arm64-musl": "4.14.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.14.0", - "@rollup/rollup-linux-riscv64-gnu": "4.14.0", - "@rollup/rollup-linux-s390x-gnu": "4.14.0", - "@rollup/rollup-linux-x64-gnu": "4.14.0", - "@rollup/rollup-linux-x64-musl": "4.14.0", - "@rollup/rollup-win32-arm64-msvc": "4.14.0", - "@rollup/rollup-win32-ia32-msvc": "4.14.0", - "@rollup/rollup-win32-x64-msvc": "4.14.0", + "@rollup/rollup-android-arm-eabi": "4.21.3", + "@rollup/rollup-android-arm64": "4.21.3", + "@rollup/rollup-darwin-arm64": "4.21.3", + "@rollup/rollup-darwin-x64": "4.21.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.21.3", + "@rollup/rollup-linux-arm-musleabihf": "4.21.3", + "@rollup/rollup-linux-arm64-gnu": "4.21.3", + "@rollup/rollup-linux-arm64-musl": "4.21.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.21.3", + "@rollup/rollup-linux-riscv64-gnu": "4.21.3", + "@rollup/rollup-linux-s390x-gnu": "4.21.3", + "@rollup/rollup-linux-x64-gnu": "4.21.3", + "@rollup/rollup-linux-x64-musl": "4.21.3", + "@rollup/rollup-win32-arm64-msvc": "4.21.3", + "@rollup/rollup-win32-ia32-msvc": "4.21.3", + "@rollup/rollup-win32-x64-msvc": "4.21.3", "fsevents": "~2.3.2" } }, @@ -6589,9 +6602,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "engines": { "node": ">=0.10.0" } @@ -7171,13 +7184,13 @@ } }, "node_modules/vite": { - "version": "5.2.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz", - "integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==", + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.6.tgz", + "integrity": "sha512-IeL5f8OO5nylsgzd9tq4qD2QqI0k2CQLGrWD0rCN0EQJZpBK5vJAx0I+GDkMOXxQX/OfFHMuLIx6ddAxGX/k+Q==", "dependencies": { - "esbuild": "^0.20.1", - "postcss": "^8.4.38", - "rollup": "^4.13.0" + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -7196,6 +7209,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -7213,6 +7227,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, @@ -7243,9 +7260,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", "cpu": [ "arm" ], @@ -7258,9 +7275,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", "cpu": [ "arm64" ], @@ -7273,9 +7290,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", "cpu": [ "x64" ], @@ -7288,9 +7305,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", "cpu": [ "arm64" ], @@ -7303,9 +7320,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", "cpu": [ "x64" ], @@ -7318,9 +7335,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", "cpu": [ "arm64" ], @@ -7333,9 +7350,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", "cpu": [ "x64" ], @@ -7348,9 +7365,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", "cpu": [ "arm" ], @@ -7363,9 +7380,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", "cpu": [ "arm64" ], @@ -7378,9 +7395,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", "cpu": [ "ia32" ], @@ -7393,9 +7410,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", "cpu": [ "loong64" ], @@ -7408,9 +7425,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", "cpu": [ "mips64el" ], @@ -7423,9 +7440,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", "cpu": [ "ppc64" ], @@ -7438,9 +7455,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", "cpu": [ "riscv64" ], @@ -7453,9 +7470,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", "cpu": [ "s390x" ], @@ -7468,9 +7485,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", "cpu": [ "x64" ], @@ -7483,9 +7500,9 @@ } }, "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", "cpu": [ "x64" ], @@ -7498,9 +7515,9 @@ } }, "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", "cpu": [ "x64" ], @@ -7513,9 +7530,9 @@ } }, "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", "cpu": [ "x64" ], @@ -7528,9 +7545,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", "cpu": [ "arm64" ], @@ -7543,9 +7560,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", "cpu": [ "ia32" ], @@ -7558,9 +7575,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", "cpu": [ "x64" ], @@ -7573,9 +7590,9 @@ } }, "node_modules/vite/node_modules/esbuild": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", "hasInstallScript": true, "bin": { "esbuild": "bin/esbuild" @@ -7584,29 +7601,29 @@ "node": ">=12" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.20.2", - "@esbuild/android-arm": "0.20.2", - "@esbuild/android-arm64": "0.20.2", - "@esbuild/android-x64": "0.20.2", - "@esbuild/darwin-arm64": "0.20.2", - "@esbuild/darwin-x64": "0.20.2", - "@esbuild/freebsd-arm64": "0.20.2", - "@esbuild/freebsd-x64": "0.20.2", - "@esbuild/linux-arm": "0.20.2", - "@esbuild/linux-arm64": "0.20.2", - "@esbuild/linux-ia32": "0.20.2", - "@esbuild/linux-loong64": "0.20.2", - "@esbuild/linux-mips64el": "0.20.2", - "@esbuild/linux-ppc64": "0.20.2", - "@esbuild/linux-riscv64": "0.20.2", - "@esbuild/linux-s390x": "0.20.2", - "@esbuild/linux-x64": "0.20.2", - "@esbuild/netbsd-x64": "0.20.2", - "@esbuild/openbsd-x64": "0.20.2", - "@esbuild/sunos-x64": "0.20.2", - "@esbuild/win32-arm64": "0.20.2", - "@esbuild/win32-ia32": "0.20.2", - "@esbuild/win32-x64": "0.20.2" + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" } }, "node_modules/vitefu": { diff --git a/package.json b/package.json index c095546c7d..511b17aa9e 100644 --- a/package.json +++ b/package.json @@ -100,7 +100,7 @@ "react-dom": "^18.1.0", "ssim.js": "^3.5.0", "typescript": "^5.5.3", - "vite": "^5.0.13", + "vite": "^5.4.6", "ws": "^8.17.1", "xml2js": "^0.5.0", "yaml": "^2.2.2" diff --git a/packages/playwright-core/src/cli/program.ts b/packages/playwright-core/src/cli/program.ts index d8fa8230c6..fb27b14231 100644 --- a/packages/playwright-core/src/cli/program.ts +++ b/packages/playwright-core/src/cli/program.ts @@ -567,10 +567,6 @@ async function codegen(options: Options & { target: string, output?: string, tes tracesDir, }); dotenv.config({ path: 'playwright.env' }); - if (process.env.PW_RECORDER_IS_TRACE_VIEWER) { - await fs.promises.mkdir(tracesDir, { recursive: true }); - await context.tracing.start({ name: 'trace', _live: true }); - } await context._enableRecorder({ language, launchOptions, diff --git a/packages/playwright-core/src/server/bidi/bidiConnection.ts b/packages/playwright-core/src/server/bidi/bidiConnection.ts index 7138f2e06a..f348815940 100644 --- a/packages/playwright-core/src/server/bidi/bidiConnection.ts +++ b/packages/playwright-core/src/server/bidi/bidiConnection.ts @@ -72,7 +72,7 @@ export class BidiConnection { let context; if ('context' in object.params) context = object.params.context; - else if (object.method === 'log.entryAdded') + else if (object.method === 'log.entryAdded' || object.method === 'script.message') context = object.params.source?.context; if (context) { const session = this._browsingContextToSession.get(context); diff --git a/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts b/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts index eaacb629e6..c037ba44b4 100644 --- a/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts +++ b/packages/playwright-core/src/server/bidi/bidiExecutionContext.ts @@ -23,7 +23,7 @@ import { BidiSerializer } from './third_party/bidiSerializer'; export class BidiExecutionContext implements js.ExecutionContextDelegate { private readonly _session: BidiSession; - private readonly _target: bidi.Script.Target; + readonly _target: bidi.Script.Target; constructor(session: BidiSession, realmInfo: bidi.Script.RealmInfo) { this._session = session; diff --git a/packages/playwright-core/src/server/bidi/bidiPage.ts b/packages/playwright-core/src/server/bidi/bidiPage.ts index c2d499bd67..180e8a651e 100644 --- a/packages/playwright-core/src/server/bidi/bidiPage.ts +++ b/packages/playwright-core/src/server/bidi/bidiPage.ts @@ -21,7 +21,8 @@ import type * as accessibility from '../accessibility'; import * as dom from '../dom'; import * as dialog from '../dialog'; import type * as frames from '../frames'; -import { type InitScript, Page, type PageDelegate } from '../page'; +import { Page } from '../page'; +import type { InitScript, PageDelegate } from '../page'; import type { Progress } from '../progress'; import type * as types from '../types'; import type { BidiBrowserContext } from './bidiBrowser'; @@ -33,6 +34,7 @@ import { BidiNetworkManager } from './bidiNetworkManager'; import { BrowserContext } from '../browserContext'; const UTILITY_WORLD_NAME = '__playwright_utility_world__'; +const kPlaywrightBindingChannel = 'playwrightChannel'; export class BidiPage implements PageDelegate { readonly rawMouse: RawMouseImpl; @@ -62,6 +64,7 @@ export class BidiPage implements PageDelegate { this._page.on(Page.Events.FrameDetached, (frame: frames.Frame) => this._removeContextsForFrame(frame, false)); this._sessionListeners = [ eventsHelper.addEventListener(bidiSession, 'script.realmCreated', this._onRealmCreated.bind(this)), + eventsHelper.addEventListener(bidiSession, 'script.message', this._onScriptMessage.bind(this)), eventsHelper.addEventListener(bidiSession, 'browsingContext.contextDestroyed', this._onBrowsingContextDestroyed.bind(this)), eventsHelper.addEventListener(bidiSession, 'browsingContext.navigationStarted', this._onNavigationStarted.bind(this)), eventsHelper.addEventListener(bidiSession, 'browsingContext.navigationAborted', this._onNavigationAborted.bind(this)), @@ -93,6 +96,7 @@ export class BidiPage implements PageDelegate { this.updateHttpCredentials(), this.updateRequestInterception(), this._updateViewport(), + this._installMainBinding(), this._addAllInitScripts(), ]); } @@ -315,18 +319,63 @@ export class BidiPage implements PageDelegate { }); } - goBack(): Promise { - throw new Error('Method not implemented.'); + async goBack(): Promise { + return await this._session.send('browsingContext.traverseHistory', { + context: this._session.sessionId, + delta: -1, + }).then(() => true).catch(() => false); } - goForward(): Promise { - throw new Error('Method not implemented.'); + async goForward(): Promise { + return await this._session.send('browsingContext.traverseHistory', { + context: this._session.sessionId, + delta: +1, + }).then(() => true).catch(() => false); } async forceGarbageCollection(): Promise { throw new Error('Method not implemented.'); } + // TODO: consider calling this only when bindings are added. + private async _installMainBinding() { + const functionDeclaration = addMainBinding.toString(); + const args: bidi.Script.ChannelValue[] = [{ + type: 'channel', + value: { + channel: kPlaywrightBindingChannel, + ownership: bidi.Script.ResultOwnership.Root, + } + }]; + const promises = []; + promises.push(this._session.send('script.addPreloadScript', { + functionDeclaration, + arguments: args, + })); + promises.push(this._session.send('script.callFunction', { + functionDeclaration, + arguments: args, + target: toBidiExecutionContext(await this._page.mainFrame()._mainContext())._target, + awaitPromise: false, + userActivation: false, + })); + await Promise.all(promises); + } + + private async _onScriptMessage(event: bidi.Script.MessageParameters) { + if (event.channel !== kPlaywrightBindingChannel) + return; + const pageOrError = await this.pageOrError(); + if (pageOrError instanceof Error) + return; + const context = this._realmToContext.get(event.source.realm); + if (!context) + return; + if (event.data.type !== 'string') + return; + await this._page._onBindingCalled(event.data.value, context); + } + async addInitScript(initScript: InitScript): Promise { const { script } = await this._session.send('script.addPreloadScript', { // TODO: remove function call from the source. @@ -355,7 +404,20 @@ export class BidiPage implements PageDelegate { } async takeScreenshot(progress: Progress, format: string, documentRect: types.Rect | undefined, viewportRect: types.Rect | undefined, quality: number | undefined, fitsViewport: boolean, scale: 'css' | 'device'): Promise { - throw new Error('Method not implemented.'); + const rect = (documentRect || viewportRect)!; + const { data } = await this._session.send('browsingContext.captureScreenshot', { + context: this._session.sessionId, + format: { + type: `image/${format === 'png' ? 'png' : 'jpeg'}`, + quality: quality || 80, + }, + origin: documentRect ? 'document' : 'viewport', + clip: { + type: 'box', + ...rect, + } + }); + return Buffer.from(data, 'base64'); } async getContentFrame(handle: dom.ElementHandle): Promise { @@ -522,6 +584,10 @@ export class BidiPage implements PageDelegate { } } +function addMainBinding(callback: (arg: any) => void) { + (globalThis as any)['__playwright__binding__'] = callback; +} + function toBidiExecutionContext(executionContext: dom.FrameExecutionContext): BidiExecutionContext { return (executionContext as any)[contextDelegateSymbol] as BidiExecutionContext; } diff --git a/packages/playwright-core/src/server/codegen/language.ts b/packages/playwright-core/src/server/codegen/language.ts index 4b1ba99b6f..7ee775b18b 100644 --- a/packages/playwright-core/src/server/codegen/language.ts +++ b/packages/playwright-core/src/server/codegen/language.ts @@ -20,7 +20,6 @@ import type * as types from '../types'; import type { ActionInContext, LanguageGenerator, LanguageGeneratorOptions } from './types'; export function generateCode(actions: ActionInContext[], languageGenerator: LanguageGenerator, options: LanguageGeneratorOptions) { - actions = collapseActions(actions); const header = languageGenerator.generateHeader(options); const footer = languageGenerator.generateFooter(options.saveStorage); const actionTexts = actions.map(a => languageGenerator.generateAction(a)).filter(Boolean); @@ -70,6 +69,23 @@ export function toKeyboardModifiers(modifiers: number): types.SmartKeyboardModif return result; } +export function fromKeyboardModifiers(modifiers?: types.SmartKeyboardModifier[]): number { + let result = 0; + if (!modifiers) + return result; + if (modifiers.includes('Alt')) + result |= 1; + if (modifiers.includes('Control')) + result |= 2; + if (modifiers.includes('ControlOrMeta')) + result |= 2; + if (modifiers.includes('Meta')) + result |= 4; + if (modifiers.includes('Shift')) + result |= 8; + return result; +} + export function toClickOptionsForSourceCode(action: actions.ClickAction): types.MouseClickOptions { const modifiers = toKeyboardModifiers(action.modifiers); const options: types.MouseClickOptions = {}; @@ -84,19 +100,3 @@ export function toClickOptionsForSourceCode(action: actions.ClickAction): types. options.position = action.position; return options; } - -function collapseActions(actions: ActionInContext[]): ActionInContext[] { - const result: ActionInContext[] = []; - for (const action of actions) { - const lastAction = result[result.length - 1]; - const isSameAction = lastAction && lastAction.action.name === action.action.name && lastAction.frame.pageAlias === action.frame.pageAlias && lastAction.frame.framePath.join('|') === action.frame.framePath.join('|'); - const isSameSelector = lastAction && 'selector' in lastAction.action && 'selector' in action.action && action.action.selector === lastAction.action.selector; - const shouldMerge = isSameAction && (action.action.name === 'navigate' || (action.action.name === 'fill' && isSameSelector)); - if (!shouldMerge) { - result.push(action); - continue; - } - result[result.length - 1] = action; - } - return result; -} diff --git a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts index 5c8fa550a7..c2d5d8e1f6 100644 --- a/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts +++ b/packages/playwright-core/src/server/dispatchers/browserContextDispatcher.ts @@ -41,6 +41,7 @@ import { serializeError } from '../errors'; import { ElementHandleDispatcher } from './elementHandlerDispatcher'; import { RecorderInTraceViewer } from '../recorder/recorderInTraceViewer'; import { RecorderApp } from '../recorder/recorderApp'; +import type { IRecorderAppFactory } from '../recorder/recorderFrontend'; export class BrowserContextDispatcher extends Dispatcher implements channels.BrowserContextChannel { _type_EventTarget = true; @@ -293,7 +294,20 @@ export class BrowserContextDispatcher extends Dispatcher { - const factory = process.env.PW_RECORDER_IS_TRACE_VIEWER ? RecorderInTraceViewer.factory(this._context) : RecorderApp.factory(this._context); + let factory: IRecorderAppFactory; + if (process.env.PW_RECORDER_IS_TRACE_VIEWER) { + factory = RecorderInTraceViewer.factory(this._context); + await this._context.tracing.start({ + name: 'trace', + snapshots: true, + screenshots: false, + live: true, + inMemory: true, + }); + await this._context.tracing.startChunk({ name: 'trace', title: 'trace' }); + } else { + factory = RecorderApp.factory(this._context); + } await Recorder.show(this._context, factory, params); } diff --git a/packages/playwright-core/src/server/fetch.ts b/packages/playwright-core/src/server/fetch.ts index 2aa7f27fee..6c14b625fe 100644 --- a/packages/playwright-core/src/server/fetch.ts +++ b/packages/playwright-core/src/server/fetch.ts @@ -41,6 +41,7 @@ import type * as types from './types'; import type { HeadersArray, ProxySettings } from './types'; import { getMatchingTLSOptionsForOrigin, rewriteOpenSSLErrorIfNeeded } from './socksClientCertificatesInterceptor'; import type * as har from '@trace/har'; +import { TLSSocket } from 'tls'; type FetchRequestOptions = { userAgent: string; @@ -73,6 +74,7 @@ export type APIRequestFinishedEvent = { statusMessage: string; body?: Buffer; timings: har.Timings; + securityDetails?: har.SecurityDetails; }; type SendRequestOptions = https.RequestOptions & { @@ -303,6 +305,8 @@ export abstract class APIRequestContext extends SdkObject { let tlsHandshakeAt: number | undefined; let requestFinishAt: number | undefined; + let securityDetails: har.SecurityDetails | undefined; + const request = requestConstructor(url, requestOptions as any, async response => { const responseAt = monotonicTime(); const notifyRequestFinished = (body?: Buffer) => { @@ -328,6 +332,7 @@ export abstract class APIRequestContext extends SdkObject { cookies, body, timings, + securityDetails, }; this.emit(APIRequestContext.Events.RequestFinished, requestFinishedEvent); }; @@ -482,7 +487,20 @@ export abstract class APIRequestContext extends SdkObject { // non-happy-eyeballs sockets socket.on('lookup', () => { dnsLookupAt = monotonicTime(); }); socket.on('connect', () => { tcpConnectionAt = monotonicTime(); }); - socket.on('secureConnect', () => { tlsHandshakeAt = monotonicTime(); }); + socket.on('secureConnect', () => { + tlsHandshakeAt = monotonicTime(); + + if (socket instanceof TLSSocket) { + const peerCertificate = socket.getPeerCertificate(); + securityDetails = { + protocol: socket.getProtocol() ?? undefined, + subjectName: peerCertificate.subject.CN, + validFrom: new Date(peerCertificate.valid_from).getTime() / 1000, + validTo: new Date(peerCertificate.valid_to).getTime() / 1000, + issuer: peerCertificate.issuer.CN + }; + } + }); }); request.on('finish', () => { requestFinishAt = monotonicTime(); }); diff --git a/packages/playwright-core/src/server/har/harTracer.ts b/packages/playwright-core/src/server/har/harTracer.ts index 288e32a310..448e511242 100644 --- a/packages/playwright-core/src/server/har/harTracer.ts +++ b/packages/playwright-core/src/server/har/harTracer.ts @@ -218,6 +218,9 @@ export class HarTracer { this._computeHarEntryTotalTime(harEntry); } + if (!this._options.omitSecurityDetails) + harEntry._securityDetails = event.securityDetails; + for (let i = 0; i < event.rawHeaders.length; i += 2) { harEntry.response.headers.push({ name: event.rawHeaders[i], diff --git a/packages/playwright-core/src/server/recorder/contextRecorder.ts b/packages/playwright-core/src/server/recorder/contextRecorder.ts index 71a1d3ec75..13f3ae1a62 100644 --- a/packages/playwright-core/src/server/recorder/contextRecorder.ts +++ b/packages/playwright-core/src/server/recorder/contextRecorder.ts @@ -73,7 +73,7 @@ export class ContextRecorder extends EventEmitter { saveStorage: params.saveStorage, }; - const collection = new RecorderCollection(this._pageAliases, params.mode === 'recording'); + const collection = new RecorderCollection(context, this._pageAliases, params.mode === 'recording'); collection.on('change', () => { this._recorderSources = []; for (const languageGenerator of this._orderedLanguages) { diff --git a/packages/playwright-core/src/server/recorder/recorderApp.ts b/packages/playwright-core/src/server/recorder/recorderApp.ts index 67d8b6e8dc..5e170b2e3e 100644 --- a/packages/playwright-core/src/server/recorder/recorderApp.ts +++ b/packages/playwright-core/src/server/recorder/recorderApp.ts @@ -43,6 +43,7 @@ declare global { } export class EmptyRecorderApp extends EventEmitter implements IRecorderApp { + wsEndpointForTest: undefined; async close(): Promise {} async setPaused(paused: boolean): Promise {} async setMode(mode: Mode): Promise {} @@ -54,7 +55,7 @@ export class EmptyRecorderApp extends EventEmitter implements IRecorderApp { export class RecorderApp extends EventEmitter implements IRecorderApp { private _page: Page; - readonly wsEndpoint: string | undefined; + readonly wsEndpointForTest: string | undefined; private _recorder: IRecorder; constructor(recorder: IRecorder, page: Page, wsEndpoint: string | undefined) { @@ -62,7 +63,7 @@ export class RecorderApp extends EventEmitter implements IRecorderApp { this.setMaxListeners(0); this._recorder = recorder; this._page = page; - this.wsEndpoint = wsEndpoint; + this.wsEndpointForTest = wsEndpoint; } async close() { diff --git a/packages/playwright-core/src/server/recorder/recorderCollection.ts b/packages/playwright-core/src/server/recorder/recorderCollection.ts index e9c2b31427..e67865a2dc 100644 --- a/packages/playwright-core/src/server/recorder/recorderCollection.ts +++ b/packages/playwright-core/src/server/recorder/recorderCollection.ts @@ -20,31 +20,35 @@ import type { Page } from '../page'; import type { Signal } from './recorderActions'; import type { ActionInContext } from '../codegen/types'; import { monotonicTime } from '../../utils/time'; -import { callMetadataForAction } from './recorderUtils'; +import { callMetadataForAction, collapseActions, traceEventsToAction } from './recorderUtils'; import { serializeError } from '../errors'; import { performAction } from './recorderRunner'; import type { CallMetadata } from '@protocol/callMetadata'; import { isUnderTest } from '../../utils/debug'; +import type { BrowserContext } from '../browserContext'; export class RecorderCollection extends EventEmitter { private _actions: ActionInContext[] = []; private _enabled: boolean; private _pageAliases: Map; + private _context: BrowserContext; - constructor(pageAliases: Map, enabled: boolean) { + constructor(context: BrowserContext, pageAliases: Map, enabled: boolean) { super(); + this._context = context; this._enabled = enabled; this._pageAliases = pageAliases; - this.restart(); } restart() { this._actions = []; - this.emit('change'); + this._fireChange(); } actions() { - return this._actions; + if (!process.env.PW_RECORDER_IS_TRACE_VIEWER) + return collapseActions(this._actions); + return collapseActions(traceEventsToAction(this._context.tracing.inMemoryEvents())); } setEnabled(enabled: boolean) { @@ -60,7 +64,7 @@ export class RecorderCollection extends EventEmitter { addRecordedAction(actionInContext: ActionInContext) { if (['openPage', 'closePage'].includes(actionInContext.action.name)) { this._actions.push(actionInContext); - this.emit('change'); + this._fireChange(); return; } this._addAction(actionInContext).catch(() => {}); @@ -69,11 +73,16 @@ export class RecorderCollection extends EventEmitter { private async _addAction(actionInContext: ActionInContext, callback?: (callMetadata: CallMetadata) => Promise) { if (!this._enabled) return; + if (actionInContext.action.name === 'openPage' || actionInContext.action.name === 'closePage') { + this._actions.push(actionInContext); + this._fireChange(); + return; + } const { callMetadata, mainFrame } = callMetadataForAction(this._pageAliases, actionInContext); await mainFrame.instrumentation.onBeforeCall(mainFrame, callMetadata); this._actions.push(actionInContext); - this.emit('change'); + this._fireChange(); const error = await callback?.(callMetadata).catch((e: Error) => e); callMetadata.endTime = monotonicTime(); callMetadata.error = error ? serializeError(error) : undefined; @@ -120,4 +129,8 @@ export class RecorderCollection extends EventEmitter { return; } } + + private _fireChange() { + this.emit('change'); + } } diff --git a/packages/playwright-core/src/server/recorder/recorderFrontend.ts b/packages/playwright-core/src/server/recorder/recorderFrontend.ts index 162c9f9964..d2cdffdca4 100644 --- a/packages/playwright-core/src/server/recorder/recorderFrontend.ts +++ b/packages/playwright-core/src/server/recorder/recorderFrontend.ts @@ -23,6 +23,7 @@ export interface IRecorder { } export interface IRecorderApp extends EventEmitter { + readonly wsEndpointForTest: string | undefined; close(): Promise; setPaused(paused: boolean): Promise; setMode(mode: Mode): Promise; diff --git a/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts b/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts index a9fd766141..8f08b969e1 100644 --- a/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts +++ b/packages/playwright-core/src/server/recorder/recorderInTraceViewer.ts @@ -25,6 +25,7 @@ import { gracefullyProcessExitDoNotHang } from '../../utils/processLauncher'; import type { Transport } from '../../utils/httpServer'; export class RecorderInTraceViewer extends EventEmitter implements IRecorderApp { + readonly wsEndpointForTest: string | undefined; private _recorder: IRecorder; private _transport: Transport; @@ -32,15 +33,16 @@ export class RecorderInTraceViewer extends EventEmitter implements IRecorderApp return async (recorder: IRecorder) => { const transport = new RecorderTransport(); const trace = path.join(context._browser.options.tracesDir, 'trace'); - await openApp(trace, { transport }); - return new RecorderInTraceViewer(context, recorder, transport); + const wsEndpointForTest = await openApp(trace, { transport, headless: !context._browser.options.headful }); + return new RecorderInTraceViewer(context, recorder, transport, wsEndpointForTest); }; } - constructor(context: BrowserContext, recorder: IRecorder, transport: Transport) { + constructor(context: BrowserContext, recorder: IRecorder, transport: Transport, wsEndpointForTest: string | undefined) { super(); this._recorder = recorder; this._transport = transport; + this.wsEndpointForTest = wsEndpointForTest; } async close(): Promise { @@ -72,11 +74,12 @@ export class RecorderInTraceViewer extends EventEmitter implements IRecorderApp } } -async function openApp(trace: string, options?: TraceViewerServerOptions & { headless?: boolean }) { +async function openApp(trace: string, options?: TraceViewerServerOptions & { headless?: boolean }): Promise { const server = await startTraceViewerServer(options); await installRootRedirect(server, [trace], { ...options, webApp: 'recorder.html' }); const page = await openTraceViewerApp(server.urlPrefix('precise'), 'chromium', options); page.on('close', () => gracefullyProcessExitDoNotHang(0)); + return page.context()._browser.options.wsEndpoint; } class RecorderTransport implements Transport { diff --git a/packages/playwright-core/src/server/recorder/recorderUtils.ts b/packages/playwright-core/src/server/recorder/recorderUtils.ts index ac6c970489..27c212c6e5 100644 --- a/packages/playwright-core/src/server/recorder/recorderUtils.ts +++ b/packages/playwright-core/src/server/recorder/recorderUtils.ts @@ -20,9 +20,12 @@ import type { Page } from '../page'; import type { ActionInContext } from '../codegen/types'; import type { Frame } from '../frames'; import type * as actions from './recorderActions'; -import { toKeyboardModifiers } from '../codegen/language'; +import type * as channels from '@protocol/channels'; +import type * as trace from '@trace/trace'; +import { fromKeyboardModifiers, toKeyboardModifiers } from '../codegen/language'; import { serializeExpectedTextValues } from '../../utils/expectUtils'; import { createGuid, monotonicTime } from '../../utils'; +import { serializeValue } from '../../protocol/serializers'; export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus): CallLog { let title = metadata.apiName || metadata.method; @@ -76,57 +79,113 @@ export async function frameForAction(pageAliases: Map, actionInCon return result.frame; } -export function traceParamsForAction(actionInContext: ActionInContext) { +export function traceParamsForAction(actionInContext: ActionInContext): { method: string, params: any } { const { action } = actionInContext; switch (action.name) { - case 'navigate': return { url: action.url }; - case 'openPage': return {}; - case 'closePage': return {}; + case 'navigate': { + const params: channels.FrameGotoParams = { + url: action.url, + }; + return { method: 'goto', params }; + } + case 'openPage': + case 'closePage': + throw new Error('Not reached'); } const selector = buildFullSelector(actionInContext.frame.framePath, action.selector); switch (action.name) { - case 'click': return { selector, clickCount: action.clickCount }; - case 'press': { - const modifiers = toKeyboardModifiers(action.modifiers); - const shortcut = [...modifiers, action.key].join('+'); - return { selector, key: shortcut }; - } - case 'fill': return { selector, text: action.text }; - case 'setInputFiles': return { selector, files: action.files }; - case 'check': return { selector }; - case 'uncheck': return { selector }; - case 'select': return { selector, values: action.options.map(value => ({ value })) }; - case 'assertChecked': { - return { + case 'click': { + const params: channels.FrameClickParams = { selector, - expression: 'to.be.checked', - isNot: !action.checked, + strict: true, + modifiers: toKeyboardModifiers(action.modifiers), + button: action.button, + clickCount: action.clickCount, + position: action.position, }; + return { method: 'click', params }; + } + case 'press': { + const params: channels.FramePressParams = { + selector, + strict: true, + key: [...toKeyboardModifiers(action.modifiers), action.key].join('+'), + }; + return { method: 'press', params }; + } + case 'fill': { + const params: channels.FrameFillParams = { + selector, + strict: true, + value: action.text, + }; + return { method: 'fill', params }; + } + case 'setInputFiles': { + const params: channels.FrameSetInputFilesParams = { + selector, + strict: true, + localPaths: action.files, + }; + return { method: 'setInputFiles', params }; + } + case 'check': { + const params: channels.FrameCheckParams = { + selector, + strict: true, + }; + return { method: 'check', params }; + } + case 'uncheck': { + const params: channels.FrameUncheckParams = { + selector, + strict: true, + }; + return { method: 'uncheck', params }; + } + case 'select': { + const params: channels.FrameSelectOptionParams = { + selector, + strict: true, + options: action.options.map(option => ({ value: option })), + }; + return { method: 'selectOption', params }; + } + case 'assertChecked': { + const params: channels.FrameExpectParams = { + selector: action.selector, + expression: 'to.be.checked', + isNot: action.checked, + }; + return { method: 'expect', params }; } case 'assertText': { - return { + const params: channels.FrameExpectParams = { selector, expression: 'to.have.text', expectedText: serializeExpectedTextValues([action.text], { matchSubstring: true, normalizeWhiteSpace: true }), isNot: false, }; + return { method: 'expect', params }; } case 'assertValue': { - return { + const params: channels.FrameExpectParams = { selector, expression: 'to.have.value', - expectedValue: action.value, + expectedValue: { value: serializeValue(action.value, value => ({ fallThrough: value })), handles: [] }, isNot: false, }; + return { method: 'expect', params }; } case 'assertVisible': { - return { + const params: channels.FrameExpectParams = { selector, expression: 'to.be.visible', isNot: false, }; + return { method: 'expect', params }; } } } @@ -134,8 +193,10 @@ export function traceParamsForAction(actionInContext: ActionInContext) { export function callMetadataForAction(pageAliases: Map, actionInContext: ActionInContext): { callMetadata: CallMetadata, mainFrame: Frame } { const mainFrame = mainFrameForAction(pageAliases, actionInContext); const { action } = actionInContext; + const { method, params } = traceParamsForAction(actionInContext); const callMetadata: CallMetadata = { id: `call@${createGuid()}`, + stepId: `recorder@${createGuid()}`, apiName: 'frame.' + action.name, objectId: mainFrame.guid, pageId: mainFrame._page.guid, @@ -143,9 +204,132 @@ export function callMetadataForAction(pageAliases: Map, actionInCo startTime: monotonicTime(), endTime: 0, type: 'Frame', - method: action.name, - params: traceParamsForAction(actionInContext), + method, + params, log: [], }; return { callMetadata, mainFrame }; } + +export function traceEventsToAction(events: trace.TraceEvent[]): ActionInContext[] { + const result: ActionInContext[] = []; + for (const event of events) { + if (event.type !== 'before') + continue; + if (!event.stepId?.startsWith('recorder@')) + continue; + + if (event.method === 'goto') { + result.push({ + frame: { pageAlias: 'page', framePath: [] }, + action: { + name: 'navigate', + url: event.params.url, + signals: [], + }, + timestamp: event.startTime, + }); + continue; + } + if (event.method === 'click') { + result.push({ + frame: { pageAlias: 'page', framePath: [] }, + action: { + name: 'click', + selector: event.params.selector, + signals: [], + button: event.params.button, + modifiers: fromKeyboardModifiers(event.params.modifiers), + clickCount: event.params.clickCount, + position: event.params.position, + }, + timestamp: event.startTime + }); + continue; + } + if (event.method === 'fill') { + result.push({ + frame: { pageAlias: 'page', framePath: [] }, + action: { + name: 'fill', + selector: event.params.selector, + signals: [], + text: event.params.value, + }, + timestamp: event.startTime + }); + continue; + } + if (event.method === 'press') { + const tokens = event.params.key.split('+'); + const modifiers = tokens.slice(0, tokens.length - 1); + const key = tokens[tokens.length - 1]; + result.push({ + frame: { pageAlias: 'page', framePath: [] }, + action: { + name: 'press', + selector: event.params.selector, + signals: [], + key, + modifiers: fromKeyboardModifiers(modifiers), + }, + timestamp: event.startTime + }); + continue; + } + if (event.method === 'check') { + result.push({ + frame: { pageAlias: 'page', framePath: [] }, + action: { + name: 'check', + selector: event.params.selector, + signals: [], + }, + timestamp: event.startTime + }); + continue; + } + if (event.method === 'uncheck') { + result.push({ + frame: { pageAlias: 'page', framePath: [] }, + action: { + name: 'uncheck', + selector: event.params.selector, + signals: [], + }, + timestamp: event.startTime + }); + continue; + } + if (event.method === 'selectOption') { + result.push({ + frame: { pageAlias: 'page', framePath: [] }, + action: { + name: 'select', + selector: event.params.selector, + signals: [], + options: event.params.options.map((option: any) => option.value), + }, + timestamp: event.startTime + }); + continue; + } + } + return result; +} + +export function collapseActions(actions: ActionInContext[]): ActionInContext[] { + const result: ActionInContext[] = []; + for (const action of actions) { + const lastAction = result[result.length - 1]; + const isSameAction = lastAction && lastAction.action.name === action.action.name && lastAction.frame.pageAlias === action.frame.pageAlias && lastAction.frame.framePath.join('|') === action.frame.framePath.join('|'); + const isSameSelector = lastAction && 'selector' in lastAction.action && 'selector' in action.action && action.action.selector === lastAction.action.selector; + const shouldMerge = isSameAction && (action.action.name === 'navigate' || (action.action.name === 'fill' && isSameSelector)); + if (!shouldMerge) { + result.push(action); + continue; + } + result[result.length - 1] = action; + } + return result; +} diff --git a/packages/playwright-core/src/server/trace/recorder/tracing.ts b/packages/playwright-core/src/server/trace/recorder/tracing.ts index b09bbe3134..25437e53a2 100644 --- a/packages/playwright-core/src/server/trace/recorder/tracing.ts +++ b/packages/playwright-core/src/server/trace/recorder/tracing.ts @@ -46,6 +46,7 @@ export type TracerOptions = { snapshots?: boolean; screenshots?: boolean; live?: boolean; + inMemory?: boolean; }; type RecordingState = { @@ -79,6 +80,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps private _allResources = new Set(); private _contextCreatedEvent: trace.ContextCreatedTraceEvent; private _pendingHarEntries = new Set(); + private _inMemoryEvents: trace.TraceEvent[] | undefined; constructor(context: BrowserContext | APIRequestContext, tracesDir: string | undefined) { super(context, 'tracing'); @@ -153,6 +155,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps // Tracing is 10x bigger if we include scripts in every trace. if (options.snapshots) this._harTracer.start({ omitScripts: !options.live }); + this._inMemoryEvents = options.inMemory ? [] : undefined; } async startChunk(options: { name?: string, title?: string } = {}): Promise<{ traceName: string }> { @@ -179,7 +182,7 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps wallTime: Date.now(), monotonicTime: monotonicTime() }; - this._fs.appendFile(this._state.traceFile, JSON.stringify(event) + '\n'); + this._appendTraceEvent(event); this._context.instrumentation.addListener(this, this._context); this._eventListeners.push( @@ -193,6 +196,10 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps return { traceName: this._state.traceName }; } + inMemoryEvents(): trace.TraceEvent[] { + return this._inMemoryEvents || []; + } + private _startScreencast() { if (!(this._context instanceof BrowserContext)) return; @@ -487,6 +494,8 @@ export class Tracing extends SdkObject implements InstrumentationListener, Snaps // Do not flush (console) events, they are too noisy, unless we are in ui mode (live). const flush = this._state!.options.live || (event.type !== 'event' && event.type !== 'console' && event.type !== 'log'); this._fs.appendFile(this._state!.traceFile, JSON.stringify(visited) + '\n', flush); + if (this._inMemoryEvents) + this._inMemoryEvents.push(event); } private _appendResource(sha1: string, buffer: Buffer) { diff --git a/packages/playwright-core/src/server/trace/viewer/traceViewer.ts b/packages/playwright-core/src/server/trace/viewer/traceViewer.ts index 6ca0319aa3..78848142a8 100644 --- a/packages/playwright-core/src/server/trace/viewer/traceViewer.ts +++ b/packages/playwright-core/src/server/trace/viewer/traceViewer.ts @@ -178,6 +178,7 @@ export async function openTraceViewerApp(url: string, browserName: string, optio ...options?.persistentContextOptions, useWebSocket: isUnderTest(), headless: !!options?.headless, + args: process.env.PWTEST_RECORDER_PORT ? [`--remote-debugging-port=${process.env.PWTEST_RECORDER_PORT}`] : [], }, }); diff --git a/packages/playwright/src/common/testType.ts b/packages/playwright/src/common/testType.ts index 5c7850a3df..f0882735dc 100644 --- a/packages/playwright/src/common/testType.ts +++ b/packages/playwright/src/common/testType.ts @@ -259,11 +259,11 @@ export class TestTypeImpl { suite._use.push({ fixtures, location }); } - async _step(title: string, body: () => Promise, options: { box?: boolean } = {}): Promise { + async _step(title: string, body: () => Promise, options: {box?: boolean, location?: Location } = {}): Promise { const testInfo = currentTestInfo(); if (!testInfo) throw new Error(`test.step() can only be called from a test`); - const step = testInfo._addStep({ category: 'test.step', title, box: options.box }); + const step = testInfo._addStep({ category: 'test.step', title, location: options.location, box: options.box }); return await zones.run('stepZone', step, async () => { try { const result = await body(); diff --git a/packages/playwright/src/reporters/html.ts b/packages/playwright/src/reporters/html.ts index fd1fb0cbdf..5aada7e495 100644 --- a/packages/playwright/src/reporters/html.ts +++ b/packages/playwright/src/reporters/html.ts @@ -143,7 +143,7 @@ class HtmlReporter implements ReporterV2 { const shouldOpen = !this._options._isTestServer && (this._open === 'always' || (!ok && this._open === 'on-failure')); if (shouldOpen) { await showHTMLReport(this._outputFolder, this._host, this._port, singleTestId); - } else if (this._options._mode === 'test') { + } else if (this._options._mode === 'test' && !this._options._isTestServer) { const packageManagerCommand = getPackageManagerExecCommand(); const relativeReportPath = this._outputFolder === standaloneDefaultFolder() ? '' : ' ' + path.relative(process.cwd(), this._outputFolder); const hostArg = this._host ? ` --host ${this._host}` : ''; diff --git a/packages/playwright/types/test.d.ts b/packages/playwright/types/test.d.ts index e17a43843c..51a6720a2e 100644 --- a/packages/playwright/types/test.d.ts +++ b/packages/playwright/types/test.d.ts @@ -4703,7 +4703,7 @@ export interface TestType(title: string, body: () => T | Promise, options?: { box?: boolean }): Promise; + step(title: string, body: () => T | Promise, options?: { box?: boolean, location?: Location }): Promise; /** * `expect` function can be used to create test assertions. Read more about [test assertions](https://playwright.dev/docs/test-assertions). * diff --git a/packages/trace-viewer/src/traceModel.ts b/packages/trace-viewer/src/traceModel.ts index 1248dde967..87a3d42491 100644 --- a/packages/trace-viewer/src/traceModel.ts +++ b/packages/trace-viewer/src/traceModel.ts @@ -73,6 +73,7 @@ export class TraceModel { unzipProgress(++done, total); contextEntry.actions = modernizer.actions().sort((a1, a2) => a1.startTime - a2.startTime); + if (!backend.isLive()) { // Terminate actions w/o after event gracefully. // This would close after hooks event that has not been closed because diff --git a/packages/trace-viewer/src/ui/modelUtil.ts b/packages/trace-viewer/src/ui/modelUtil.ts index a544d4dc3f..098b387c8f 100644 --- a/packages/trace-viewer/src/ui/modelUtil.ts +++ b/packages/trace-viewer/src/ui/modelUtil.ts @@ -312,7 +312,7 @@ function monotonicTimeDeltaBetweenLibraryAndRunner(nonPrimaryContexts: ContextEn for (const action of context.actions) { if (!action.startTime) continue; - const key = matchByStepId ? action.stepId! : `${action.apiName}@${(action as any).wallTime}`; + const key = matchByStepId ? action.callId! : `${action.apiName}@${(action as any).wallTime}`; const libraryAction = libraryActions.get(key); if (libraryAction) return action.startTime - libraryAction.startTime; diff --git a/packages/trace-viewer/src/ui/recorderView.tsx b/packages/trace-viewer/src/ui/recorderView.tsx index 940fd146a9..4d8ec8d297 100644 --- a/packages/trace-viewer/src/ui/recorderView.tsx +++ b/packages/trace-viewer/src/ui/recorderView.tsx @@ -45,6 +45,8 @@ export const RecorderView: React.FunctionComponent = () => { connection.setMode('recording'); }, [connection]); + window.playwrightSourcesEchoForTest = sources; + return
{ @@ -845,6 +848,8 @@ it('should respect minimal mode for API Requests', async ({ contextFactory, serv expect(entries).toHaveLength(1); const [entry] = entries; expect(entry.timings).toEqual({ receive: -1, send: -1, wait: -1 }); + expect(entry.request.cookies).toEqual([]); + expect(entry.request.bodySize).toBe(-1); expect(entry.response.bodySize).toBe(-1); }); diff --git a/tests/library/inspector/inspectorTest.ts b/tests/library/inspector/inspectorTest.ts index 02acdeb6fd..5e39d29261 100644 --- a/tests/library/inspector/inspectorTest.ts +++ b/tests/library/inspector/inspectorTest.ts @@ -55,7 +55,7 @@ export const test = contextTest.extend({ await run(async () => { while (!toImpl(context).recorderAppForTest) await new Promise(f => setTimeout(f, 100)); - const wsEndpoint = toImpl(context).recorderAppForTest.wsEndpoint; + const wsEndpoint = toImpl(context).recorderAppForTest.wsEndpointForTest; const browser = await playwrightToAutomateInspector.chromium.connectOverCDP({ wsEndpoint }); const c = browser.contexts()[0]; return c.pages()[0] || await c.waitForEvent('page'); diff --git a/tests/library/trace-viewer.spec.ts b/tests/library/trace-viewer.spec.ts index 2b00949b19..44c1258eb7 100644 --- a/tests/library/trace-viewer.spec.ts +++ b/tests/library/trace-viewer.spec.ts @@ -127,6 +127,12 @@ test('should complain about newer version of trace in old viewer', async ({ show await expect(traceViewer.page.getByText('The trace was created by a newer version of Playwright and is not supported by this version of the viewer.')).toBeVisible(); }); +test('should properly synchronize local and remote time', async ({ showTraceViewer, asset }, testInfo) => { + const traceViewer = await showTraceViewer([asset('trace-remote-time-diff.zip')]); + // The total duration should be sub 10s, rather than 16h. + await expect(traceViewer.page.locator('.timeline-time').last()).toHaveText('8.5s'); +}); + test('should contain action info', async ({ showTraceViewer }) => { const traceViewer = await showTraceViewer([traceFile]); await traceViewer.selectAction('locator.click'); diff --git a/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-bounding-box-bidi-chromium.png b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-bounding-box-bidi-chromium.png new file mode 100644 index 0000000000..c2c3ddca29 Binary files /dev/null and b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-bounding-box-bidi-chromium.png differ diff --git a/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-fractional-bidi-chromium.png b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-fractional-bidi-chromium.png new file mode 100644 index 0000000000..35c53377f9 Binary files /dev/null and b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-fractional-bidi-chromium.png differ diff --git a/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-fractional-offset-bidi-chromium.png b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-fractional-offset-bidi-chromium.png new file mode 100644 index 0000000000..971561ba69 Binary files /dev/null and b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-fractional-offset-bidi-chromium.png differ diff --git a/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-larger-than-viewport-bidi-chromium.png b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-larger-than-viewport-bidi-chromium.png new file mode 100644 index 0000000000..52c1bb2b92 Binary files /dev/null and b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-larger-than-viewport-bidi-chromium.png differ diff --git a/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-padding-border-bidi-chromium.png b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-padding-border-bidi-chromium.png new file mode 100644 index 0000000000..fc34319896 Binary files /dev/null and b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-padding-border-bidi-chromium.png differ diff --git a/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-rotate-bidi-chromium.png b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-rotate-bidi-chromium.png new file mode 100644 index 0000000000..5aab14c4b9 Binary files /dev/null and b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-rotate-bidi-chromium.png differ diff --git a/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-scrolled-into-view-bidi-chromium.png b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-scrolled-into-view-bidi-chromium.png new file mode 100644 index 0000000000..fc34319896 Binary files /dev/null and b/tests/page/elementhandle-screenshot.spec.ts-snapshots/screenshot-element-scrolled-into-view-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/canvas-changes-0-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/canvas-changes-0-bidi-chromium.png new file mode 100644 index 0000000000..6fcce2cf53 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/canvas-changes-0-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/canvas-changes-1-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/canvas-changes-1-bidi-chromium.png new file mode 100644 index 0000000000..f6f130a44a Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/canvas-changes-1-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/canvas-changes-2-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/canvas-changes-2-bidi-chromium.png new file mode 100644 index 0000000000..ecabd8b22d Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/canvas-changes-2-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/grid-cell-1-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/grid-cell-1-bidi-chromium.png new file mode 100644 index 0000000000..2eab11668d Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/grid-cell-1-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/hide-should-work-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/hide-should-work-bidi-chromium.png new file mode 100644 index 0000000000..dd0549a411 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/hide-should-work-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/mask-color-should-work-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/mask-color-should-work-bidi-chromium.png new file mode 100644 index 0000000000..fef9e17ffe Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/mask-color-should-work-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/mask-should-work-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/mask-should-work-bidi-chromium.png new file mode 100644 index 0000000000..c663e342dc Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/mask-should-work-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/mask-should-work-with-elementhandle-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/mask-should-work-with-elementhandle-bidi-chromium.png new file mode 100644 index 0000000000..5a13aac48b Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/mask-should-work-with-elementhandle-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/mask-should-work-with-locator-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/mask-should-work-with-locator-bidi-chromium.png new file mode 100644 index 0000000000..5a13aac48b Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/mask-should-work-with-locator-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/page-screenshot-should-capture-css-transform-1-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/page-screenshot-should-capture-css-transform-1-bidi-chromium.png new file mode 100644 index 0000000000..d29a2b4ab2 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/page-screenshot-should-capture-css-transform-1-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/remove-should-work-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/remove-should-work-bidi-chromium.png new file mode 100644 index 0000000000..d35ddd4289 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/remove-should-work-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/screenshot-canvas-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-canvas-bidi-chromium.png new file mode 100644 index 0000000000..dfafbda638 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-canvas-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/screenshot-canvas-text-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-canvas-text-bidi-chromium.png new file mode 100644 index 0000000000..0971000b5f Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-canvas-text-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/screenshot-clip-odd-size-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-clip-odd-size-bidi-chromium.png new file mode 100644 index 0000000000..61c0b7044a Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-clip-odd-size-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/screenshot-clip-rect-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-clip-rect-bidi-chromium.png new file mode 100644 index 0000000000..8c4046910b Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-clip-rect-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/screenshot-grid-fullpage-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-grid-fullpage-bidi-chromium.png new file mode 100644 index 0000000000..0354694da1 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-grid-fullpage-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/screenshot-grid-fullpage-mask-outside-viewport-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-grid-fullpage-mask-outside-viewport-bidi-chromium.png new file mode 100644 index 0000000000..9e97bfcedb Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-grid-fullpage-mask-outside-viewport-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/screenshot-iframe-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-iframe-bidi-chromium.png new file mode 100644 index 0000000000..56aa8706bb Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-iframe-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/screenshot-offscreen-clip-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-offscreen-clip-bidi-chromium.png new file mode 100644 index 0000000000..21f7b0f461 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-offscreen-clip-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/screenshot-sanity-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-sanity-bidi-chromium.png new file mode 100644 index 0000000000..122a4f0ae0 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-sanity-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/screenshot-translateZ-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-translateZ-bidi-chromium.png new file mode 100644 index 0000000000..e3f5642e60 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-translateZ-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/screenshot-web-font-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-web-font-bidi-chromium.png new file mode 100644 index 0000000000..40aa6d483c Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-web-font-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/screenshot-webgl-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-webgl-bidi-chromium.png new file mode 100644 index 0000000000..76109512b5 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/screenshot-webgl-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/should-capture-css-box-shadow-1-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/should-capture-css-box-shadow-1-bidi-chromium.png new file mode 100644 index 0000000000..089549eb3c Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/should-capture-css-box-shadow-1-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/should-mask-in-parallel-1-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/should-mask-in-parallel-1-bidi-chromium.png new file mode 100644 index 0000000000..18f4ed1e60 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/should-mask-in-parallel-1-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/should-mask-in-parallel-2-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/should-mask-in-parallel-2-bidi-chromium.png new file mode 100644 index 0000000000..bf3dbe65bd Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/should-mask-in-parallel-2-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/should-mask-inside-iframe-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/should-mask-inside-iframe-bidi-chromium.png new file mode 100644 index 0000000000..c663e342dc Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/should-mask-inside-iframe-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/should-mask-multiple-elements-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/should-mask-multiple-elements-bidi-chromium.png new file mode 100644 index 0000000000..0ff0cf5137 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/should-mask-multiple-elements-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/transparent-bidi-chromium.png b/tests/page/page-screenshot.spec.ts-snapshots/transparent-bidi-chromium.png new file mode 100644 index 0000000000..b9aa8f7a72 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/transparent-bidi-chromium.png differ diff --git a/tests/page/page-screenshot.spec.ts-snapshots/white-bidi-chromium.jpg b/tests/page/page-screenshot.spec.ts-snapshots/white-bidi-chromium.jpg new file mode 100644 index 0000000000..ed816ce766 Binary files /dev/null and b/tests/page/page-screenshot.spec.ts-snapshots/white-bidi-chromium.jpg differ diff --git a/tests/playwright-test/test-step.spec.ts b/tests/playwright-test/test-step.spec.ts index d14bccd98b..ad3478b112 100644 --- a/tests/playwright-test/test-step.spec.ts +++ b/tests/playwright-test/test-step.spec.ts @@ -1246,3 +1246,42 @@ fixture | fixture: page fixture | fixture: context `); }); + +test('test location to test.step', async ({ runInlineTest }) => { + const result = await runInlineTest({ + 'reporter.ts': stepIndentReporter, + 'helper.ts': ` + import { test } from '@playwright/test'; + + export async function dummyStep(test, title, action, location) { + return await test.step(title, action, { location }); + } + + export function getCustomLocation() { + return { file: 'dummy-file.ts', line: 123, column: 45 }; + } + `, + 'playwright.config.ts': ` + module.exports = { + reporter: './reporter', + }; + `, + 'a.test.ts': ` + import { test } from '@playwright/test'; + import { dummyStep, getCustomLocation } from './helper'; + + test('custom location test', async () => { + const location = getCustomLocation(); + await dummyStep(test, 'Perform a dummy step', async () => { + }, location); + }); + ` + }, { reporter: '', workers: 1 }); + + expect(result.exitCode).toBe(0); + expect(stripAnsi(result.output)).toBe(` +hook |Before Hooks +test.step |Perform a dummy step @ dummy-file.ts:123 +hook |After Hooks +`); +}); \ No newline at end of file diff --git a/tests/playwright-test/watch.spec.ts b/tests/playwright-test/watch.spec.ts index b56fb470ea..4f687f72fc 100644 --- a/tests/playwright-test/watch.spec.ts +++ b/tests/playwright-test/watch.spec.ts @@ -215,6 +215,24 @@ test('should run tests on Enter', async ({ runWatchTest }) => { await testProcess.waitForOutput('Waiting for file changes.'); }); +test('should not print show-report command of HTML reporter', async ({ runWatchTest }) => { + const testProcess = await runWatchTest({ + 'a.test.ts': ` + import { test, expect } from '@playwright/test'; + test('passes', () => {}); + `, + 'playwright.config.ts': ` + import { defineConfig } from '@playwright/test'; + export default defineConfig({ reporter: 'html' }); + `, + }); + await testProcess.waitForOutput('Waiting for file changes.'); + testProcess.clearOutput(); + testProcess.write('\r\n'); + await testProcess.waitForOutput('Waiting for file changes.'); + expect(testProcess.output).not.toContain('To open last HTML report run:'); +}); + test('should run tests on R', async ({ runWatchTest }) => { const testProcess = await runWatchTest({ 'a.test.ts': ` diff --git a/utils/generate_types/overrides-test.d.ts b/utils/generate_types/overrides-test.d.ts index 90ef7fa75a..be1fa7ee37 100644 --- a/utils/generate_types/overrides-test.d.ts +++ b/utils/generate_types/overrides-test.d.ts @@ -128,7 +128,7 @@ export interface TestType Promise | any): void; afterAll(title: string, inner: (args: TestArgs & WorkerArgs, testInfo: TestInfo) => Promise | any): void; use(fixtures: Fixtures<{}, {}, TestArgs, WorkerArgs>): void; - step(title: string, body: () => T | Promise, options?: { box?: boolean }): Promise; + step(title: string, body: () => T | Promise, options?: { box?: boolean, location?: Location }): Promise; expect: Expect<{}>; extend(fixtures: Fixtures): TestType; info(): TestInfo;