test: add browser acceptance matrix

This commit is contained in:
NuklearRabbit
2026-07-29 18:49:08 +02:00
parent a0733875c4
commit 1e86afb7d9
9 changed files with 340 additions and 47 deletions
+9
View File
@@ -16,4 +16,13 @@ jobs:
cache: npm
- run: npm ci
- run: npm run quality
- run: npx playwright install --with-deps chromium
- run: npm run test:browser:ci
- name: Preserve browser failure evidence
if: failure()
uses: actions/upload-artifact@v4
with:
name: forgeflow-browser-failure-evidence
path: artifacts/
if-no-files-found: ignore
- run: npm audit --omit=dev --audit-level=high
+2
View File
@@ -3,4 +3,6 @@ dist/
.DS_Store
*.log
coverage/
artifacts/
playwright-report/
.forgeflow/
+45 -41
View File
@@ -1,9 +1,11 @@
ForgeFlow 0.10.0 source manifest
SHA-256 BYTES PATH
(The manifest excludes itself, dependencies and generated release artifacts.)
a6ce7703a16e24b307af98b06e99e7f6a4230d854bc6ed9b745510603a03926d 370 .gitea/workflows/quality.yml
755f4db7d76bfec0963ef051748a82810c0d58acd4ffd823aa6928a5167fceb4 58 .gitignore
cedceb71eb846d99c7c4019031833c1c7f93b84a1c6073aec7d2435dc744ca3d 703 .gitea/workflows/quality.yml
0182dfbbf5b9fc5e62f064f094f5412bf245f9a67ec712e8807f2ce4da443717 88 .gitignore
f14b4987904bcb5814e4459a057ed4d20f58a633152288a761214dcd28780b56 3 .nvmrc
f2cdfd679f074475a515fc809607154fd355a60f96e3e69ba69a04c7092c1856 514823 artifacts/browser-report/index.html
91d1c43004802cd49950d78eb11c8fa7d05da8ffffe219a8b13b2f561bc00903 45 artifacts/browser/.last-run.json
89545860bd6f7566da81edc8328cd2a1ebf33e81a4b0dcf2cec74338c05e8cac 1753 build-windows.ps1
0970821475a4452aa19e447e9397a95db836791f16890a1a83fd748ac033dc86 8830 build/icon-128.png
09112c1425ca953d8dd8b2bcfd221e5a84b9f81752f7168f360e295030cbc8f2 521 build/icon-16.png
@@ -15,41 +17,41 @@ ca32a76e708d565c4af659f0f4d2615fc32114c3f75aec1454862a3ed1e72c41 2263
25048ed854e8ce8fece115e555c98d25507b002f8019b6ae717b54604c868c50 46223 build/icon.ico
16efd2fca83004f781eae40ae0f706a004ce0bddf338dd087b8adf7eb10c1d84 85704 build/icon.png
5f4aca19a35cbcaffa1a6993ce96b7d66052ec2b286022f2af74594e8a310568 15712 CHANGELOG.md
04a3058e507c938447d35564e503cf89b76670d8ea45f5209db074dfa2625b91 303090 coverage/tmp/coverage-14228-1785341379444-0.json
0f86a35f2e6a48c0789263a4e36190760b0dfa45ce9a1128555b4dd77da2df5b 352006 coverage/tmp/coverage-14596-1785341377618-0.json
4ae93dacf27d8ec7e84bd00ab52398a0d99cecb25cf2244be8e6d89824c3d5db 345122 coverage/tmp/coverage-15236-1785341392510-0.json
2607418ea469b58eb6190e4cabde794c0773b7746afb0fdd2d401c99a353752a 358520 coverage/tmp/coverage-17880-1785341380606-0.json
ea98763aaacb035c1c75fca8f50784fc0cc89b2dedf2ddfa80c0eb32d633af27 342639 coverage/tmp/coverage-19480-1785341377315-0.json
fdaef396ca19f7e1f62ea7e6f2172fa615c7e7f26b157e80c896199f5ba3e77e 329358 coverage/tmp/coverage-19612-1785341379233-0.json
d2808be6bce7d7d3d4662def01522170b8cef6c802813db92ade8c27b335fbbe 346825 coverage/tmp/coverage-21808-1785341377499-0.json
7f788bab2eb9ada8e3c3f7fc1d6948f001018721eccb54e87b98a8916e30695b 299000 coverage/tmp/coverage-24368-1785341377951-0.json
d3584daefb19adb8e0e7cd3ca6ffc7f6992636a692a4a3b186da94281950aa8f 302346 coverage/tmp/coverage-25148-1785341378806-0.json
e4045202501f53544351ae8f0aba6cdcccef9d08d735f4d0c83634af5d3033b3 344499 coverage/tmp/coverage-29628-1785341379071-0.json
bf66bf3c80bfed0d145dc2a73363e47089a74858d3cece5dbef2756bfbfd623b 360893 coverage/tmp/coverage-31472-1785341379151-0.json
0d52bfc33883af8fccdc37c436edd95eb83f54c65de87fa09ad03719995574e4 335635 coverage/tmp/coverage-36800-1785341386017-0.json
bcb44d6e6369e3cb5fd5306a02097fadd44e6d85eeac5490e7660f841ba86ea3 288320 coverage/tmp/coverage-37176-1785341378929-0.json
da4b2512633624ef659682bf16af77d92799bd228e0d69a985fa23246460ce09 111308 coverage/tmp/coverage-44380-1785341379522-0.json
96a9f4b2bb3a429dbe5286c7f6c13c39b0e0de882d46724853083ea3d9ac9f6e 317635 coverage/tmp/coverage-44612-1785341382147-0.json
705a92db802fac29c0bbe5aa5727ad35350ddfd03bc98efc24d68529f5a41a99 360058 coverage/tmp/coverage-44984-1785341422918-0.json
279e70e06e6366bf15bb60f6079c38580d006832ba200ba5cebd9bac8a4e4f02 321057 coverage/tmp/coverage-48188-1785341379614-0.json
20758be193868d8f9275dd1eaf35b6bfc652f4682ebc89e4c4aa481e63dd1664 301398 coverage/tmp/coverage-49592-1785341377478-0.json
a4275d02b93b13deadcc554844b321fe186ad272e676f93ccf8f84772bc2760c 284667 coverage/tmp/coverage-49952-1785341378477-0.json
d704dd8bab1e3fcf52d4e4762e6a3e4eb9818073a2fc1d622a2afe4425ec9cc4 336008 coverage/tmp/coverage-50036-1785341378337-0.json
e4dc4ec12d779464d974f7d5c9713f63db6776596698fdc4ce03d18e773a34d9 354684 coverage/tmp/coverage-50728-1785341378056-0.json
0487ef221d132f9ada2b57cb216fb0ff167f903cea8df46c61b39b9fa52bd38a 282952 coverage/tmp/coverage-51204-1785341379590-0.json
42be90ace3af568e4eace7f1a3a5974856360707839c793b3b092ede0f6c5ad1 344613 coverage/tmp/coverage-51236-1785341383324-0.json
1b5a01cc5508a78e28b27f7a46878d54aaca027deb0e93f34f445d9dd32116f1 335945 coverage/tmp/coverage-54004-1785341378407-0.json
b46e7b4d5f95b167803a7d0481163a01026b5ac2e9c32e2ba40afa8a4791163e 428588 coverage/tmp/coverage-55224-1785341380041-0.json
b175e49795c65f6011dbf04aefd75cb7ebee5c2b248edb50e7df1780f2a599b8 367436 coverage/tmp/coverage-55472-1785341379481-0.json
2a53a441228fb74c1f7a488d49c1545e53640af5c1ca720352919c86527e5c19 344934 coverage/tmp/coverage-56192-1785341380236-0.json
dd2c111bc050afda47ed7379f88651cda579a965cfed56ddf2b3ca0e5d748ee1 304508 coverage/tmp/coverage-5636-1785341377677-0.json
8ce33572e4a3ba6e63b85155dc0ebba61b3e6fd203ef17b762735639dab72467 360058 coverage/tmp/coverage-56392-1785341444543-0.json
9efac9c2f67e00fbc2ddd22ff17886ffa212b55b76e21597df1a85417e55ef0c 89175 coverage/tmp/coverage-56440-1785341377010-0.json
4b6892c586091b5bf3eead8d8687a318ef2d82874fc0c9efaab25430ee819600 296835 coverage/tmp/coverage-58592-1785341378685-0.json
81f7395f307d0c4776331a5125653388650dea21876ace362ec92e3ce1efefda 318326 coverage/tmp/coverage-58856-1785341378523-0.json
5370cdb8a3384f62e68314bd4bf0efa78d9db57ced656b011d3b1e995369596e 327419 coverage/tmp/coverage-59512-1785341377212-0.json
7e7571c84f4bcdcdd39ba6fcaa2ac9ea1583eeff6cf59ea4c46c601add76e40a 310269 coverage/tmp/coverage-60040-1785341379655-0.json
d19fda134998419dd45ecd943d587c26cf80742b140de46449f8ec4bcf9191a9 302513 coverage/tmp/coverage-61124-1785341378630-0.json
56691e95912beab52b9e072ad0735a706934def093d91a83a235119e03f7ef21 111308 coverage/tmp/coverage-14112-1785342182302-0.json
35e206d8e448f56715fdeaafc82a00680df74edce1bd1d74d2c626c0ebdc8dd4 344934 coverage/tmp/coverage-18504-1785342182921-0.json
cd5a1817ee7f405f72ede736ec2ccc5831a14c4ab99c3ae9e1af11c93c7f4c13 354684 coverage/tmp/coverage-23740-1785342180573-0.json
f2dbe68eb60983b2038bfda8448e148e0d762794651c8f0ef0a5985549f35d11 301398 coverage/tmp/coverage-30356-1785342180023-0.json
04e0ac6a30fdcbbc9f9856697b228626e0355b7c98c6045cf0db01de0c93fc68 344613 coverage/tmp/coverage-35156-1785342187759-0.json
0d4e6a6dcc0ce9f8d2d9e304dbbeccc2b551fbe79637fdd16b7e860a58685d68 302513 coverage/tmp/coverage-35780-1785342181205-0.json
3c5a7491afc0247568bce0824331c16e2e9af0693b216e01fe73bb41ad4fe91f 327420 coverage/tmp/coverage-38404-1785342179951-0.json
24ca89f314bacd35321b8772706e5c4658602abf90e701561d23f67a1d4dd10e 288320 coverage/tmp/coverage-38780-1785342181696-0.json
8711b58d90963d5c5239011527a8d504fde3bed93671e09b8e6f8e865837d928 358165 coverage/tmp/coverage-39472-1785342183215-0.json
5e8d4cd9c0b348618088733e133986c8bff8d822b7a0f0fb6c04a777f025e249 303090 coverage/tmp/coverage-39536-1785342182261-0.json
9aa7a9e3b5daa4a3f2139df2a8b8857d851ef2ce8e3c7679db787f6e72f5df38 302346 coverage/tmp/coverage-41596-1785342181753-0.json
cd9419f7829a06e3932ca774fb06c51b3637639e8f750fe0bbce400921c20866 321057 coverage/tmp/coverage-42088-1785342182383-0.json
5b75886d617be03df5d1271223cbd8cc660d6603368349d7f7740e7b5005d3d2 304508 coverage/tmp/coverage-42724-1785342180308-0.json
e567e65494a3fadf0effdd7058761c890ff35411532673fb6d0feb0f7d0d681f 346823 coverage/tmp/coverage-44760-1785342180156-0.json
4f2a6563b9710b23c918d10f1a2d713975f0f5d2feedb5731e87a441bb9eabae 352006 coverage/tmp/coverage-44924-1785342180167-0.json
5e355c76dab29a1ecebe30d390970397ae6ccd71798b111fa67e395a1310ad5e 361403 coverage/tmp/coverage-46136-1785342180786-0.json
1623101020fed0786a83d5244a03df9bf2124a9a01961a7739ce63ba7c941ca4 336008 coverage/tmp/coverage-47576-1785342181138-0.json
0307f0113c6b76b5044cae887f76f3c01cfd7133fd4a00a8591a979eff3ab06e 317635 coverage/tmp/coverage-48580-1785342185938-0.json
4588ba2d6ef043d6552135f874f2415865f2401a53c46c6e29229b6925deceb0 335945 coverage/tmp/coverage-48608-1785342180830-0.json
9335bccbbea8122e6bcd7190e5266e4a0fbd1e3631d882004e2d43cec41de95d 318326 coverage/tmp/coverage-49480-1785342181092-0.json
e82fa624b1ef3e7ea427b58aac1c3135d0ac1af62b5c1768a7c2bd59791765c7 367436 coverage/tmp/coverage-50664-1785342182886-0.json
0884915ee6ccec377161cb960b768bb44da6f8622ab3d9542a0c51a9fd0106b9 375705 coverage/tmp/coverage-54052-1785342191177-0.json
a0150efac6b758945f87373f8329f05d6be53d11a3201a88a1124372e8ee7d69 282952 coverage/tmp/coverage-54676-1785342182110-0.json
885fb349d550fa169fb0a0fe97c96c9f0353e62a321a2022da2de0f1c1958811 345124 coverage/tmp/coverage-56728-1785342195847-0.json
e0dbe28900b09fc36b22fbb05bc844f57ff3094b9a1f84da97f793311c37b3f9 344499 coverage/tmp/coverage-57256-1785342181830-0.json
615cc6516905c8e53e4b888bc81589ce8542e34634e5a391ba9204843f57221b 428589 coverage/tmp/coverage-58964-1785342182533-0.json
5d845ca1a0ff28f1e570dcb8a26502e902589eafc2727171f1aad0aa7a2ce52e 296835 coverage/tmp/coverage-59284-1785342181255-0.json
3afca2678ab83c1ecaba8c1abf70b89b3763016acb52df4027d61a590dcf5c19 284668 coverage/tmp/coverage-59848-1785342181206-0.json
5a39c973f886c05b9c34ca089f9d410ab5831036e83d7d5dbf525c2feb0e3670 342640 coverage/tmp/coverage-60948-1785342181050-0.json
79d1a3635a8c972ac6ab0aa75149b2a5f01b84ae2a0ae68e736490160b22bc27 360058 coverage/tmp/coverage-61560-1785342250533-0.json
03adfd7ea1161c638fe6f6a75a0ab0d860f89412ea047209ea1b15f879de9aad 299000 coverage/tmp/coverage-62420-1785342180409-0.json
8387e10a857a2195458bf576c4129b2cd516a50ac5408a605eff038376bba64b 310269 coverage/tmp/coverage-62428-1785342182350-0.json
a02d7c51b209b5967e63cec657caeb8e671b5c87d34dbe2cfbec2be891f6266e 329358 coverage/tmp/coverage-62604-1785342181856-0.json
3488e63749aed14a820a72d8fad385175f2111648bdf2c10a48c1148c419836f 330765 coverage/tmp/coverage-62652-1785342180356-0.json
3da8a2ab49221e4c837c413af878b951f1011be09824ee7dcb49485fb40550bd 89175 coverage/tmp/coverage-63428-1785342179556-0.json
21cb96e7afe71b1dc791c818dedd244d92f9a6ed4d9ffbb3022ccb187e1bdf0f 852 docs/ACCEPTANCE.md
a17f95d96d3c9fbc69d870874e6fbb7472091adefc454b24f835db1279511d72 8296 docs/ARCHITECTURE.md
72e846f591c47a0291e7466e58e052d3d5afcf551c4e6c848632ac3c552a1244 3043 docs/CURRENT_STATE.md
@@ -121,8 +123,9 @@ c230b931abf2293d2d44b7a69b94c35f1142c093cc46b88739a0de5cbd6d1896 1532
4a561ead5ba7cdfaf4efce91842a4308c5f2a77980205879d83835efb8a579db 1067 LICENSE
1f0f388df4397e548887bbc7579fd3c864581b86469c01703201ece7a6cbf931 13667 main.cjs
91a984a89dd57a084b9a2331763cacdb061582fb590f13df379d92c1a77a2ee1 352 OVERLAY-INSTRUCTIONS.md
f3e2a262e251eb165f342dc3c184625ec10d56e536b5a987199b9944f463eee9 181069 package-lock.json
43725a143cb927cc70bd823d789788d785d872119c008043c270bbf73948851f 4882 package.json
17eeeb1d6f18fbbbb918cdfdbc3836e46ec6be9e32c059cfc330e9e4e73e39d0 183079 package-lock.json
96ab185db639205021c5b0b6fbf8d4bf19d2cad8d1a042eab9a425613ea6a574 5018 package.json
2a597a5704c576783b8a72407fbc377fa7506b36a4596ea7f7bce126e394f837 1326 playwright.config.mjs
69318fdf054be7aa2fe86ead9847da9da65745d8d5de548c8346f3ba0afc4892 12175 preload.cjs
abe5dd6fd68f2970cd19ef134094907c67219061d8fe9a1a08324c78de4ad437 484 PUBLISH-AND-ENABLE-UPDATE.cmd
f018383f755352ca448e2ebb1e19b1dba412a3eb793d61e64b02953e300754fd 10538 Publish-ForgeFlow-Release.ps1
@@ -191,11 +194,11 @@ cdfaacdcd5ae04b0e5c79fefa21f5e09d5c810bcea504c5b6e1d6b744182ff84 15567
813b8cdeecac43794166f3db9d3c5d2c441e0292f9ab7bd465ba136d6201e95d 82476 src/renderer/assets/itworx-wordmark-dark.png
094c1b71cc2482a9db250ac175f45f3de68f53277dfbde371a03e61923d00988 75240 src/renderer/assets/itworx-wordmark-light.png
813b8cdeecac43794166f3db9d3c5d2c441e0292f9ab7bd465ba136d6201e95d 82476 src/renderer/assets/itworx-wordmark.png
5d6c87d51e55c9e976cf83ce242be0c28cf831d9c52082f02e745193500e6348 47992 src/renderer/dialogs.js
1a577af2459715cf38d49d118da8c5f897a7ff1d3eb42ad41f121675eb871732 48389 src/renderer/dialogs.js
977af9585074f0a404830c5a93de6939b4897d2eb98ba8ae75c3cf0023dea673 5782 src/renderer/events.js
a84da5aecbb16ce7983dba1f6d6aab1bf47b2e9a87c2933fa1afb8123f7ef7d6 1497 src/renderer/index.html
06180d9656dd254edfb6949c397f8e313954fc560ddcb22b3a35fce3c3e35655 21350 src/renderer/mock-bridge.js
19603be9f471ba695c66f17fc027ab427b68ce146af80d52e3c94624a5ba1c35 27712 src/renderer/mock-deployment-bridge.js
4b0a64610da0c446f15a43e4753b26f29de72e07793e2fa7b7322d4f07cf8050 27806 src/renderer/mock-deployment-bridge.js
26065ffa2359cd27b9c9b5b9fb67bcad83ba0f118960c7c024e7e9392dbb16a3 20032 src/renderer/mock-repository-bridge.js
94fa265c2fe9ca8d644f0ce9b620b6f85d9b25dca5802c4e9195b66dcbe80120 6522 src/renderer/operations.js
45692591428575b518678a6b548c25d3de95f568541e5648b0f06f42c48b5bbf 77872 src/renderer/styles.css
@@ -213,6 +216,7 @@ f8853dce6fdf360d5df2fbe2b6df3e5687630c807fee5ba8436679b34ec737ea 2436
058aeaa5d9bfe377c7e322f213c7871ecc4151b5d08ef790992f4ee28d857658 743 START-FORGEFLOW-OVERLAY.ps1
f5b0ea887fcdeadec78c1ad49b0ec7979723562f5c0b730703acb77a37281ee0 1009 tests/acceptance.test.mjs
a4e5947204ff6878e601e32477bc85b53cd0153baf95a161c8935b6e5466c257 1155 tests/audit-service.test.mjs
33bc892963e89b868235b959498308a456b1285057bda524ba7c1d5a9ee2159e 8763 tests/browser/forgeflow.spec.mjs
454edeaccb2bd41043bc918d3e3a6127db14339031d6a1c1562ac855e90455d2 4318 tests/clone-target.test.mjs
f1463326aee79842d265687ae628189ce54e92544600f2bd14073780287cfb14 2502 tests/configuration-backup.test.mjs
b7e009fed4171d6dd6b4c3154ba1d3f7198e98f5b79b298687841fc8169447cd 9354 tests/deploy-key-lifecycle.test.mjs
+64
View File
@@ -12,6 +12,7 @@
},
"devDependencies": {
"@eslint/js": "9.39.5",
"@playwright/test": "1.55.0",
"c8": "10.1.3",
"electron": "43.2.0",
"electron-builder": "26.15.3",
@@ -916,6 +917,22 @@
"node": ">=14"
}
},
"node_modules/@playwright/test": {
"version": "1.55.0",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.55.0.tgz",
"integrity": "sha512-04IXzPwHrW69XusN/SIdDdKZBzMfOT9UNT/YiJit/xpy2VuAoB8NHc8Aplb96zsWDddLnbkPL3TsmrS04ZU2xQ==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.55.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@sindresorhus/is": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
@@ -2743,6 +2760,21 @@
"dev": true,
"license": "ISC"
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -4000,6 +4032,38 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/playwright": {
"version": "1.55.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.55.0.tgz",
"integrity": "sha512-sdCWStblvV1YU909Xqx0DhOjPZE4/5lJsIS84IfN9dAZfcl/CIZ5O8l3o0j7hPMjDvqoTF8ZUcc+i/GL5erstA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.55.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.55.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.55.0.tgz",
"integrity": "sha512-GvZs4vU3U5ro2nZpeiwyb0zuFaqb9sUiAJuyrWpcGouD8y9/HLgGbNRjIph7zU9D3hnPaisMl9zG9CgFi/biIg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/plist": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz",
+6 -3
View File
@@ -19,6 +19,8 @@
"doctor": "node scripts/doctor.mjs",
"acceptance": "node scripts/acceptance.mjs",
"architecture:audit": "node scripts/architecture-audit.mjs",
"test:browser": "playwright test",
"test:browser:ci": "playwright test --reporter=line,html",
"connections:check": "electron scripts/validate-installed-connections.cjs",
"deployments:audit": "electron scripts/audit-installed-deployments.cjs",
"release:binary": "electron scripts/publish-binary-release.cjs",
@@ -28,6 +30,7 @@
},
"devDependencies": {
"@eslint/js": "9.39.5",
"@playwright/test": "1.55.0",
"c8": "10.1.3",
"electron": "43.2.0",
"electron-builder": "26.15.3",
@@ -101,9 +104,9 @@
"docs/RELEASE_NOTES_0.9.5.md",
"docs/RELEASE_NOTES_0.10.0.md",
"docs/CURRENT_STATE.md",
"docs/MUTATION_MODEL.md"
,"docs/RELEASING.md"
,"docs/ERROR_CODES.md"
"docs/MUTATION_MODEL.md",
"docs/RELEASING.md",
"docs/ERROR_CODES.md"
],
"asarUnpack": [
"scripts/apply-binary-update.ps1"
+42
View File
@@ -0,0 +1,42 @@
import { defineConfig } from "@playwright/test";
const matrices = [
["compact-dark-100", 1120, 720, "dark", 1, false],
["desktop-light-125", 1440, 900, "light", 1.25, false],
["wide-dark-150", 1920, 1080, "dark", 1.5, false],
["compact-light-reduced", 1120, 720, "light", 1, true],
["desktop-dark-reduced", 1440, 900, "dark", 1.25, true],
["wide-light-100", 1920, 1080, "light", 1, false],
];
export default defineConfig({
testDir: "./tests/browser",
outputDir: "artifacts/browser",
timeout: 45_000,
expect: { timeout: 7_000 },
fullyParallel: false,
workers: process.env.CI ? 2 : 3,
reporter: [["line"], ["html", { outputFolder: "artifacts/browser-report", open: "never" }]],
use: {
baseURL: "http://127.0.0.1:4173",
screenshot: "only-on-failure",
trace: "retain-on-failure",
video: "retain-on-failure",
},
webServer: {
command: "node scripts/serve-demo.mjs",
url: "http://127.0.0.1:4173",
reuseExistingServer: !process.env.CI,
timeout: 30_000,
},
projects: matrices.map(([name, width, height, theme, scale, reduced]) => ({
name,
metadata: { theme, scale, reduced },
use: {
viewport: { width, height },
deviceScaleFactor: scale,
colorScheme: theme,
reducedMotion: reduced ? "reduce" : "no-preference",
},
})),
});
+5 -1
View File
@@ -289,6 +289,11 @@ function enhanceRenderedUi() {
if (!control.id) control.id = `forgeflow-field-${index}`;
label.htmlFor = control.id;
});
document.querySelectorAll("input:not([aria-label]), select:not([aria-label]), textarea:not([aria-label])").forEach((control) => {
if (control.labels?.length) return;
const fallback = String(control.placeholder || control.name || control.id || "Form control").replaceAll("-", " ").trim();
control.setAttribute("aria-label", fallback.charAt(0).toUpperCase() + fallback.slice(1));
});
}
function render() {
@@ -316,4 +321,3 @@ function render() {
document.querySelector("#palette-input")?.focus(),
);
}
+3 -2
View File
@@ -466,7 +466,7 @@ function createMockDeploymentBridge(context) {
checkedAt: iso(),
score: 78,
grade: "Good",
policy: { id: "standard", label: "Standard", requiredScore: 70 },
policy: state.gitValidatorPolicy || { id: "standard", label: "Standard", requiredScore: 70 },
ready: true,
commitSha: "8cbaf303aa3bb9b4023a7c89aa13fb70ce612847",
trend: { newlyFound: ["working-tree"], resolved: ["editorconfig"], regressions: [], suppressions: [] },
@@ -572,7 +572,8 @@ function createMockDeploymentBridge(context) {
};
},
async gitValidatorSetPolicy(_fullName, policy) {
return { id: policy.id, label: policy.id[0].toUpperCase() + policy.id.slice(1) };
state.gitValidatorPolicy = { id: policy.id, label: policy.id[0].toUpperCase() + policy.id.slice(1) };
return clone(state.gitValidatorPolicy);
},
async gitValidatorSuppress(_fullName, suppression) {
return { ...suppression, id: `suppression-${Date.now()}`, createdAt: iso() };
+164
View File
@@ -0,0 +1,164 @@
import { test, expect } from "@playwright/test";
import { writeFile } from "node:fs/promises";
const consoleEntries = new WeakMap();
const runtimeErrors = new WeakMap();
test.beforeEach(async ({ page }, testInfo) => {
const logs = [];
const errors = [];
consoleEntries.set(page, logs);
runtimeErrors.set(page, errors);
await page.emulateMedia({ colorScheme: testInfo.project.metadata.theme, reducedMotion: testInfo.project.metadata.reduced ? "reduce" : "no-preference" });
page.on("console", (message) => logs.push({ type: message.type(), text: message.text() }));
page.on("pageerror", (error) => errors.push({ name: error.name, message: error.message, stack: error.stack }));
await page.goto("/");
await expect(page.locator(".app-shell")).toBeVisible();
const theme = testInfo.project.metadata.theme;
await page.evaluate((requested) => {
document.documentElement.dataset.theme = requested;
localStorage.setItem("forgeflow-demo-theme", requested);
}, theme);
});
test.afterEach(async ({ page }, testInfo) => {
const logs = consoleEntries.get(page) || [];
const errors = runtimeErrors.get(page) || [];
if (testInfo.status !== testInfo.expectedStatus) {
const prefix = testInfo.outputPath("failure");
await writeFile(`${prefix}-console.json`, JSON.stringify({ test: testInfo.title, project: testInfo.project.name, metadata: testInfo.project.metadata, logs, errors }, null, 2));
await writeFile(`${prefix}-dom.html`, await page.content());
await writeFile(`${prefix}-fixture.json`, JSON.stringify({ url: page.url(), viewport: page.viewportSize(), theme: await page.locator("html").getAttribute("data-theme") }, null, 2));
}
expect(errors, "page errors").toEqual([]);
expect(logs.filter((entry) => entry.type === "error"), "console errors").toEqual([]);
});
async function assertSurface(page) {
const audit = await page.evaluate(() => {
const interactive = [...document.querySelectorAll('button,input,select,textarea,a[href],[role="button"]')].filter((element) => {
const style = getComputedStyle(element);
return style.display !== "none" && style.visibility !== "hidden" && element.getBoundingClientRect().width > 0;
});
const unnamed = interactive.filter((element) => !String(element.getAttribute("aria-label") || element.getAttribute("title") || element.labels?.[0]?.textContent || element.textContent || element.value || "").trim());
const outside = interactive.filter((element) => { const rect = element.getBoundingClientRect(); const fixed = ["fixed", "sticky"].includes(getComputedStyle(element).position) || Boolean(element.closest('[role="dialog"]')); return rect.left < -1 || rect.right > innerWidth + 1 || (fixed && (rect.top < -1 || rect.bottom > innerHeight + 1)); });
const text = document.body.innerText;
return {
horizontalOverflow: document.documentElement.scrollWidth > document.documentElement.clientWidth + 1,
unnamed: unnamed.map((element) => element.outerHTML.slice(0, 180)),
outside: outside.map((element) => element.outerHTML.slice(0, 180)),
badTokens: ["undefined", "[object Object]", "â", "Â", "Ã"].filter((token) => text.includes(token)),
nullText: /(^|\s)null($|\s)/i.test(text),
headings: [...document.querySelectorAll("h1,h2,h3")].map((heading) => Number(heading.tagName[1])),
};
});
expect(audit.horizontalOverflow).toBe(false);
expect(audit.unnamed).toEqual([]);
expect(audit.outside).toEqual([]);
expect(audit.badTokens).toEqual([]);
expect(audit.nullText).toBe(false);
expect(audit.headings.length).toBeGreaterThan(0);
}
test("shell, overview, repositories and settings remain responsive and accessible", async ({ page }, testInfo) => {
await assertSurface(page);
for (const view of ["overview", "deployments", "settings"]) {
await page.locator(`.nav-button[data-action="navigate"][data-view="${view}"]`).click();
await expect(page.locator("main")).toBeVisible();
await assertSurface(page);
}
await expect(page.locator("html")).toHaveAttribute("data-theme", String(testInfo.project.metadata.theme));
if (testInfo.project.metadata.reduced) {
expect(await page.evaluate(() => matchMedia("(prefers-reduced-motion: reduce)").matches)).toBe(true);
}
});
test("repository changes, Git tools and Git Validator complete their primary flow", async ({ page }) => {
await page.locator('[data-action="select-repo"]').first().click();
await expect(page.locator('[data-action="repo-tab"]')).toHaveCount(6);
for (const tab of ["changes", "history", "deployments", "gittools", "validator", "settings"]) {
const control = page.locator(`[data-action="repo-tab"][data-tab="${tab}"]`);
if (await control.count()) {
await control.click();
await expect(control).toHaveClass(/active/);
await assertSurface(page);
}
}
await page.locator('[data-action="repo-tab"][data-tab="validator"]').click();
await expect(page.locator(".validator-score")).toBeVisible();
await expect(page.locator("#validator-policy")).toBeVisible();
await page.locator("#validator-policy").selectOption("production");
await expect(page.locator(".validator-hero")).toContainText(/Production policy/i);
await page.keyboard.press("Tab");
await expect(page.locator(":focus")).toBeVisible();
});
test("deployment inventory supports dense workloads without ambiguous blank cards", async ({ page }) => {
await page.locator('.nav-button[data-action="navigate"][data-view="deployments"]').click();
await expect(page.locator(".deploy-card, .server-inventory-panel .tool-row").first()).toBeVisible();
const cards = page.locator(".deploy-card, .server-inventory-panel .tool-row");
const count = await cards.count();
expect(count).toBeGreaterThan(0);
for (let index = 0; index < Math.min(count, 25); index += 1) {
await expect(cards.nth(index)).not.toHaveText(/^\s*$/);
}
await assertSurface(page);
});
test("dialogs expose semantics, labels, keyboard close and focus restoration", async ({ page }) => {
await page.locator('[data-action="select-repo"]').first().click();
const trigger = page.locator('[data-action="edit-deployment-profile"], [data-action="add-deployment-profile"]').first();
if (await trigger.count()) {
await trigger.focus();
await trigger.click();
const dialog = page.locator('[role="dialog"]');
await expect(dialog).toBeVisible();
await expect(dialog.locator("button").first()).toBeVisible();
await page.keyboard.press("Escape");
await expect(dialog).toHaveCount(0);
}
await assertSurface(page);
});
test("onboarding and updater states remain usable without an existing configuration", async ({ page }) => {
await page.evaluate(() => localStorage.setItem("forgeflow-demo-setup", "false"));
await page.reload();
await expect(page.locator(".setup-window")).toBeVisible();
await expect(page.getByRole("heading", { name: "Check this computer" })).toBeVisible();
await page.locator('[data-action="setup-run-preflight"]').click();
await expect(page.locator('[data-action="setup-continue"]')).toBeEnabled();
await assertSurface(page);
await page.evaluate(() => localStorage.setItem("forgeflow-demo-setup", "true"));
await page.reload();
await page.locator('.nav-button[data-view="settings"]').click();
await page.locator('[data-action="check-updates"]').first().click();
await expect(page.locator(".update-card")).toContainText(/ForgeFlow/i);
await assertSurface(page);
});
test("inventory, deployment safety and failure evidence dialogs are reviewable", async ({ page }) => {
await page.locator('.nav-button[data-view="deployments"]').click();
const reconciliation = page.locator('[data-action="plan-server-reconciliation"]').first();
if (await reconciliation.count()) {
await reconciliation.click();
await expect(page.locator('[role="dialog"]')).toContainText(/reconciliation/i);
await page.keyboard.press("Escape");
}
const keyLifecycle = page.locator('[data-action="manage-deploy-key"]').first();
if (await keyLifecycle.count()) {
await keyLifecycle.click();
await expect(page.locator('[role="dialog"]')).toContainText(/Deploy key lifecycle/i);
await page.keyboard.press("Escape");
}
const preflight = page.locator('[data-action="run-deployment-preflight"]').first();
await preflight.click();
await expect(page.locator('[role="dialog"]')).toContainText(/preflight/i);
await page.keyboard.press("Escape");
const failed = page.locator('[data-action="open-operation"]').last();
if (await failed.count()) {
await failed.click();
await expect(page.locator('[role="dialog"]')).toContainText(/failed|failure|healthcheck/i);
await page.keyboard.press("Escape");
}
await assertSurface(page);
});