diff --git a/package.json b/package.json index 0ceb77667..6433a7f5e 100644 --- a/package.json +++ b/package.json @@ -57,11 +57,11 @@ "@types/chrome": "^0.3.4", "@types/node": "^26.6.4", "@vitest/coverage-v8": "^5.0.3", - "@wdio/cli": "^9.32.0", - "@wdio/globals": "^9.31.3", - "@wdio/local-runner": "^9.32.0", - "@wdio/mocha-framework": "^9.32.0", - "@wdio/spec-reporter": "^9.32.0", + "@wdio/cli": "^10.0.0", + "@wdio/globals": "^10.0.0", + "@wdio/local-runner": "^10.0.0", + "@wdio/mocha-framework": "^10.0.0", + "@wdio/spec-reporter": "^10.0.0", "Coloris": "github:mdbassit/Coloris#v0.25.0", "acorn": "8.19.0", "dashjs": "5.2.1", @@ -80,6 +80,7 @@ "sortablejs": "1.15.7", "typescript": "^7.0.2", "vitest": "^5.0.3", - "web-ext": "^10.7.0" + "web-ext": "^10.7.0", + "webdriverio": "^10.0.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 48a3cd4bf..68b457899 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,20 +45,20 @@ importers: specifier: ^5.0.3 version: 5.0.3(vitest@5.0.3) '@wdio/cli': - specifier: ^9.32.0 - version: 9.32.0(@types/node@26.6.4)(expect-webdriverio@6.0.10)(supports-color@8.1.1) + specifier: ^10.0.0 + version: 10.0.0(@types/node@26.6.4)(expect-webdriverio@8.0.0-alpha.3(@wdio/types@10.0.0)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)))(supports-color@8.1.1)(yauzl@3.4.0) '@wdio/globals': - specifier: ^9.31.3 - version: 9.31.3(expect-webdriverio@6.0.10)(webdriverio@9.32.0(supports-color@8.1.1)) + specifier: ^10.0.0 + version: 10.0.0(expect-webdriverio@8.0.0-alpha.3(@wdio/types@10.0.0)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)))(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)) '@wdio/local-runner': - specifier: ^9.32.0 - version: 9.32.0(@wdio/globals@9.31.3)(supports-color@8.1.1)(webdriverio@9.32.0(supports-color@8.1.1)) + specifier: ^10.0.0 + version: 10.0.0(supports-color@8.1.1)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0))(yauzl@3.4.0) '@wdio/mocha-framework': - specifier: ^9.32.0 - version: 9.32.0(supports-color@8.1.1) + specifier: ^10.0.0 + version: 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) '@wdio/spec-reporter': - specifier: ^9.32.0 - version: 9.32.0 + specifier: ^10.0.0 + version: 10.0.0 Coloris: specifier: github:mdbassit/Coloris#v0.25.0 version: https://codeload.github.com/mdbassit/Coloris/tar.gz/a00946eb69780d2ba3b906c3f4535b03f987cc05(patch_hash=d8a066fa49aa80466d054aad0f7a2c767b2f62cf365492c155bb9b4ce30fb2ec) @@ -112,10 +112,13 @@ importers: version: 7.0.2 vitest: specifier: ^5.0.3 - version: 5.0.3(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(vite@8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)) + version: 5.0.3(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(vite@8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)) web-ext: specifier: ^10.7.0 version: 10.7.0(jiti@2.7.0)(supports-color@8.1.1) + webdriverio: + specifier: ^10.0.0 + version: 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) packages: @@ -351,10 +354,6 @@ packages: engines: {node: '>= 0.10.4'} hasBin: true - '@electron-internal/extract-zip@1.0.5': - resolution: {integrity: sha512-+bqFCP98pLI0Tt0XQo1TmlXtwjWchISndDOxCkEcIuUgXWpBnLyRI+2DU+mesvnMMX6L1XDqYNA0lXNDHd/yiA==} - engines: {node: '>=22.12.0'} - '@esbuild/aix-ppc64@0.28.2': resolution: {integrity: sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==} engines: {node: '>=18'} @@ -873,8 +872,8 @@ packages: resolution: {integrity: sha512-OsqBjHXCn8cadasoAZBP6nWYvMsRhpMzGXTpxJ5aO04NlbdhIz+FVe3q49l0AwVhsz/cEmIpBes6gAFl1/dWQg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - '@jest/expect-utils@30.5.1': - resolution: {integrity: sha512-WcRWhHQdTMRDpyWKZ/6MINBmovI7zeD+bL8wFjCncRV3NQOwKy1X45IfyblfHR4k/XciIlNEdFL9QjFO+HNKOg==} + '@jest/expect-utils@30.5.2': + resolution: {integrity: sha512-d/HOLSrJUlBIl6n3LzOa7cjW3xdePm5H5gQw2lixZ/0h157l9sw3mLblzFqnPcTbhDzhRQdBOX3A3KcG73nm5Q==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} '@jest/get-type@30.5.0': @@ -943,10 +942,18 @@ packages: '@promptbook/utils@0.69.5': resolution: {integrity: sha512-xm5Ti/Hp3o4xHrsK9Yy3MS6KbDxYbq485hDsFvxqaNA7equHLPdo8H8faTitTeb14QCDfLW4iwCxdVYu5sn6YQ==} - '@puppeteer/browsers@2.13.2': - resolution: {integrity: sha512-5EUZSUIc37H6aIXyWO0Z4y8NlF8NnjgmqeQgOGiswAU7pY0HOo16ho4+alIWmSfdZnjqBRawMsP3I5YqLSn6kw==} - engines: {node: '>=18'} + '@puppeteer/browsers@3.2.4': + resolution: {integrity: sha512-okJ6yQMc0SBexOKxMi966omwAV1uAPwgF/YMZdb04gZQPXVjWHl9bmXjZYtIcsN1+PN3Bz4iFfGK3g+c6lZJ0Q==} + engines: {node: '>=22.12.0'} hasBin: true + peerDependencies: + proxy-agent: '>=8.0.1' + yauzl: ^2.10.0 || ^3.4.0 + peerDependenciesMeta: + proxy-agent: + optional: true + yauzl: + optional: true '@rolldown/binding-android-arm-eabi@1.2.6': resolution: {integrity: sha512-b+jTcARdTiFLI6jB4a5XjTm0RWd6KcRfQj/I2356fxUZemiho9zQLxo0RtCuMDAyKcLo6cEltkgbQp6d1+sjjQ==} @@ -1148,9 +1155,6 @@ packages: peerDependencies: '@svta/cml-utils': 1.5.0 - '@tootallnate/quickjs-emscripten@0.23.0': - resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -1205,8 +1209,8 @@ packages: '@types/mocha@10.0.10': resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==} - '@types/node@20.19.43': - resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==} + '@types/node@22.20.5': + resolution: {integrity: sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA==} '@types/node@26.6.4': resolution: {integrity: sha512-ldVPDCzj7fsaGZrLB0NuHuTvJcsNasysBAqMolr/cgxrLd1xbqxIr3XJiPnHHJUCxj5sNF1vnRj9aWnrVh5Jcg==} @@ -1217,6 +1221,9 @@ packages: '@types/sinonjs__fake-timers@8.1.5': resolution: {integrity: sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==} + '@types/split2@4.2.3': + resolution: {integrity: sha512-59OXIlfUsi2k++H6CHgUQKEb2HKRokUA39HY1i1dS8/AIcqVjtAAFdf8u+HxTWK/4FUHMJQlKSZ4I6irCBJ1Zw==} + '@types/stack-utils@2.0.3': resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -1384,89 +1391,104 @@ packages: vite: optional: true - '@vitest/pretty-format@3.2.7': - resolution: {integrity: sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==} + '@vitest/pretty-format@5.0.3': + resolution: {integrity: sha512-3hr5eV5z2ff0l2Q1vaKQbB4j3M3tElw8stDokEOTqXJB3Es5VrT8gBRIMY59gGuFp8Y61sWlWFK1aNUH5dHDYw==} - '@vitest/pretty-format@4.1.11': - resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} - - '@vitest/snapshot@3.2.7': - resolution: {integrity: sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==} - - '@vitest/snapshot@4.1.11': - resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} + '@vitest/snapshot@5.0.3': + resolution: {integrity: sha512-SK4YTg9jL+jwakHIZxfciuvyOQsbY15AueR3USGtsXzmy+3FbJgqjAbC+ac20mRLwfEDtO5sfTqK6bUCDTRVPA==} '@vitest/spy@5.0.3': resolution: {integrity: sha512-XhFysQTB8AZ+P4gMi+Lpo99vg2AZi0qKpaB9yXQl37+CaMEAPO3iH/wGVnSyL5MPERiLezpqTVtrR6UZH5GCXg==} - '@vitest/utils@4.1.11': - resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} + '@vitest/utils@5.0.3': + resolution: {integrity: sha512-77fDTZv8eu1CzlIfgPU2yrl5MXSUaUAOvgdZEeVm3X3WIxUubIX2SforqS6176lT32HCF6uBVMYVqfHxw7o6eg==} - '@wdio/cli@9.32.0': - resolution: {integrity: sha512-JQvracXVFvRT4bTkWdcRL5HfsZS/JL3GQGTcNkGPuaL/u5S+W7nHU1loAhefGBJnUQ2MRWpCE4XRikpXLP7KsQ==} - engines: {node: '>=18.20.0'} + '@wdio/cli@10.0.0': + resolution: {integrity: sha512-m8ILazuQQLfdE14IEFrrJLTXkz25Nomhnypw7p5royHuxacB643+d9fYjX4VrI0h/Nufomg2ua/vxRMTm6wVBA==} + engines: {node: '>=22.19.0'} hasBin: true - '@wdio/config@9.32.0': - resolution: {integrity: sha512-HjIANiNoL5ehIuot/KbZyNa26r5SaOaVRscYBxZ6fE4BxZMoHqthiuzGsmbr54Hn6pqQm4nn46GCsEfhPxYzEA==} - engines: {node: '>=18.20.0'} + '@wdio/config@10.0.0': + resolution: {integrity: sha512-g0xaQxKJM5hIhZBW05Ivs7vZi/txB+UcWeoiuzNl0/fS5rvQYvofnYlROvCBKeEdf72ooIRtfkRnW8nKQ97P+A==} + engines: {node: '>=22.19.0'} - '@wdio/dot-reporter@9.32.0': - resolution: {integrity: sha512-Pp+1yqen+G2CnGErvkUdUoRpSKyrm/utKRcY/CpNSLOtMag8+B3mfEq/1VvrT6D0+nl8IVESNl5HQLVtycozbQ==} - engines: {node: '>=18.20.0'} + '@wdio/display-server@10.0.0': + resolution: {integrity: sha512-aBLa9Qd+7Cm6LnazcxNGdet141ZY80rx5pAiO6B+f9b/Dz2h3tAP8ryPyKclxKaMoGNh8QDcyNluPGfPW3k0hA==} + engines: {node: '>=22.19.0'} - '@wdio/globals@9.31.3': - resolution: {integrity: sha512-ZzmRKUlwUBahNoNxHbAzst+9dNInEjLQc7Z3c86OhDRxAf3Ivw8k2rc56UCbyYiZnkd1dOpuA91zFiTrUNNORw==} - engines: {node: '>=18.20.0'} + '@wdio/dot-reporter@10.0.0': + resolution: {integrity: sha512-8Y0Ras2CdgNqTWLtOp6Y8HC+vFVtYWw31wvnLtizLvertfDh4mMSgi/loJbfxAEkHu64WK9cIU4iLsq0S/6G/g==} + engines: {node: '>=22.19.0'} + + '@wdio/globals@10.0.0': + resolution: {integrity: sha512-QlC6dkB2hu//cnWIcqine7MUdiJknmB8v7A3jLqEEIOe4J9QXU6HLk35oKKbVQfDnFiEr0JViLQu9XggqH6K0w==} + engines: {node: '>=22.19.0'} peerDependencies: - expect-webdriverio: ^6.0.9 - webdriverio: ^9.28.0 + expect-webdriverio: ^8.0.0-0 || ^8.0.0-alpha.3 + webdriverio: ^10.0.0-0 || ^10.0.0 - '@wdio/local-runner@9.32.0': - resolution: {integrity: sha512-6AhK9OHXzOMfoZb3iU95jOVwjM2VspsVVkxBmWyYBYrpnM+DluHJzPnf0kXlb0h+osnRpIL35ZNon4XmVI0Izw==} - engines: {node: '>=18.20.0'} + '@wdio/local-runner@10.0.0': + resolution: {integrity: sha512-9XGliTTbbhqufM+hbTqAOvAX6Q32KZ0Fcrn/G0xVxmxKqRzu1UByi0nwcu1zqkakwuLRXiD1hYQ76Mwn0fEZ3Q==} + engines: {node: '>=22.19.0'} + + '@wdio/logger@10.0.0': + resolution: {integrity: sha512-kUIw0ZzMs1mvXOaLEcjrM1nqNXY1reaJKReKNcjDSnfhnfUIb0Q5pAsxiMeil2uR0WsSUPzn0r3D9v//7vTOkA==} + engines: {node: '>=22.19.0'} '@wdio/logger@9.29.1': resolution: {integrity: sha512-0ZAEIo6PNyMIJPlOGkIgyOJUjcd0pC8/QHlVAAe1c91/IcjZ1X+k0yidXHaboJdN7dq1XPUacmhRdtua0U5EZg==} engines: {node: '>=18.20.0'} - '@wdio/mocha-framework@9.32.0': - resolution: {integrity: sha512-POrOuA9pEN03sB7eLh13hmrhgvyZ20rQC6PwAZ/9v2LeebmW9xlSIC1UoBqiSiIKQ9qiGGf7lf7CX4NWIqzC2w==} - engines: {node: '>=18.20.0'} + '@wdio/mocha-framework@10.0.0': + resolution: {integrity: sha512-nd1eQs28FPrRqtol0V3BeXsS2OlZUxvgz1bDo2TDdWJPbhEuDZd+JxSqjVbKhPn6A9IYnOYviGnJrdBPx3PZeg==} + engines: {node: '>=22.19.0'} - '@wdio/protocols@9.31.5': - resolution: {integrity: sha512-e4H5exl5xCcITF0y/3zd6pf9kX4TmV8/0nnWDGt4X0KbgDi/+tGqlquZ+YS4YBEPbv95yTqVhnazEsaBiH85Tw==} + '@wdio/protocols@10.0.0': + resolution: {integrity: sha512-426ENC79fH7N4ggoGRHhZBY/exkdHAndbBiXcs9pQkzGP356H8C7VkQUx8g78m4k1TUC0RhOl8Se8VE0Mutpqw==} + engines: {node: '>=22.19.0'} - '@wdio/repl@9.16.2': - resolution: {integrity: sha512-FLTF0VL6+o5BSTCO7yLSXocm3kUnu31zYwzdsz4n9s5YWt83sCtzGZlZpt7TaTzb3jVUfxuHNQDTb8UMkCu0lQ==} - engines: {node: '>=18.20.0'} + '@wdio/repl@10.0.0': + resolution: {integrity: sha512-wAC9ksdWWv2E3Sj86eNKxB3IUt0gEyw9rgDGAWjk/GWgi6K3e+/n3erGNweI9JMJyC/+RNe0qHgVPQpRJMRVpg==} + engines: {node: '>=22.19.0'} - '@wdio/reporter@9.32.0': - resolution: {integrity: sha512-9z8pG7zAayRXwheN8BH6YNuzyPJ/kFIRyPl2fsD+6IvF/LFSptXPlk2XjcnKvY9Am6Lif5oAwebDFcLHp1ztAA==} - engines: {node: '>=18.20.0'} + '@wdio/reporter@10.0.0': + resolution: {integrity: sha512-qPxe2dq0hFDd6AZj6Z3SunpMvCOv9UdXS3CINtSrJweGNGHj1VltSNVPVqWYWwzs3ywuEjGg2t91JIQVxgfgWA==} + engines: {node: '>=22.19.0'} - '@wdio/runner@9.32.0': - resolution: {integrity: sha512-/qGB9id4fX+bmC0HxdelZdh+Rm/u0uAW4y4xs7PoD0B+5qHQ137tRMwq/HAcPLeDJh/LaMYlhqcIj1LyMi6eEA==} - engines: {node: '>=18.20.0'} + '@wdio/runner@10.0.0': + resolution: {integrity: sha512-eFpec964oivTK33/8zhsbwzp6lcTElaL6h3KCEb9I0+jP2avaZS8g6KStHP9Nl/itSYarKiIrsoxZmT9k2x/ng==} + engines: {node: '>=22.19.0'} peerDependencies: - expect-webdriverio: ^6.0.9 - webdriverio: ^9.28.0 + expect-webdriverio: ^8.0.0-0 || ^8.0.0-alpha.3 + webdriverio: ^10.0.0-0 || ^10.0.0 - '@wdio/spec-reporter@9.32.0': - resolution: {integrity: sha512-WXL98WXqSGLffvDhLNsn4zD/sCGaWK1GF+i/gQsBxJN86vRiktL8XsB7q4SfBr7doyrR5+POwceX248M26bRgA==} - engines: {node: '>=18.20.0'} + '@wdio/session@10.0.0': + resolution: {integrity: sha512-at9DQLmPfAinFUs6xmbhjHlj0ApaiAbzJ4JPvTW//csAGgeV+C8KAZmkN9jB4ttHHk//GFWANU6tNzEOPFdG5Q==} + engines: {node: '>=22.19.0'} + hasBin: true + peerDependencies: + '@wdio/electron-service': '*' + '@wdio/visual-service': '*' + appium: ^3.0.0 + peerDependenciesMeta: + '@wdio/electron-service': + optional: true + '@wdio/visual-service': + optional: true + appium: + optional: true - '@wdio/types@9.32.0': - resolution: {integrity: sha512-7jMZQ4TGX45bPLgCv9du7mw/Kq7FrjEbQ5vTGg/pUgQy/wSygJ3u4FvPu8FS+DNeQUmvjSPKEUGfl4N+rZjpUg==} - engines: {node: '>=18.20.0'} + '@wdio/spec-reporter@10.0.0': + resolution: {integrity: sha512-48q0tROaNvP8JQ+JyquJYk1eXBXE9JTGksCKwuiuC8hhYcNyunX1uAtkxAshC5qPVg31pByLcSWnIkBLZkzMBg==} + engines: {node: '>=22.19.0'} - '@wdio/utils@9.32.0': - resolution: {integrity: sha512-LsU+PxRQVXJKISaj9v1NZQTmp4m9uugCh55qjN0cKvdqi3bI17V+3orMXNvDYufBgzzZ1eoGxYejJpdIKx98jA==} - engines: {node: '>=18.20.0'} + '@wdio/types@10.0.0': + resolution: {integrity: sha512-qUJbKEHjsN0PuC3S2r7Gy9wYtbChmX+PYghKGHYQhjyH9P9t+sWhSPQWUBk3S+IUTYT1f+Z4VbF+5FmvlHL6Lg==} + engines: {node: '>=22.19.0'} - '@wdio/xvfb@9.32.0': - resolution: {integrity: sha512-K1DL05PngTMKY8mkzMNYpKNqZNPTrdJheuvTeWwwYlm4Yn9hLc8V3hw6EDsuEYw4cyEyiVeukLbLOEW+AhvqVw==} - engines: {node: '>=18.20.0'} + '@wdio/utils@10.0.0': + resolution: {integrity: sha512-Ta3wq+u83DT0Vnz1QcuqkagBSwWBnTg8LCIQmDO6DGkMIiHXJrhUMfz6ibrlMJPslt4g0P/Ri5wm7WkJXo9HXw==} + engines: {node: '>=22.19.0'} '@zip.js/zip.js@2.14.0': resolution: {integrity: sha512-7HIW+xGAl6LeTlgfLEKeRBPBskNtLSXRNXqb0QuUv6kYO7hhJvSda1UfYvyfyKJxod3q5GRvYZjjdtkxO1FMiQ==} @@ -1530,10 +1552,6 @@ packages: ansi-align@3.0.1: resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} - ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1554,10 +1572,6 @@ packages: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} - anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - archiver@8.0.0: resolution: {integrity: sha512-fV1orZfsnPn9BaSByR/qE67rJCLJEy2Ox5bq7nJh+jquWaNh6Sfec75kJ2T6PtdGUbPQlrVoSVCEOa5SdiTQ1g==} engines: {node: '>=18'} @@ -1581,10 +1595,6 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - ast-types@0.13.4: - resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} - engines: {node: '>=4'} - ast-v8-to-istanbul@1.0.6: resolution: {integrity: sha512-fvpl29helSO2w/z7utIbrkNXILdrLwDwAMH2I/zPKlGf5244+gf+B4cyS1sANcrPY2h+hWCGSgC8N61s/+AF9A==} @@ -1662,17 +1672,9 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - basic-ftp@6.2.1: - resolution: {integrity: sha512-bK67isD+lKq46AU8vNtjvMaT2ZqAOAmNCbxUHlFBRD4k15NWxyEjmaKtZPlgce58So4BNTjITGQOVTjL9y0ECA==} - engines: {node: '>=10.0.0'} - bcp-47-match@2.0.3: resolution: {integrity: sha512-JtTezzbAibu8G0R9op9zb3vcWZd9JF6M0xOYGPn0fNCd7wOpRB1mU2mH9T8gaBGbAAyIIVgB2G7xG0GP98zMAQ==} - binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} @@ -1693,10 +1695,6 @@ packages: resolution: {integrity: sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==} engines: {node: 20 || >=22} - braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} - browser-stdout@1.3.1: resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} @@ -1737,10 +1735,6 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - camelcase@8.0.0: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} @@ -1770,14 +1764,14 @@ packages: resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==} engines: {node: '>=20.18.1'} - chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} - chokidar@4.0.3: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + chrome-launcher@1.2.0: resolution: {integrity: sha512-JbuGuBNss258bvGil7FT4HKdC3SC2K7UAEUqiPy3ACS3Yxo3hAW6bvFpCu2HsIJLgTqxgEX6BkujvzZfLpUD0Q==} engines: {node: '>=12.13.0'} @@ -1795,13 +1789,14 @@ packages: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} - cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + cliui@9.0.1: + resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} + engines: {node: '>=20'} + clone@1.0.4: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} @@ -1865,9 +1860,9 @@ packages: resolution: {integrity: sha512-IBWsY8xznyQrcHn8h4bC8/4ErNke5elzgG8GcqF4RFPw6aHkWWRc7Tgw6upjaTX/CT/yQgqYENkxYsTYN+hW2g==} engines: {node: '>=18'} - create-wdio@9.32.0: - resolution: {integrity: sha512-FDPY5NE41kNMMzvMCHMVuNOebHiwpb9fLYrX7836UYPqJ8Ntkdf0qq1CYfbM0p+Oji6GKlDV6FBrmJN/GRX7AQ==} - engines: {node: '>=18.20.0'} + create-wdio@10.0.0: + resolution: {integrity: sha512-eMvUZd2SVATrIPnYdWZur098ZVShrkpCk/g9QSrgelFcX5oijwUsxn7T0PSDfRLJcIQxPyl7Dyk3ObcAmdMFtg==} + engines: {node: '>=22.19.0'} hasBin: true cross-spawn@7.0.6: @@ -1895,10 +1890,6 @@ packages: resolution: {integrity: sha512-axcJmLeJCTJMLUfQRErUJB1KOiU5iHF+DMLBc3FKeox1HPYtGUQlC6Xm5Mm6vhBUR3CHHLIPOqw0JDIqMtP+Dw==} engines: {node: '>=20'} - data-uri-to-buffer@6.0.2: - resolution: {integrity: sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==} - engines: {node: '>= 14'} - debounce@1.2.1: resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} @@ -1920,10 +1911,6 @@ packages: supports-color: optional: true - decamelize@4.0.0: - resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} - engines: {node: '>=10'} - decamelize@6.0.1: resolution: {integrity: sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1962,10 +1949,6 @@ packages: resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} engines: {node: '>=12'} - degenerator@5.0.1: - resolution: {integrity: sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==} - engines: {node: '>= 14'} - des.js@1.1.0: resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} @@ -1976,14 +1959,14 @@ packages: diff-match-patch@1.0.5: resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} - diff@5.2.2: - resolution: {integrity: sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==} - engines: {node: '>=0.3.1'} - diff@8.0.4: resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} engines: {node: '>=0.3.1'} + diff@9.0.0: + resolution: {integrity: sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==} + engines: {node: '>=0.3.1'} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -2022,8 +2005,8 @@ packages: resolution: {integrity: sha512-sB7vSrDnFa4ezWQk9nZ/n0FdpdUuC6R1EOrlU3DL+bovcNFK28rqu2emmAUjujYEJTWIgQGqgVVWUZXMnc8iWg==} engines: {node: '>=14.0.0'} - edgedriver@6.3.1: - resolution: {integrity: sha512-wCL/Ydgt0DPkWecjhhPj2/lnDxsGeHLPKeJYKKMTNol+ULFVg4WaTvxoB19GU4tieM6LSA3vZEdZxmDC0DwIpQ==} + edgedriver@7.0.0: + resolution: {integrity: sha512-4sJLhorcBSfzaqMN41q2VI9KVW9Qcpo7CHbUcBUDWeQ7uHiWnRWayV1KZOQr9jllSBOHD2aXzhUWrC2yCt+PRA==} engines: {node: '>=20.0.0'} hasBin: true @@ -2051,9 +2034,6 @@ packages: encoding-sniffer@0.2.1: resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==} - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -2108,11 +2088,6 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - escodegen@2.1.0: - resolution: {integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==} - engines: {node: '>=6.0'} - hasBin: true - eslint-plugin-no-unsanitized@4.1.5: resolution: {integrity: sha512-MSB4hXPVFQrI8weqzs6gzl7reP2k/qSjtCoL2vUMSDejIIq9YL1ZKvq5/ORBXab/PvfBBrWO2jWviYpL+4Ghfg==} peerDependencies: @@ -2220,25 +2195,21 @@ packages: resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} - expect-webdriverio@6.0.10: - resolution: {integrity: sha512-VBaMl4U6orgn3uzM7n65Gd48legqi37GoP0mklR+3f7vs/cC1s4hQVmGoiqz9aBcISVF37dAynP3aoi0R0CDow==} - engines: {node: '>=20'} + expect-webdriverio@8.0.0-alpha.3: + resolution: {integrity: sha512-kqQKGhRSZhUWzJSv6RRSE27Tk0xh2boP5wXVzh84HS79rSH61YXxiJMTJmPVjr1tQXcrFWfsiCxed60NnYjuXg==} + engines: {node: '>=22.19.0'} peerDependencies: - '@wdio/globals': ^9.0.0 - '@wdio/logger': ^9.0.0 - webdriverio: ^9.28.0 + '@wdio/types': ^10.0.0-0 + webdriverio: ^10.0.0-0 - expect@30.5.1: - resolution: {integrity: sha512-m8YrYgvKe9+9gEnWEuKz+qCGfHqkrff7PPfyDnOFkjsfYRKqiYyOxDFMFieCGAuhtk/VNm63tvNgKZVzVy+Hvg==} + expect@30.5.2: + resolution: {integrity: sha512-0LNuMvs8/5mRYhnCR4k+lGV7fqPMs6Bnksda4S3zsCUmdxBpAn4zU4NNzXdq1pMKe/H4W5t/zVIDSJ/H3e0vDA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} fast-check@4.10.2: resolution: {integrity: sha512-iK2f+YrcmoeGqk6fA0ea2bptcu/itMIm4NfEozq6N25+aG6h7s5HZbB/k1aV7b5w5sFLMCbbtRUsTVR+BgC3xw==} engines: {node: '>=12.17.0'} - fast-deep-equal@2.0.1: - resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} - fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2289,9 +2260,9 @@ packages: filelist@1.0.6: resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} - fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} + find-up-simple@1.0.1: + resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} + engines: {node: '>=18'} find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} @@ -2317,10 +2288,6 @@ packages: flat-cache@6.1.23: resolution: {integrity: sha512-f++BY9pTk+983xK1FLzlLpmM0i0z+jHmx3QESGkURMXujQZz1k5wzwX6hjnQ8goaD0B+sYnDK1yZ6MTyZfUaqA==} - flat@5.0.2: - resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} - hasBin: true - flatted@3.4.4: resolution: {integrity: sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==} @@ -2336,9 +2303,6 @@ packages: resolution: {integrity: sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==} engines: {node: '>=14.14'} - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2355,11 +2319,6 @@ packages: resolution: {integrity: sha512-PEEUfnQJw0/io6SspBbR4EIeXFL/qe/sugesCyfr9M3llgyxNaOH8stvO6KJOKRjeUAvEB6oSZKS3lgjSEOsXg==} hasBin: true - geckodriver@6.1.1: - resolution: {integrity: sha512-/AcCyc9o9o6hUbudaSJM2iOtXbxSLqQPOb4GrPvEN40cjraUeaX/j5kH3mSgwiroyMn7qzx2wM61AQ2XY8j3sA==} - engines: {node: '>=20.0.0'} - hasBin: true - geckodriver@7.0.0: resolution: {integrity: sha512-RypjU7cNcyjWK3ruEriCkWMRa9nroCT3y2qwkPynGzKTK1wG0UCWubb4AfVDTXzG9BhzRTwg29DoeK7PCRnK1w==} engines: {node: '>=20.0.0'} @@ -2393,17 +2352,9 @@ packages: resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} engines: {node: '>=18'} - get-uri@6.0.5: - resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} - engines: {node: '>= 14'} - gif.js@0.2.0: resolution: {integrity: sha512-bYxCoT8OZKmbxY8RN4qDiYuj4nrQDTzgLRcFVovyona1PTWNePzI4nzOmotnlOFIzTk/ZxAHtv+TfVLiBWj/hw==} - glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} - glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} @@ -2413,10 +2364,9 @@ packages: deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true - glob@8.1.0: - resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} - engines: {node: '>=12'} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + glob@13.0.6: + resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==} + engines: {node: 18 || 20 || >=22} global-directory@4.0.1: resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} @@ -2459,10 +2409,6 @@ packages: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} - he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - hls.js@1.7.3: resolution: {integrity: sha512-MsPlx6yVW4Qv4C7mEVou4/gk/5cN2dVxOTnrNjPSmyH2f0Ln+/UkG10Ax1PE6OvrmTVPKgQYozTtBaPOuCzq7A==} @@ -2542,10 +2488,6 @@ packages: resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} engines: {node: '>=18'} - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -2569,17 +2511,9 @@ packages: '@types/node': optional: true - ip-address@10.7.2: - resolution: {integrity: sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==} - engines: {node: '>= 12'} - is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} - is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - is-docker@2.2.1: resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==} engines: {node: '>=8'} @@ -2624,18 +2558,10 @@ packages: resolution: {integrity: sha512-O2z4/kNgyjhQwVR1Wpkbfc19JIhggF97NZNCpWTnjH7kVcZMUrnut9XSN7txI7VdyIYk5ZatOq3zvSuWpU8hoA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} - is-path-inside@4.0.0: resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} engines: {node: '>=12'} - is-plain-obj@2.1.0: - resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} - engines: {node: '>=8'} - is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -2685,20 +2611,20 @@ packages: engines: {node: '>=10'} hasBin: true - jest-diff@30.5.1: - resolution: {integrity: sha512-e3cNNMpv8Kh20MjjphTXs+3Vz7DQyLM1nft7KJhnh46atFhjVJRa+0Hq0beywuwsACtMQUBihQkFl8zxb7gt1Q==} + jest-diff@30.5.2: + resolution: {integrity: sha512-rBtHnI6BWTiWj3lM7fOLVfChx7R9B0u9VV3PRHawKs79x6ZjW1QoSrKenaigNGWlvhtoi2BqvDbABDie+Os3xQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-matcher-utils@30.5.1: - resolution: {integrity: sha512-aroZVqwOz/wC2y6pC+obgFWKV9viaQWQTTSB6W5H55+egtUczIfXR/rxExTv92xD/ADYwpqaYfVWx1aqwKg7FA==} + jest-matcher-utils@30.5.2: + resolution: {integrity: sha512-lFkB365PTIHOhPzwrb9T6gvlDnPpOZ7/c3ewOKKB7bzztqjVrtlyRL2MkyfJXpyY2u2SKw001JKrTx14fBUFcQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-message-util@30.5.1: resolution: {integrity: sha512-UdQlLdd9wL/Ys7xRErckqwD6wPlSZYueosSWuHc1r2ztGLwlgPvtSJq2+BPEgaEY13WLvfFbmhTj8pba0Sd1jg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} - jest-mock@30.5.1: - resolution: {integrity: sha512-9fVjc3leUpGID2/by/LU4Dvdcp7PFh9LlxS3QRWK3ABm+KtvEVsG/AEGeLY3gKOZsjkBxyfwGltoAVlW7dygHg==} + jest-mock@30.5.2: + resolution: {integrity: sha512-KTHb37Fhj8xY8qPFvTFaFJHcdsumQp+ExsUlgoX232Agmc706gagBs53+CYvulV7MVQo9AgXOUxsqzzVBoJlrA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} jest-regex-util@30.5.0: @@ -2943,13 +2869,6 @@ packages: lodash.union@4.6.0: resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} - lodash.zip@4.2.0: - resolution: {integrity: sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==} - - log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} - loglevel-plugin-prefix@0.8.4: resolution: {integrity: sha512-WpG9CcFAOjz/FtNht+QJeGpvVl/cdR6P0z6OcXSkr8wFJOsV2GRj2j10JLfjuA4aYkcKCNIEqRGCyTife9R8/g==} @@ -2960,12 +2879,9 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@7.18.3: - resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} - engines: {node: '>=12'} - - magic-string@0.30.21: - resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + lru-cache@11.5.3: + resolution: {integrity: sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==} + engines: {node: 20 || >=22} magic-string@1.3.1: resolution: {integrity: sha512-rm91zr2Ou+XueDTohjQQjdQEcYM6zVi8KVUCG8Ec3vHwUEKrhSdCNyfuIywkA6hcCAteIn0ZOtAHA6eGpiX+Pg==} @@ -3017,15 +2933,11 @@ packages: mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} - mocha@10.8.2: - resolution: {integrity: sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==} - engines: {node: '>= 14.0.0'} + mocha@12.0.3: + resolution: {integrity: sha512-beammieDdiWdkjANWeWPpkxUROYGi5FN88fPOkLiticz7HTg3Y+Pho3EYE6SUdcuWnZ3h2WD8EXCA55JF4o6wg==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - modern-tar@0.7.7: - resolution: {integrity: sha512-t9VmxaqrmANnEOBhpSDI6HD192Ge48k8vmWqQQL7hSFEqHEYwZbbsu49+aKLWZeRvFs3j1pMhXOqqF4kPlvjkQ==} - engines: {node: '>=18.0.0'} - modern-tar@0.8.5: resolution: {integrity: sha512-snEhs+6G5Tjd4I7tLCDOaoln2RgE0bD19RzEKgvgK2hZ5VKy3MpLhLTZ2fWpXSTg4K2cyPwp+VHATFJhxfnOeA==} engines: {node: '>=18.0.0'} @@ -3070,10 +2982,6 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - netmask@2.1.1: - resolution: {integrity: sha512-eonl3sLUha+S1GzTPxychyhnUzKyeQkZ7jLjKrBagJgPla13F+uQ71HgpFefyHgqrjEbCPkDArxYsjY8/+gLKA==} - engines: {node: '>= 0.4.0'} - node-forge@1.4.0: resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} engines: {node: '>= 6.13.0'} @@ -3113,9 +3021,6 @@ packages: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - open@11.0.3: resolution: {integrity: sha512-b3souUgU0VaAvMmzA4+9cRgrOgMdE0vc9H9VhQ0FYnjjcyZ8R/f9DiwMU9V8rl+vjf05On9UJhbgb06fsTs87Q==} engines: {node: '>=20'} @@ -3140,14 +3045,6 @@ packages: resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - pac-proxy-agent@7.2.0: - resolution: {integrity: sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==} - engines: {node: '>= 14'} - - pac-resolver@7.0.1: - resolution: {integrity: sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==} - engines: {node: '>= 14'} - package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -3206,6 +3103,10 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} + path-scurry@2.0.2: + resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} + engines: {node: 18 || 20 || >=22} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -3215,10 +3116,6 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.2: - resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} - engines: {node: '>=8.6'} - picomatch@4.0.7: resolution: {integrity: sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==} engines: {node: '>=12'} @@ -3278,16 +3175,6 @@ packages: proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} - proxy-agent@6.5.0: - resolution: {integrity: sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==} - engines: {node: '>= 14'} - - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - - pump@3.0.4: - resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3342,14 +3229,14 @@ packages: resolution: {integrity: sha512-AhNB2KgKeVJr16nK9LLZbJNWnYoT23ZrumNKFDebHBdkC8KHSqWo871JAUhoWC/RtjEVdqNMFpM6qrwRbaUqpw==} engines: {node: '>=18'} - readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - readdirp@4.1.2: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + readdirp@5.1.1: + resolution: {integrity: sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==} + engines: {node: '>= 20.19.0'} + real-require@0.2.0: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} @@ -3357,10 +3244,6 @@ packages: real-require@1.0.0: resolution: {integrity: sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==} - recursive-readdir@2.2.3: - resolution: {integrity: sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==} - engines: {node: '>=6.0.0'} - registry-auth-token@5.1.1: resolution: {integrity: sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==} engines: {node: '>=14'} @@ -3381,9 +3264,6 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resq@1.11.0: - resolution: {integrity: sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw==} - ret@0.5.0: resolution: {integrity: sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==} engines: {node: '>=10'} @@ -3407,8 +3287,8 @@ packages: rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} - safaridriver@1.0.1: - resolution: {integrity: sha512-jkg4434cYgtrIF2AeY/X0Wmd2W73cK5qIEFE3hDrrQenJH/2SDJIXGvPAigfvQTcE9+H31zkiNHbUqcihEiMRA==} + safaridriver@2.0.0: + resolution: {integrity: sha512-LL7Ufa9p1cltLmw0WGIbM6FFXibWMoM9NjJuS5QFiAAtEnEwkwMxaXy1BRKCBZKSZ+brs9eYd3LHHiJfIgjmZg==} engines: {node: '>=18.0.0'} safe-buffer@5.1.2: @@ -3487,18 +3367,6 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - - socks-proxy-agent@8.0.5: - resolution: {integrity: sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==} - engines: {node: '>= 14'} - - socks@2.8.10: - resolution: {integrity: sha512-e0VyvkVTwVYViNovRkZ9aodhxVlyoMn7eJhVUPxZ+eK9P/7CBkxvvsBOHqFPEH416726W8tLXXXjKwqgTErrCQ==} - engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} - sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} @@ -3568,6 +3436,10 @@ packages: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} + string-width@8.3.0: + resolution: {integrity: sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==} + engines: {node: '>=20'} + string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} @@ -3624,9 +3496,6 @@ packages: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} - tar-fs@3.1.3: - resolution: {integrity: sha512-/hU4AXnIdZu+Gvl1pk0oI5f5HxWsCJRtY2aFaJdk9VvyL48DWU6iU5WAIPG+wIi1YvWA6eTJvIviP/tMAZZNwQ==} - tar-stream@3.2.1: resolution: {integrity: sha512-nqsEO8zLZJvrOMdEwkA0QdCLFbetHMn95Zqu4fKwX+hkaTWJPZZOrxx/PwtxoK0MMGQmBQNRW3CPs8IFYQz4cQ==} @@ -3655,10 +3524,6 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} - tinyrainbow@2.0.0: - resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} - engines: {node: '>=14.0.0'} - tinyrainbow@3.1.1: resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} engines: {node: '>=14.0.0'} @@ -3667,10 +3532,6 @@ packages: resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==} engines: {node: '>=14.14'} - to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} - tree-kill@1.2.2: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true @@ -3683,6 +3544,11 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tsx@4.23.15: + resolution: {integrity: sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==} + engines: {node: '>=18.0.0'} + hasBin: true + tunnel@0.0.6: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} @@ -3725,14 +3591,14 @@ packages: undici-types@8.9.0: resolution: {integrity: sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==} - undici@6.28.1: - resolution: {integrity: sha512-zWpdTVD54H48CIybL0rWQ3ukpb9d23wM7eH5RtfdmeP70cWHNjtfo7P4vZX+5CoDcO53J4Pu5uXp7lNfjc6DRA==} - engines: {node: '>=18.17'} - undici@7.29.1: resolution: {integrity: sha512-RYONW2MeafgYlkVOKYKkA/Ag7BmXqgIWCa8t1m0JcxrQg9pI9lEqRhAOruOBCbAohOa/gkCF+iPi9hrgvTzu6Q==} engines: {node: '>=20.18.1'} + undici@8.11.2: + resolution: {integrity: sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==} + engines: {node: '>=22.19.0'} + unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} @@ -3879,15 +3745,15 @@ packages: engines: {node: '>=20.0.0', npm: '>=8.0.0'} hasBin: true - webdriver@9.32.0: - resolution: {integrity: sha512-1Z7mydyw3v3UfzQ6UtrRvu3ij1eHuSoitxBMWlnKi2e3Zpvc1EBPp6FhsXBYhdFg0F4900j6sUTkoF//CkpbpQ==} - engines: {node: '>=18.20.0'} + webdriver@10.0.0: + resolution: {integrity: sha512-EyMSQjY+Nq9mAUTCzzW1hjarwHNJWtCBRtGSZkiTcdJdFZ6siKF0Z01hHJFVkF1Xm+aCkFxD1atm1S2cNxCQxA==} + engines: {node: '>=22.19.0'} - webdriverio@9.32.0: - resolution: {integrity: sha512-A7r7SnvZ6eEJROIWAG6GMOlV82W2Pl7FDjXLn/tJ4L8BEc3yjCrDWzY4EB9i4MtPZwlNZoHgNV9fYYKn7JWAew==} - engines: {node: '>=18.20.0'} + webdriverio@10.0.0: + resolution: {integrity: sha512-DiDflg731z/b+qg4EmVAGihMJSWhHJYhlwrS6Q1uSFqL4c48+xHDj9VjhQr7sFzZ/MBJySjGZfMBGyjP7p547Q==} + engines: {node: '>=22.19.0'} peerDependencies: - puppeteer-core: '>=22.x <=24.x' + puppeteer-core: '>=24 <26' peerDependenciesMeta: puppeteer-core: optional: true @@ -3935,8 +3801,8 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - workerpool@6.5.1: - resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} + workerpool@10.0.3: + resolution: {integrity: sha512-6z2Iis68Wqth93/G/wJP9u+R3O+d2XTlgWChGCwuT1qLbBsOYueGRZuJ++v3mtDP5KjYdy+WzvWC+VWETSVXJA==} wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} @@ -3954,9 +3820,6 @@ packages: resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.21.3: resolution: {integrity: sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==} engines: {node: '>=10.0.0'} @@ -3989,29 +3852,21 @@ packages: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} - yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} - yargs-unparser@2.0.0: - resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} - engines: {node: '>=10'} - - yargs@16.2.2: - resolution: {integrity: sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==} - engines: {node: '>=10'} + yargs-parser@22.0.0: + resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yargs@17.7.3: - resolution: {integrity: sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==} - engines: {node: '>=12'} + yargs@18.2.0: + resolution: {integrity: sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==} + engines: {node: ^20.19.0 || ^22.12.0 || >=23} yauzl@3.4.0: resolution: {integrity: sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==} @@ -4320,8 +4175,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@electron-internal/extract-zip@1.0.5': {} - '@esbuild/aix-ppc64@0.28.2': optional: true @@ -4749,7 +4602,7 @@ snapshots: '@jest/diff-sequences@30.5.0': {} - '@jest/expect-utils@30.5.1': + '@jest/expect-utils@30.5.2': dependencies: '@jest/get-type': 30.5.0 @@ -4821,20 +4674,12 @@ snapshots: dependencies: spacetrim: 0.11.59 - '@puppeteer/browsers@2.13.2(supports-color@8.1.1)': + '@puppeteer/browsers@3.2.4(yauzl@3.4.0)': dependencies: - debug: 4.4.3(supports-color@8.1.1) - extract-zip: '@electron-internal/extract-zip@1.0.5' - progress: 2.0.3 - proxy-agent: 6.5.0(supports-color@8.1.1) - semver: 7.8.5 - tar-fs: 3.1.3 - yargs: 17.7.3 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - - supports-color + modern-tar: 0.8.5 + yargs: 18.2.0 + optionalDependencies: + yauzl: 3.4.0 '@rolldown/binding-android-arm-eabi@1.2.6': optional: true @@ -4953,7 +4798,7 @@ snapshots: '@stryker-mutator/util': 10.0.0 semver: 7.8.5 tslib: 2.8.1 - vitest: 5.0.3(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(vite@8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)) + vitest: 5.0.3(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(vite@8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)) '@stylistic/eslint-plugin@5.10.0(eslint@10.12.0(jiti@2.7.0)(supports-color@8.1.1))': dependencies: @@ -5007,8 +4852,6 @@ snapshots: dependencies: '@svta/cml-utils': 1.5.0 - '@tootallnate/quickjs-emscripten@0.23.0': {} - '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -5059,7 +4902,7 @@ snapshots: '@types/mocha@10.0.10': {} - '@types/node@20.19.43': + '@types/node@22.20.5': dependencies: undici-types: 6.21.0 @@ -5071,6 +4914,10 @@ snapshots: '@types/sinonjs__fake-timers@8.1.5': {} + '@types/split2@4.2.3': + dependencies: + '@types/node': 26.6.4 + '@types/stack-utils@2.0.3': {} '@types/which@2.0.2': {} @@ -5157,7 +5004,7 @@ snapshots: obug: 2.2.1 std-env: 4.2.0 tinyrainbow: 3.1.1 - vitest: 5.0.3(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(vite@8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)) + vitest: 5.0.3(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(vite@8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)) '@vitest/istanbul-lib-coverage@1.0.1': {} @@ -5165,57 +5012,48 @@ snapshots: dependencies: '@vitest/istanbul-lib-coverage': 1.0.1 - '@vitest/mocker@5.0.3(vite@8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13))': + '@vitest/mocker@5.0.3(vite@8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15))': dependencies: '@jridgewell/trace-mapping': 0.3.31 '@vitest/spy': 5.0.3 estree-walker: 3.0.3 magic-string: 1.3.1 optionalDependencies: - vite: 8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13) - - '@vitest/pretty-format@3.2.7': - dependencies: - tinyrainbow: 2.0.0 + vite: 8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15) - '@vitest/pretty-format@4.1.11': + '@vitest/pretty-format@5.0.3': dependencies: tinyrainbow: 3.1.1 - '@vitest/snapshot@3.2.7': + '@vitest/snapshot@5.0.3': dependencies: - '@vitest/pretty-format': 3.2.7 - magic-string: 0.30.21 - pathe: 2.0.3 - - '@vitest/snapshot@4.1.11': - dependencies: - '@vitest/pretty-format': 4.1.11 - '@vitest/utils': 4.1.11 - magic-string: 0.30.21 + '@vitest/pretty-format': 5.0.3 + '@vitest/utils': 5.0.3 + magic-string: 1.3.1 pathe: 2.0.3 '@vitest/spy@5.0.3': {} - '@vitest/utils@4.1.11': + '@vitest/utils@5.0.3': dependencies: - '@vitest/pretty-format': 4.1.11 + '@vitest/pretty-format': 5.0.3 convert-source-map: 2.0.0 tinyrainbow: 3.1.1 - '@wdio/cli@9.32.0(@types/node@26.6.4)(expect-webdriverio@6.0.10)(supports-color@8.1.1)': + '@wdio/cli@10.0.0(@types/node@26.6.4)(expect-webdriverio@8.0.0-alpha.3(@wdio/types@10.0.0)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)))(supports-color@8.1.1)(yauzl@3.4.0)': dependencies: - '@vitest/snapshot': 3.2.7 - '@wdio/config': 9.32.0(supports-color@8.1.1) - '@wdio/globals': 9.31.3(expect-webdriverio@6.0.10)(webdriverio@9.32.0(supports-color@8.1.1)) - '@wdio/logger': 9.29.1 - '@wdio/protocols': 9.31.5 - '@wdio/types': 9.32.0 - '@wdio/utils': 9.32.0(supports-color@8.1.1) + '@vitest/snapshot': 5.0.3 + '@wdio/config': 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) + '@wdio/globals': 10.0.0(expect-webdriverio@8.0.0-alpha.3(@wdio/types@10.0.0)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)))(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)) + '@wdio/logger': 10.0.0 + '@wdio/protocols': 10.0.0 + '@wdio/session': 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) + '@wdio/types': 10.0.0 + '@wdio/utils': 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) async-exit-hook: 2.0.1 chalk: 5.6.2 chokidar: 4.0.3 - create-wdio: 9.32.0(@types/node@26.6.4) + create-wdio: 10.0.0(@types/node@26.6.4) dotenv: 17.4.2 import-meta-resolve: 4.2.0 lodash.flattendeep: 4.4.0 @@ -5223,66 +5061,89 @@ snapshots: lodash.union: 4.6.0 read-pkg-up: 10.1.0 tsx: 4.23.13 - webdriverio: 9.32.0(supports-color@8.1.1) - yargs: 17.7.3 + webdriverio: 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) + yargs: 18.2.0 transitivePeerDependencies: - '@types/node' + - '@wdio/electron-service' + - '@wdio/visual-service' + - appium - bare-abort-controller - bare-buffer - bufferutil - expect-webdriverio + - proxy-agent - puppeteer-core - react-native-b4a - supports-color - utf-8-validate + - yauzl - '@wdio/config@9.32.0(supports-color@8.1.1)': + '@wdio/config@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)': dependencies: - '@wdio/logger': 9.29.1 - '@wdio/types': 9.32.0 - '@wdio/utils': 9.32.0(supports-color@8.1.1) + '@wdio/logger': 10.0.0 + '@wdio/types': 10.0.0 + '@wdio/utils': 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) deepmerge-ts: 8.0.2 glob: 10.5.0 import-meta-resolve: 4.2.0 jiti: 2.7.0 transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a + - proxy-agent - supports-color + - yauzl - '@wdio/dot-reporter@9.32.0': + '@wdio/display-server@10.0.0': dependencies: - '@wdio/reporter': 9.32.0 - '@wdio/types': 9.32.0 + '@wdio/logger': 10.0.0 + '@wdio/types': 10.0.0 + + '@wdio/dot-reporter@10.0.0': + dependencies: + '@wdio/reporter': 10.0.0 + '@wdio/types': 10.0.0 chalk: 5.6.2 - '@wdio/globals@9.31.3(expect-webdriverio@6.0.10)(webdriverio@9.32.0(supports-color@8.1.1))': + '@wdio/globals@10.0.0(expect-webdriverio@8.0.0-alpha.3(@wdio/types@10.0.0)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)))(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0))': dependencies: - expect-webdriverio: 6.0.10(@wdio/globals@9.31.3)(@wdio/logger@9.29.1)(webdriverio@9.32.0(supports-color@8.1.1)) - webdriverio: 9.32.0(supports-color@8.1.1) + expect-webdriverio: 8.0.0-alpha.3(@wdio/types@10.0.0)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)) + webdriverio: 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) - '@wdio/local-runner@9.32.0(@wdio/globals@9.31.3)(supports-color@8.1.1)(webdriverio@9.32.0(supports-color@8.1.1))': + '@wdio/local-runner@10.0.0(supports-color@8.1.1)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0))(yauzl@3.4.0)': dependencies: - '@types/node': 20.19.43 - '@wdio/logger': 9.29.1 - '@wdio/repl': 9.16.2 - '@wdio/runner': 9.32.0(expect-webdriverio@6.0.10)(supports-color@8.1.1)(webdriverio@9.32.0(supports-color@8.1.1)) - '@wdio/types': 9.32.0 - '@wdio/xvfb': 9.32.0 + '@types/node': 22.20.5 + '@types/split2': 4.2.3 + '@wdio/display-server': 10.0.0 + '@wdio/logger': 10.0.0 + '@wdio/repl': 10.0.0 + '@wdio/runner': 10.0.0(expect-webdriverio@8.0.0-alpha.3(@wdio/types@10.0.0)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)))(supports-color@8.1.1)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0))(yauzl@3.4.0) + '@wdio/types': 10.0.0 exit-hook: 4.0.0 - expect-webdriverio: 6.0.10(@wdio/globals@9.31.3)(@wdio/logger@9.29.1)(webdriverio@9.32.0(supports-color@8.1.1)) + expect-webdriverio: 8.0.0-alpha.3(@wdio/types@10.0.0)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)) split2: 4.2.0 stream-buffers: 3.0.3 transitivePeerDependencies: - - '@wdio/globals' + - '@wdio/electron-service' + - '@wdio/visual-service' + - appium - bare-abort-controller - bare-buffer - bufferutil + - proxy-agent + - puppeteer-core - react-native-b4a - supports-color - utf-8-validate - webdriverio + - yauzl + + '@wdio/logger@10.0.0': + dependencies: + chalk: 5.6.2 + loglevel: 1.9.2 + loglevel-plugin-prefix: 0.8.4 + safe-regex2: 5.1.1 + strip-ansi: 7.2.0 '@wdio/logger@9.29.1': dependencies: @@ -5292,92 +5153,123 @@ snapshots: safe-regex2: 5.1.1 strip-ansi: 7.2.0 - '@wdio/mocha-framework@9.32.0(supports-color@8.1.1)': + '@wdio/mocha-framework@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)': dependencies: '@types/mocha': 10.0.10 - '@types/node': 20.19.43 - '@wdio/logger': 9.29.1 - '@wdio/types': 9.32.0 - '@wdio/utils': 9.32.0(supports-color@8.1.1) - mocha: 10.8.2 + '@types/node': 22.20.5 + '@wdio/logger': 10.0.0 + '@wdio/types': 10.0.0 + '@wdio/utils': 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) + mocha: 12.0.3 transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a + - proxy-agent - supports-color + - yauzl - '@wdio/protocols@9.31.5': {} + '@wdio/protocols@10.0.0': {} - '@wdio/repl@9.16.2': + '@wdio/repl@10.0.0': dependencies: - '@types/node': 20.19.43 + '@types/node': 22.20.5 - '@wdio/reporter@9.32.0': + '@wdio/reporter@10.0.0': dependencies: - '@types/node': 20.19.43 - '@wdio/logger': 9.29.1 - '@wdio/types': 9.32.0 + '@types/node': 22.20.5 + '@wdio/types': 10.0.0 diff: 8.0.4 object-inspect: 1.13.4 - '@wdio/runner@9.32.0(expect-webdriverio@6.0.10)(supports-color@8.1.1)(webdriverio@9.32.0(supports-color@8.1.1))': + '@wdio/runner@10.0.0(expect-webdriverio@8.0.0-alpha.3(@wdio/types@10.0.0)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)))(supports-color@8.1.1)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0))(yauzl@3.4.0)': dependencies: - '@types/node': 20.19.43 - '@wdio/config': 9.32.0(supports-color@8.1.1) - '@wdio/dot-reporter': 9.32.0 - '@wdio/globals': 9.31.3(expect-webdriverio@6.0.10)(webdriverio@9.32.0(supports-color@8.1.1)) - '@wdio/logger': 9.29.1 - '@wdio/types': 9.32.0 - '@wdio/utils': 9.32.0(supports-color@8.1.1) + '@types/node': 22.20.5 + '@wdio/config': 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) + '@wdio/dot-reporter': 10.0.0 + '@wdio/globals': 10.0.0(expect-webdriverio@8.0.0-alpha.3(@wdio/types@10.0.0)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)))(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)) + '@wdio/logger': 10.0.0 + '@wdio/session': 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) + '@wdio/types': 10.0.0 + '@wdio/utils': 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) deepmerge-ts: 8.0.2 - expect-webdriverio: 6.0.10(@wdio/globals@9.31.3)(@wdio/logger@9.29.1)(webdriverio@9.32.0(supports-color@8.1.1)) - webdriver: 9.32.0(supports-color@8.1.1) - webdriverio: 9.32.0(supports-color@8.1.1) + expect-webdriverio: 8.0.0-alpha.3(@wdio/types@10.0.0)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)) + webdriver: 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) + webdriverio: 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) transitivePeerDependencies: + - '@wdio/electron-service' + - '@wdio/visual-service' + - appium - bare-abort-controller - bare-buffer - bufferutil + - proxy-agent + - puppeteer-core - react-native-b4a - supports-color - utf-8-validate + - yauzl - '@wdio/spec-reporter@9.32.0': + '@wdio/session@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)': dependencies: - '@wdio/reporter': 9.32.0 - '@wdio/types': 9.32.0 + '@types/node': 22.20.5 + '@wdio/config': 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) + '@wdio/display-server': 10.0.0 + '@wdio/logger': 10.0.0 + '@wdio/types': 10.0.0 + '@wdio/utils': 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) + acorn: 8.19.0 + expect-webdriverio: 8.0.0-alpha.3(@wdio/types@10.0.0)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)) + get-port: 7.2.0 + image-size: 2.0.4 + picomatch: 4.0.7 + semver: 7.8.5 + tsx: 4.23.15 + webdriverio: 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) + yargs: 18.2.0 + transitivePeerDependencies: + - bare-abort-controller + - bare-buffer + - bufferutil + - proxy-agent + - puppeteer-core + - react-native-b4a + - supports-color + - utf-8-validate + - yauzl + + '@wdio/spec-reporter@10.0.0': + dependencies: + '@wdio/reporter': 10.0.0 + '@wdio/types': 10.0.0 chalk: 5.6.2 easy-table: 1.2.0 pretty-ms: 9.3.1 - '@wdio/types@9.32.0': + '@wdio/types@10.0.0': dependencies: - '@types/node': 20.19.43 + '@types/node': 22.20.5 - '@wdio/utils@9.32.0(supports-color@8.1.1)': + '@wdio/utils@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)': dependencies: - '@puppeteer/browsers': 2.13.2(supports-color@8.1.1) - '@wdio/logger': 9.29.1 - '@wdio/types': 9.32.0 + '@puppeteer/browsers': 3.2.4(yauzl@3.4.0) + '@types/split2': 4.2.3 + '@wdio/logger': 10.0.0 + '@wdio/types': 10.0.0 + aria-query: 5.3.2 decamelize: 6.0.1 deepmerge-ts: 8.0.2 - edgedriver: 6.3.1(supports-color@8.1.1) - geckodriver: 6.1.1(supports-color@8.1.1) + edgedriver: 7.0.0(supports-color@8.1.1) + electron-to-chromium: 1.5.443 + geckodriver: 7.0.0(supports-color@8.1.1) get-port: 7.2.0 import-meta-resolve: 4.2.0 locate-app: 2.5.0 mitt: 3.0.1 - safaridriver: 1.0.1 + safaridriver: 2.0.0 split2: 4.2.0 wait-port: 1.1.0(supports-color@8.1.1) transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a + - proxy-agent - supports-color - - '@wdio/xvfb@9.32.0': - dependencies: - '@wdio/logger': 9.29.1 + - yauzl '@zip.js/zip.js@2.14.0': {} @@ -5461,8 +5353,6 @@ snapshots: dependencies: string-width: 4.2.3 - ansi-colors@4.1.3: {} - ansi-regex@5.0.1: {} ansi-regex@6.3.0: {} @@ -5475,11 +5365,6 @@ snapshots: ansi-styles@6.2.3: {} - anymatch@3.1.3: - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.2 - archiver@8.0.0: dependencies: async: 3.2.6 @@ -5506,10 +5391,6 @@ snapshots: assertion-error@2.0.1: {} - ast-types@0.13.4: - dependencies: - tslib: 2.8.1 - ast-v8-to-istanbul@1.0.6: dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -5566,12 +5447,8 @@ snapshots: baseline-browser-mapping@2.11.26: {} - basic-ftp@6.2.1: {} - bcp-47-match@2.0.3: {} - binary-extensions@2.3.0: {} - bluebird@3.7.2: {} boolbase@1.0.0: {} @@ -5600,10 +5477,6 @@ snapshots: dependencies: balanced-match: 4.0.4 - braces@3.0.3: - dependencies: - fill-range: 7.1.1 - browser-stdout@1.3.1: {} browserslist@4.29.3: @@ -5649,8 +5522,6 @@ snapshots: callsites@3.1.0: {} - camelcase@6.3.0: {} - camelcase@8.0.0: {} caniuse-lite@1.0.30001814: {} @@ -5689,22 +5560,14 @@ snapshots: undici: 7.29.1 whatwg-mimetype: 4.0.0 - chokidar@3.6.0: - dependencies: - anymatch: 3.1.3 - braces: 3.0.3 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.3 - chokidar@4.0.3: dependencies: readdirp: 4.1.2 + chokidar@5.0.0: + dependencies: + readdirp: 5.1.1 + chrome-launcher@1.2.0(supports-color@8.1.1): dependencies: '@types/node': 26.6.4 @@ -5720,17 +5583,17 @@ snapshots: cli-width@4.1.0: {} - cliui@7.0.4: + cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - cliui@8.0.1: + cliui@9.0.1: dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 + string-width: 7.2.0 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 clone@1.0.4: {} @@ -5788,7 +5651,7 @@ snapshots: crc-32: 1.2.2 readable-stream: 4.7.0 - create-wdio@9.32.0(@types/node@26.6.4): + create-wdio@10.0.0(@types/node@26.6.4): dependencies: chalk: 5.6.2 commander: 14.0.3 @@ -5799,10 +5662,9 @@ snapshots: inquirer: 12.11.1(@types/node@26.6.4) normalize-package-data: 7.0.1 read-pkg-up: 10.1.0 - recursive-readdir: 2.2.3 semver: 7.8.5 type-fest: 4.41.0 - yargs: 17.7.3 + yargs: 18.2.0 transitivePeerDependencies: - '@types/node' @@ -5852,8 +5714,6 @@ snapshots: - '@svta/cml-structured-field-values' - '@svta/cml-utils' - data-uri-to-buffer@6.0.2: {} - debounce@1.2.1: {} debug@4.3.7(supports-color@8.1.1): @@ -5868,8 +5728,6 @@ snapshots: optionalDependencies: supports-color: 8.1.1 - decamelize@4.0.0: {} - decamelize@6.0.1: {} deep-eql@5.0.2: {} @@ -5895,12 +5753,6 @@ snapshots: define-lazy-prop@3.0.0: {} - degenerator@5.0.1: - dependencies: - ast-types: 0.13.4 - escodegen: 2.1.0 - esprima: 4.0.1 - des.js@1.1.0: dependencies: inherits: 2.0.4 @@ -5910,10 +5762,10 @@ snapshots: diff-match-patch@1.0.5: {} - diff@5.2.2: {} - diff@8.0.4: {} + diff@9.0.0: {} + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -5961,7 +5813,7 @@ snapshots: '@types/which': 2.0.2 which: 2.0.2 - edgedriver@6.3.1(supports-color@8.1.1): + edgedriver@7.0.0(supports-color@8.1.1): dependencies: '@wdio/logger': 9.29.1 '@zip.js/zip.js': 2.14.0 @@ -5992,10 +5844,6 @@ snapshots: iconv-lite: 0.6.3 whatwg-encoding: 3.1.1 - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - entities@4.5.0: {} entities@6.0.1: {} @@ -6055,14 +5903,6 @@ snapshots: escape-string-regexp@4.0.0: {} - escodegen@2.1.0: - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionalDependencies: - source-map: 0.6.1 - eslint-plugin-no-unsanitized@4.1.5(eslint@10.12.0(jiti@2.7.0)(supports-color@8.1.1)): dependencies: eslint: 10.12.0(jiti@2.7.0)(supports-color@8.1.1) @@ -6231,31 +6071,28 @@ snapshots: expect-type@1.4.0: {} - expect-webdriverio@6.0.10(@wdio/globals@9.31.3)(@wdio/logger@9.29.1)(webdriverio@9.32.0(supports-color@8.1.1)): + expect-webdriverio@8.0.0-alpha.3(@wdio/types@10.0.0)(webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0)): dependencies: - '@vitest/snapshot': 4.1.11 - '@wdio/globals': 9.31.3(expect-webdriverio@6.0.10)(webdriverio@9.32.0(supports-color@8.1.1)) - '@wdio/logger': 9.29.1 + '@vitest/snapshot': 5.0.3 + '@wdio/types': 10.0.0 deep-eql: 5.0.2 - expect: 30.5.1 - jest-matcher-utils: 30.5.1 - webdriverio: 9.32.0(supports-color@8.1.1) + expect: 30.5.2 + jest-matcher-utils: 30.5.2 + webdriverio: 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) - expect@30.5.1: + expect@30.5.2: dependencies: - '@jest/expect-utils': 30.5.1 + '@jest/expect-utils': 30.5.2 '@jest/get-type': 30.5.0 - jest-matcher-utils: 30.5.1 + jest-matcher-utils: 30.5.2 jest-message-util: 30.5.1 - jest-mock: 30.5.1 + jest-mock: 30.5.2 jest-util: 30.5.1 fast-check@4.10.2: dependencies: pure-rand: 8.4.2 - fast-deep-equal@2.0.1: {} - fast-deep-equal@3.1.3: {} fast-fifo@1.3.2: {} @@ -6298,9 +6135,7 @@ snapshots: dependencies: minimatch: 5.1.9 - fill-range@7.1.1: - dependencies: - to-regex-range: 5.0.1 + find-up-simple@1.0.1: {} find-up@5.0.0: dependencies: @@ -6333,8 +6168,6 @@ snapshots: flatted: 3.4.4 hookified: 1.15.1 - flat@5.0.2: {} - flatted@3.4.4: {} flru@1.0.2: {} @@ -6350,8 +6183,6 @@ snapshots: jsonfile: 6.2.1 universalify: 2.0.1 - fs.realpath@1.0.0: {} - fsevents@2.3.3: optional: true @@ -6366,17 +6197,6 @@ snapshots: which: 4.0.0 winreg: 0.0.12 - geckodriver@6.1.1(supports-color@8.1.1): - dependencies: - '@wdio/logger': 9.29.1 - '@zip.js/zip.js': 2.14.0 - decamelize: 6.0.1 - http-proxy-agent: 7.0.2(supports-color@8.1.1) - https-proxy-agent: 7.0.6(supports-color@8.1.1) - modern-tar: 0.7.7 - transitivePeerDependencies: - - supports-color - geckodriver@7.0.0(supports-color@8.1.1): dependencies: '@wdio/logger': 9.29.1 @@ -6419,20 +6239,8 @@ snapshots: '@sec-ant/readable-stream': 0.4.1 is-stream: 4.0.1 - get-uri@6.0.5(supports-color@8.1.1): - dependencies: - basic-ftp: 6.2.1 - data-uri-to-buffer: 6.0.2 - debug: 4.4.3(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - gif.js@0.2.0(patch_hash=7f90135cc94c36690ec1e9725f81b9ec0386d2b62e08e8e6defbd864d58cc1ae): {} - glob-parent@5.1.2: - dependencies: - is-glob: 4.0.3 - glob-parent@6.0.2: dependencies: is-glob: 4.0.3 @@ -6446,13 +6254,11 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - glob@8.1.0: + glob@13.0.6: dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 5.1.9 - once: 1.4.0 + minimatch: 10.2.6 + minipass: 7.1.3 + path-scurry: 2.0.2 global-directory@4.0.1: dependencies: @@ -6482,8 +6288,6 @@ snapshots: dependencies: function-bind: 1.1.2 - he@1.2.0: {} - hls.js@1.7.3(patch_hash=7bf3c8187174dcdd9951f64f89092432f2c76698ca18dc4b55b45b62c78c9b5a): {} hookified@1.15.1: {} @@ -6556,11 +6360,6 @@ snapshots: index-to-position@1.2.0: {} - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - inherits@2.0.4: {} ini@1.3.8: {} @@ -6581,14 +6380,8 @@ snapshots: optionalDependencies: '@types/node': 26.6.4 - ip-address@10.7.2: {} - is-arrayish@0.2.1: {} - is-binary-path@2.1.0: - dependencies: - binary-extensions: 2.3.0 - is-docker@2.2.1: {} is-docker@3.0.0: {} @@ -6616,12 +6409,8 @@ snapshots: is-npm@6.1.0: {} - is-number@7.0.0: {} - is-path-inside@4.0.0: {} - is-plain-obj@2.1.0: {} - is-plain-obj@4.1.0: {} is-stream@4.0.1: {} @@ -6660,18 +6449,18 @@ snapshots: filelist: 1.0.6 picocolors: 1.1.1 - jest-diff@30.5.1: + jest-diff@30.5.2: dependencies: '@jest/diff-sequences': 30.5.0 '@jest/get-type': 30.5.0 chalk: 4.1.2 pretty-format: 30.5.1 - jest-matcher-utils@30.5.1: + jest-matcher-utils@30.5.2: dependencies: '@jest/get-type': 30.5.0 chalk: 4.1.2 - jest-diff: 30.5.1 + jest-diff: 30.5.2 pretty-format: 30.5.1 jest-message-util@30.5.1: @@ -6687,9 +6476,9 @@ snapshots: slash: 3.0.0 stack-utils: 2.0.6 - jest-mock@30.5.1: + jest-mock@30.5.2: dependencies: - '@jest/expect-utils': 30.5.1 + '@jest/expect-utils': 30.5.2 '@jest/types': 30.5.1 '@types/node': 26.6.4 jest-util: 30.5.1 @@ -6913,24 +6702,13 @@ snapshots: lodash.union@4.6.0: {} - lodash.zip@4.2.0: {} - - log-symbols@4.1.0: - dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 - loglevel-plugin-prefix@0.8.4: {} loglevel@1.9.2: {} lru-cache@10.4.3: {} - lru-cache@7.18.3: {} - - magic-string@0.30.21: - dependencies: - '@jridgewell/sourcemap-codec': 1.6.0 + lru-cache@11.5.3: {} magic-string@1.3.1: dependencies: @@ -6979,30 +6757,24 @@ snapshots: mitt@3.0.1: {} - mocha@10.8.2: + mocha@12.0.3: dependencies: - ansi-colors: 4.1.3 browser-stdout: 1.3.1 - chokidar: 3.6.0 + chokidar: 5.0.0 debug: 4.4.3(supports-color@8.1.1) - diff: 5.2.2 - escape-string-regexp: 4.0.0 - find-up: 5.0.0 - glob: 8.1.0 - he: 1.2.0 - js-yaml: 4.3.2 - log-symbols: 4.1.0 - minimatch: 5.1.9 + diff: 9.0.0 + find-up-simple: 1.0.1 + glob: 13.0.6 + is-path-inside: 4.0.0 + is-unicode-supported: 0.1.0 + js-yaml: 5.4.2 + minimatch: 10.2.6 ms: 2.1.3 + picocolors: 1.1.1 serialize-javascript: 7.1.2 - strip-json-comments: 3.1.1 + strip-json-comments: 5.0.3 supports-color: 8.1.1 - workerpool: 6.5.1 - yargs: 16.2.2 - yargs-parser: 20.2.9 - yargs-unparser: 2.0.0 - - modern-tar@0.7.7: {} + workerpool: 10.0.3 modern-tar@0.8.5: {} @@ -7037,8 +6809,6 @@ snapshots: natural-compare@1.4.0: {} - netmask@2.1.1: {} - node-forge@1.4.0: {} node-releases@2.0.57: {} @@ -7072,10 +6842,6 @@ snapshots: on-exit-leak-free@2.1.2: {} - once@1.4.0: - dependencies: - wrappy: 1.0.2 - open@11.0.3: dependencies: default-browser: 5.5.1 @@ -7110,24 +6876,6 @@ snapshots: dependencies: p-limit: 4.0.0 - pac-proxy-agent@7.2.0(supports-color@8.1.1): - dependencies: - '@tootallnate/quickjs-emscripten': 0.23.0 - agent-base: 7.1.4 - debug: 4.4.3(supports-color@8.1.1) - get-uri: 6.0.5(supports-color@8.1.1) - http-proxy-agent: 7.0.2(supports-color@8.1.1) - https-proxy-agent: 7.0.6(supports-color@8.1.1) - pac-resolver: 7.0.1 - socks-proxy-agent: 8.0.5(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - - pac-resolver@7.0.1: - dependencies: - degenerator: 5.0.1 - netmask: 2.1.1 - package-json-from-dist@1.0.1: {} package-json@10.0.1: @@ -7187,14 +6935,17 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.3 + path-scurry@2.0.2: + dependencies: + lru-cache: 11.5.3 + minipass: 7.1.3 + pathe@2.0.3: {} pend@1.2.0: {} picocolors@1.1.1: {} - picomatch@2.3.2: {} - picomatch@4.0.7: {} pino-abstract-transport@3.0.0: @@ -7254,26 +7005,6 @@ snapshots: proto-list@1.2.4: {} - proxy-agent@6.5.0(supports-color@8.1.1): - dependencies: - agent-base: 7.1.4 - debug: 4.4.3(supports-color@8.1.1) - http-proxy-agent: 7.0.2(supports-color@8.1.1) - https-proxy-agent: 7.0.6(supports-color@8.1.1) - lru-cache: 7.18.3 - pac-proxy-agent: 7.2.0(supports-color@8.1.1) - proxy-from-env: 1.1.0 - socks-proxy-agent: 8.0.5(supports-color@8.1.1) - transitivePeerDependencies: - - supports-color - - proxy-from-env@1.1.0: {} - - pump@3.0.4: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - punycode@2.3.1: {} pupa@3.3.0: @@ -7341,20 +7072,14 @@ snapshots: dependencies: minimatch: 10.2.6 - readdirp@3.6.0: - dependencies: - picomatch: 2.3.2 - readdirp@4.1.2: {} + readdirp@5.1.1: {} + real-require@0.2.0: {} real-require@1.0.0: {} - recursive-readdir@2.2.3: - dependencies: - minimatch: 3.1.5 - registry-auth-token@5.1.1: dependencies: '@pnpm/npm-conf': 3.0.3 @@ -7369,10 +7094,6 @@ snapshots: resolve-from@4.0.0: {} - resq@1.11.0: - dependencies: - fast-deep-equal: 2.0.1 - ret@0.5.0: {} rgb2hex@0.2.5: {} @@ -7406,7 +7127,7 @@ snapshots: dependencies: tslib: 2.8.1 - safaridriver@1.0.1: {} + safaridriver@2.0.0: {} safe-buffer@5.1.2: {} @@ -7474,21 +7195,6 @@ snapshots: slash@3.0.0: {} - smart-buffer@4.2.0: {} - - socks-proxy-agent@8.0.5(supports-color@8.1.1): - dependencies: - agent-base: 7.1.4 - debug: 4.4.3(supports-color@8.1.1) - socks: 2.8.10 - transitivePeerDependencies: - - supports-color - - socks@2.8.10: - dependencies: - ip-address: 10.7.2 - smart-buffer: 4.2.0 - sonic-boom@4.2.1: dependencies: atomic-sleep: 1.0.0 @@ -7563,6 +7269,11 @@ snapshots: get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 + string-width@8.3.0: + dependencies: + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 @@ -7612,18 +7323,6 @@ snapshots: dependencies: has-flag: 4.0.0 - tar-fs@3.1.3: - dependencies: - pump: 3.0.4 - tar-stream: 3.2.1 - optionalDependencies: - bare-fs: 4.8.1 - bare-path: 3.1.2 - transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - - react-native-b4a - tar-stream@3.2.1: dependencies: b4a: 1.8.1 @@ -7663,16 +7362,10 @@ snapshots: fdir: 6.5.0(picomatch@4.0.7) picomatch: 4.0.7 - tinyrainbow@2.0.0: {} - tinyrainbow@3.1.1: {} tmp@0.2.7: {} - to-regex-range@5.0.1: - dependencies: - is-number: 7.0.0 - tree-kill@1.2.2: {} tslib@2.8.1: {} @@ -7683,6 +7376,12 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tsx@4.23.15: + dependencies: + esbuild: 0.28.2 + optionalDependencies: + fsevents: 2.3.3 + tunnel@0.0.6: {} type-check@0.4.0: @@ -7734,10 +7433,10 @@ snapshots: undici-types@8.9.0: {} - undici@6.28.1: {} - undici@7.29.1: {} + undici@8.11.2: {} + unicorn-magic@0.3.0: {} universalify@2.0.1: {} @@ -7780,7 +7479,7 @@ snapshots: verkit@0.3.2: {} - vite@8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13): + vite@8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15): dependencies: lightningcss: 1.33.0 picomatch: 4.0.7 @@ -7792,12 +7491,12 @@ snapshots: esbuild: 0.28.2 fsevents: 2.3.3 jiti: 2.7.0 - tsx: 4.23.13 + tsx: 4.23.15 - vitest@5.0.3(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(vite@8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)): + vitest@5.0.3(@types/node@26.6.4)(@vitest/coverage-v8@5.0.3)(vite@8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)): dependencies: '@types/chai': 5.2.3 - '@vitest/mocker': 5.0.3(vite@8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13)) + '@vitest/mocker': 5.0.3(vite@8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15)) chai: 6.2.2 es-module-lexer: 2.3.2 expect-type: 1.4.0 @@ -7808,7 +7507,7 @@ snapshots: tinybench: 6.1.4 tinyexec: 1.3.0 tinyglobby: 0.2.17 - vite: 8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.13) + vite: 8.2.2(@types/node@26.6.4)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15) why-is-node-running: 3.2.1 optionalDependencies: '@types/node': 26.6.4 @@ -7868,37 +7567,36 @@ snapshots: - safe-compare - supports-color - webdriver@9.32.0(supports-color@8.1.1): + webdriver@10.0.0(supports-color@8.1.1)(yauzl@3.4.0): dependencies: - '@types/node': 20.19.43 + '@types/node': 22.20.5 '@types/ws': 8.18.1 - '@wdio/config': 9.32.0(supports-color@8.1.1) - '@wdio/logger': 9.29.1 - '@wdio/protocols': 9.31.5 - '@wdio/types': 9.32.0 - '@wdio/utils': 9.32.0(supports-color@8.1.1) + '@wdio/config': 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) + '@wdio/logger': 10.0.0 + '@wdio/protocols': 10.0.0 + '@wdio/types': 10.0.0 + '@wdio/utils': 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) deepmerge-ts: 8.0.2 https-proxy-agent: 7.0.6(supports-color@8.1.1) - undici: 6.28.1 + undici: 8.11.2 ws: 8.21.3 transitivePeerDependencies: - - bare-abort-controller - - bare-buffer - bufferutil - - react-native-b4a + - proxy-agent - supports-color - utf-8-validate + - yauzl - webdriverio@9.32.0(supports-color@8.1.1): + webdriverio@10.0.0(supports-color@8.1.1)(yauzl@3.4.0): dependencies: - '@types/node': 20.19.43 + '@types/node': 22.20.5 '@types/sinonjs__fake-timers': 8.1.5 - '@wdio/config': 9.32.0(supports-color@8.1.1) - '@wdio/logger': 9.29.1 - '@wdio/protocols': 9.31.5 - '@wdio/repl': 9.16.2 - '@wdio/types': 9.32.0 - '@wdio/utils': 9.32.0(supports-color@8.1.1) + '@wdio/config': 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) + '@wdio/logger': 10.0.0 + '@wdio/protocols': 10.0.0 + '@wdio/repl': 10.0.0 + '@wdio/types': 10.0.0 + '@wdio/utils': 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) archiver: 8.0.0 aria-query: 5.3.2 cheerio: 1.2.0 @@ -7909,20 +7607,20 @@ snapshots: is-plain-obj: 4.1.0 jszip: 3.10.2 lodash.clonedeep: 4.5.0 - lodash.zip: 4.2.0 query-selector-shadow-dom: 1.0.1 - resq: 1.11.0 rgb2hex: 0.2.5 serialize-error: 12.0.0 urlpattern-polyfill: 10.1.0 - webdriver: 9.32.0(supports-color@8.1.1) + webdriver: 10.0.0(supports-color@8.1.1)(yauzl@3.4.0) transitivePeerDependencies: - bare-abort-controller - bare-buffer - bufferutil + - proxy-agent - react-native-b4a - supports-color - utf-8-validate + - yauzl whatwg-encoding@3.1.1: dependencies: @@ -7954,7 +7652,7 @@ snapshots: word-wrap@1.2.5: {} - workerpool@6.5.1: {} + workerpool@10.0.3: {} wrap-ansi@6.2.0: dependencies: @@ -7980,8 +7678,6 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.2.0 - wrappy@1.0.2: {} - ws@8.21.3: {} wsl-utils@1.0.0: @@ -8000,26 +7696,9 @@ snapshots: y18n@5.0.8: {} - yargs-parser@20.2.9: {} - yargs-parser@21.1.1: {} - yargs-unparser@2.0.0: - dependencies: - camelcase: 6.3.0 - decamelize: 4.0.0 - flat: 5.0.2 - is-plain-obj: 2.1.0 - - yargs@16.2.2: - dependencies: - cliui: 7.0.4 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 20.2.9 + yargs-parser@22.0.0: {} yargs@17.7.2: dependencies: @@ -8031,15 +7710,14 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yargs@17.7.3: + yargs@18.2.0: dependencies: - cliui: 8.0.1 + cliui: 9.0.1 escalade: 3.2.0 get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 + string-width: 8.3.0 y18n: 5.0.8 - yargs-parser: 21.1.1 + yargs-parser: 22.0.0 yauzl@3.4.0: dependencies: diff --git a/tests/e2e/bidi.mjs b/tests/e2e/bidi.mjs index eae531243..4d6e3360e 100644 --- a/tests/e2e/bidi.mjs +++ b/tests/e2e/bidi.mjs @@ -4,7 +4,7 @@ // own realm. WebdriverIO connects to BiDi when the session starts and gives up after 10 s // ("Could not connect to Bidi protocol of any candidate url in time", seen on the Windows // runner). It then carries on over WebDriver classic without failing: browser.isBidi is -// false, and execute() and executeAsync() go to /execute/sync and /execute/async (9.32.0; +// false, and execute() goes to /execute/sync or /execute/async (9.32.0 and 10; // only that connect, again on reloadSession(), decides isBidi). There a script runs in a sandbox, // so its import() gets a module instance of its own (subtitle-search.e2e.mjs stubbed a // RequestUtils the player never used, and saw no request), an ArrayBuffer made in it is diff --git a/tests/e2e/classic-helpers.mjs b/tests/e2e/classic-helpers.mjs index f0d43ce55..9be96ee80 100644 --- a/tests/e2e/classic-helpers.mjs +++ b/tests/e2e/classic-helpers.mjs @@ -7,6 +7,18 @@ import {browser} from '@wdio/globals'; import {EXTENSION_ID} from './wdio.extension.conf.mjs'; +/** + * Runs a function that reports through a callback, fn(...args, done), and gives what it + * passed to done: WebdriverIO 10 has no executeAsync. A script string, as execute() sends + * every function: over BiDi and over classic, it awaits the promise the script returns. + * @param {Function} fn - Called as fn(...args, done). + * @param {...*} args - Serialisable arguments. + * @return {Promise<*>} + */ +export function executeWithDone(fn, ...args) { + return browser.execute(`return new Promise((done) => (${fn.toString()}).apply(null, [...arguments, done]));`, ...args); +} + /** * Runs an async function in Firefox's chrome context. * @param {Function} fn - Called as fn(...args, done). @@ -16,7 +28,7 @@ import {EXTENSION_ID} from './wdio.extension.conf.mjs'; export async function inChrome(fn, ...args) { await browser.setMozContext('chrome'); try { - return await browser.executeAsync(fn, ...args); + return await executeWithDone(fn, ...args); } finally { await browser.setMozContext('content'); } diff --git a/tests/e2e/classic-specs/auto-enable-list.e2e.mjs b/tests/e2e/classic-specs/auto-enable-list.e2e.mjs index db4c859ac..1a3318c66 100644 --- a/tests/e2e/classic-specs/auto-enable-list.e2e.mjs +++ b/tests/e2e/classic-specs/auto-enable-list.e2e.mjs @@ -36,14 +36,14 @@ let siteHandle; */ async function setOptions(options) { await browser.switchToWindow(extHandle); - await browser.executeAsync((json, done) => { + await browser.execute((json) => new Promise((done) => { chrome.storage.local.set({options: json}, () => { chrome.runtime.sendMessage({type: 'LOAD_OPTIONS'}, () => { void chrome.runtime.lastError; done(true); }); }); - }, JSON.stringify(options)); + }), JSON.stringify(options)); // loadOptions rebuilds the redirect rule after it answers. await browser.pause(1000); } @@ -55,7 +55,7 @@ async function setOptions(options) { */ async function tabMode(prefix) { await browser.switchToWindow(extHandle); - return await browser.executeAsync((prefix, done) => { + return await browser.execute((prefix) => new Promise((done) => { chrome.tabs.query({}, (tabs) => { const tab = tabs.find((t) => t.url && t.url.startsWith(prefix)); if (!tab) { @@ -64,7 +64,7 @@ async function tabMode(prefix) { } chrome.action.getBadgeText({tabId: tab.id}, (badge) => done(badge === 'On' ? 'on' : 'off')); }); - }, prefix); + }), prefix); } /** diff --git a/tests/e2e/classic-specs/content-cleanup.e2e.mjs b/tests/e2e/classic-specs/content-cleanup.e2e.mjs index ab2021bea..d23d31603 100644 --- a/tests/e2e/classic-specs/content-cleanup.e2e.mjs +++ b/tests/e2e/classic-specs/content-cleanup.e2e.mjs @@ -332,21 +332,21 @@ const clickToolbar = () => clickToolbarIn(siteHandle); /** @return {Promise} The site tab's id. */ async function siteTabId() { await browser.switchToWindow(extHandle); - return await browser.executeAsync((site, done) => { + return await browser.execute((site) => new Promise((done) => { chrome.tabs.query({}, (tabs) => { const tab = tabs.find((t) => t.url && t.url.startsWith(site + '/')); done(tab ? tab.id : null); }); - }, SITE); + }), SITE); } /** @return {Promise} The site tab's mode, 'on' or 'off'. */ async function tabMode() { const tabId = await siteTabId(); await browser.switchToWindow(extHandle); - return await browser.executeAsync((tabId, done) => { + return await browser.execute((tabId) => new Promise((done) => { chrome.action.getBadgeText({tabId}, (badge) => done(badge === 'On' ? 'on' : 'off')); - }, tabId); + }), tabId); } /** @@ -359,14 +359,14 @@ async function tabMode() { async function sendToPage(message, within = 0) { const tabId = await siteTabId(); await browser.switchToWindow(extHandle); - return await browser.executeAsync((tabId, message, within, done) => { + return await browser.execute((tabId, message, within) => new Promise((done) => { if (within) { setTimeout(() => done({error: 'no answer'}), within); } chrome.tabs.sendMessage(tabId, message, {frameId: 0}, (response) => { done(chrome.runtime.lastError ? {error: chrome.runtime.lastError.message} : response); }); - }, tabId, message, within); + }), tabId, message, within); } /** diff --git a/tests/e2e/classic-specs/content-overlays.e2e.mjs b/tests/e2e/classic-specs/content-overlays.e2e.mjs index 97abbba7e..63b2f4ba9 100644 --- a/tests/e2e/classic-specs/content-overlays.e2e.mjs +++ b/tests/e2e/classic-specs/content-overlays.e2e.mjs @@ -217,13 +217,13 @@ const clickToolbar = () => clickToolbarIn(siteHandle); /** @return {Promise} The site tab's mode, 'on' or 'off'. */ async function tabMode() { await browser.switchToWindow(extHandle); - const mode = await browser.executeAsync((site, done) => { + const mode = await browser.execute((site) => new Promise((done) => { chrome.tabs.query({}, (tabs) => { const tab = tabs.find((t) => t.url && t.url.startsWith(site + '/')); if (!tab) return done('none'); chrome.action.getBadgeText({tabId: tab.id}, (badge) => done(badge === 'On' ? 'on' : 'off')); }); - }, SITE); + }), SITE); await browser.switchToWindow(siteHandle); return mode; } diff --git a/tests/e2e/classic-specs/mpv-shortcut.e2e.mjs b/tests/e2e/classic-specs/mpv-shortcut.e2e.mjs index 4c0459120..aacda3061 100644 --- a/tests/e2e/classic-specs/mpv-shortcut.e2e.mjs +++ b/tests/e2e/classic-specs/mpv-shortcut.e2e.mjs @@ -157,12 +157,12 @@ async function tabMode(prefix = SITE + '/') { */ async function tabIdOf(prefix) { await browser.switchToWindow(extHandle); - return await browser.executeAsync((site, done) => { + return await browser.execute((site) => new Promise((done) => { chrome.tabs.query({}, (tabs) => { const tab = tabs.find((t) => t.url && t.url.startsWith(site)); done(tab ? tab.id : null); }); - }, prefix); + }), prefix); } /** @@ -172,7 +172,7 @@ async function tabIdOf(prefix) { */ async function modeOfTab(tabId) { await browser.switchToWindow(extHandle); - return await browser.executeAsync((tabId, done) => { + return await browser.execute((tabId) => new Promise((done) => { chrome.action.getTitle({tabId}, (title) => { chrome.action.getBadgeText({tabId}, (badge) => { if (title.includes('MPV')) done('mpv'); @@ -180,7 +180,7 @@ async function modeOfTab(tabId) { else done('off'); }); }); - }, tabId); + }), tabId); } /** @@ -189,9 +189,9 @@ async function modeOfTab(tabId) { */ async function urlOfTab(tabId) { await browser.switchToWindow(extHandle); - return await browser.executeAsync((tabId, done) => { + return await browser.execute((tabId) => new Promise((done) => { chrome.tabs.get(tabId, (tab) => done(tab.url || '')); - }, tabId); + }), tabId); } /** @@ -328,14 +328,14 @@ async function mainPlaying() { */ async function setOptions(options) { await browser.switchToWindow(extHandle); - await browser.executeAsync((json, done) => { + await browser.execute((json) => new Promise((done) => { chrome.storage.local.set({options: json}, () => { chrome.runtime.sendMessage({type: 'LOAD_OPTIONS'}, () => { void chrome.runtime.lastError; done(true); }); }); - }, JSON.stringify(options)); + }), JSON.stringify(options)); await browser.pause(500); } @@ -631,7 +631,7 @@ describe('The MPV keyboard shortcut (Alt+F)', function() { // MPV mode on, allowlist empty: nothing here auto-starts MPV, so every // switch below is the shortcut's doing. - await browser.executeAsync((done) => { + await browser.execute(() => new Promise((done) => { chrome.storage.local.set({ options: JSON.stringify({mpvMode: true, mpvAllowlist: []}), }, () => { @@ -640,7 +640,7 @@ describe('The MPV keyboard shortcut (Alt+F)', function() { done(true); }); }); - }); + })); await browser.pause(1000); siteHandle = (await browser.getWindowHandles()).find((h) => h !== extHandle); @@ -779,9 +779,9 @@ describe('The MPV keyboard shortcut (Alt+F)', function() { // took a second press. Each key now has its own command. it('binds Ctrl+Shift+F to the player\'s own command, not the toolbar button', async function() { await browser.switchToWindow(extHandle); - const commands = await browser.executeAsync((done) => { + const commands = await browser.execute(() => new Promise((done) => { chrome.commands.getAll().then((all) => done(all.map((c) => [c.name, c.shortcut || '']))); - }); + })); expect(Object.fromEntries(commands)).toEqual({ toggle_player: 'Ctrl+Shift+F', toggle_mpv: 'Alt+F', diff --git a/tests/e2e/classic-specs/mpv-suspend.e2e.mjs b/tests/e2e/classic-specs/mpv-suspend.e2e.mjs index 6fe5cb888..b479eb686 100644 --- a/tests/e2e/classic-specs/mpv-suspend.e2e.mjs +++ b/tests/e2e/classic-specs/mpv-suspend.e2e.mjs @@ -112,7 +112,7 @@ describe('MPV mode across a suspended background', function() { return false; }, {timeout: 20000, timeoutMsg: 'the extension page never opened'}); - await browser.executeAsync((site, done) => { + await browser.execute((site) => new Promise((done) => { chrome.storage.local.set({ options: JSON.stringify({mpvMode: true, mpvAllowlist: [site]}), }, () => { @@ -121,7 +121,7 @@ describe('MPV mode across a suspended background', function() { done(true); }); }); - }, SITE); + }), SITE); await browser.pause(1000); await browser.closeWindow(); await browser.switchToWindow((await browser.getWindowHandles())[0]); diff --git a/tests/e2e/classic-specs/shortcut-page-cancels.e2e.mjs b/tests/e2e/classic-specs/shortcut-page-cancels.e2e.mjs index 4469cbe01..8ecf161d5 100644 --- a/tests/e2e/classic-specs/shortcut-page-cancels.e2e.mjs +++ b/tests/e2e/classic-specs/shortcut-page-cancels.e2e.mjs @@ -201,7 +201,7 @@ async function pageKeys() { */ async function tabMode() { await browser.switchToWindow(extHandle); - return await browser.executeAsync((site, done) => { + return await browser.execute((site) => new Promise((done) => { chrome.tabs.query({}, (tabs) => { const tab = tabs.find((t) => t.url && t.url.startsWith(site)); if (!tab) { @@ -216,7 +216,7 @@ async function tabMode() { }); }); }); - }, SITE + '/'); + }), SITE + '/'); } /** @@ -262,12 +262,12 @@ async function expectModeKept(expected, when) { */ async function rebind(name, shortcut) { await browser.switchToWindow(extHandle); - await browser.executeAsync((name, shortcut, done) => { + await browser.execute((name, shortcut) => new Promise((done) => { const p = shortcut === null ? chrome.commands.reset(name) : chrome.commands.update({name, shortcut}); p.then(() => done(true), (e) => done(String(e))); - }, name, shortcut); + }), name, shortcut); } describe('FastStream shortcuts on a page that cancels them', function() { @@ -308,7 +308,7 @@ describe('FastStream shortcuts on a page that cancels them', function() { }, {timeout: 20000, timeoutMsg: 'the extension page never opened'}); // MPV mode on, allowlist empty: every switch below is a shortcut's doing. - await browser.executeAsync((done) => { + await browser.execute(() => new Promise((done) => { chrome.storage.local.set({ options: JSON.stringify({mpvMode: true, mpvAllowlist: []}), }, () => { @@ -317,7 +317,7 @@ describe('FastStream shortcuts on a page that cancels them', function() { done(true); }); }); - }); + })); await browser.pause(1000); siteHandle = (await browser.getWindowHandles()).find((h) => h !== extHandle); diff --git a/tests/e2e/classic-specs/toolbar-cycle-mpv.e2e.mjs b/tests/e2e/classic-specs/toolbar-cycle-mpv.e2e.mjs index 58ec120f8..6b2f4c926 100644 --- a/tests/e2e/classic-specs/toolbar-cycle-mpv.e2e.mjs +++ b/tests/e2e/classic-specs/toolbar-cycle-mpv.e2e.mjs @@ -61,7 +61,7 @@ const clickToolbar = () => clickToolbarIn(siteHandle); */ async function tabMode() { await browser.switchToWindow(extHandle); - return await browser.executeAsync((site, done) => { + return await browser.execute((site) => new Promise((done) => { chrome.tabs.query({}, (tabs) => { const tab = tabs.find((t) => t.url && t.url.startsWith(site + '/')); if (!tab) { @@ -76,7 +76,7 @@ async function tabMode() { }); }); }); - }, SITE); + }), SITE); } /** @@ -172,7 +172,7 @@ describe('The MPV toolbar cycle (MPV -> Off -> On -> MPV)', function() { return false; }, {timeout: 20000, timeoutMsg: 'the extension page never opened'}); - await browser.executeAsync((site, done) => { + await browser.execute((site) => new Promise((done) => { chrome.storage.local.set({ options: JSON.stringify({mpvMode: true, mpvAllowlist: [site]}), }, () => { @@ -181,7 +181,7 @@ describe('The MPV toolbar cycle (MPV -> Off -> On -> MPV)', function() { done(true); }); }); - }, SITE); + }), SITE); await browser.pause(1000); siteHandle = (await browser.getWindowHandles()).find((h) => h !== extHandle); diff --git a/tests/e2e/classic-specs/toolbar-off-race.e2e.mjs b/tests/e2e/classic-specs/toolbar-off-race.e2e.mjs index 6169b2d3b..e8b38bdb4 100644 --- a/tests/e2e/classic-specs/toolbar-off-race.e2e.mjs +++ b/tests/e2e/classic-specs/toolbar-off-race.e2e.mjs @@ -47,7 +47,7 @@ const clickToolbar = (times) => clickToolbarIn(siteHandle, times); */ async function tabMode() { await browser.switchToWindow(extHandle); - return await browser.executeAsync((site, done) => { + return await browser.execute((site) => new Promise((done) => { chrome.tabs.query({}, (tabs) => { const tab = tabs.find((t) => t.url && t.url.startsWith(site + '/')); if (!tab) { @@ -56,7 +56,7 @@ async function tabMode() { } chrome.action.getBadgeText({tabId: tab.id}, (badge) => done(badge === 'On' ? 'on' : 'off')); }); - }, SITE); + }), SITE); } /** diff --git a/tests/e2e/classic-specs/toolbar-state.e2e.mjs b/tests/e2e/classic-specs/toolbar-state.e2e.mjs index b6f4b54fc..6214cff5e 100644 --- a/tests/e2e/classic-specs/toolbar-state.e2e.mjs +++ b/tests/e2e/classic-specs/toolbar-state.e2e.mjs @@ -38,7 +38,7 @@ const clickToolbar = () => clickToolbarIn(siteHandle); */ async function tabMode() { await browser.switchToWindow(extHandle); - return await browser.executeAsync((site, done) => { + return await browser.execute((site) => new Promise((done) => { // Filtered here rather than with a url pattern: match patterns carry no // port, and the site is told apart from the opener page only by its port. chrome.tabs.query({}, (tabs) => { @@ -56,7 +56,7 @@ async function tabMode() { }); }); }); - }, SITE); + }), SITE); } /** @@ -118,7 +118,7 @@ describe('Toolbar choice on an MPV-allowlisted site', function() { return false; }, {timeout: 20000, timeoutMsg: 'the extension page never opened'}); - await browser.executeAsync((site, done) => { + await browser.execute((site) => new Promise((done) => { chrome.storage.local.set({ options: JSON.stringify({mpvMode: true, mpvAllowlist: [site]}), }, () => { @@ -127,7 +127,7 @@ describe('Toolbar choice on an MPV-allowlisted site', function() { done(true); }); }); - }, SITE); + }), SITE); await browser.pause(1000); siteHandle = (await browser.getWindowHandles()).find((h) => h !== extHandle); diff --git a/tests/e2e/ext-specs/embed-page-query.e2e.mjs b/tests/e2e/ext-specs/embed-page-query.e2e.mjs index 001cb5d86..36a81c019 100644 --- a/tests/e2e/ext-specs/embed-page-query.e2e.mjs +++ b/tests/e2e/ext-specs/embed-page-query.e2e.mjs @@ -188,7 +188,7 @@ describe('A stream named in the page\'s own query string', function() { // own, and a request overtaking it would get the old list once. let sent = []; await browser.waitUntil(async () => { - sent = await browser.executeAsync((done) => { + sent = await browser.execute(() => new Promise((done) => { const before = window.__sourcesSent.length; chrome.runtime.sendMessage({type: 'REQUEST_SOURCES'}).catch(() => {}); const started = Date.now(); @@ -198,7 +198,7 @@ describe('A stream named in the page\'s own query string', function() { done(window.__sourcesSent.slice()); } }, 50); - }); + })); return sent.length > 0 && sent[sent.length - 1] === 0; }, {timeout: 15000, interval: 500}).catch(() => {}); console.log(' sources the background sent after the clear:', JSON.stringify(sent)); diff --git a/tests/e2e/ext-specs/framed-player.e2e.mjs b/tests/e2e/ext-specs/framed-player.e2e.mjs index 33b3ed3fd..c81ab2ec7 100644 --- a/tests/e2e/ext-specs/framed-player.e2e.mjs +++ b/tests/e2e/ext-specs/framed-player.e2e.mjs @@ -153,24 +153,24 @@ describe('A page framing FastStream\'s pages', function() { timeout: 20000, interval: 200, timeoutMsg: 'the framed player never loaded', }); const url = `${SITE}/echo?id=rule`; - const asked = await browser.executeAsync((u, done) => { + const asked = await browser.execute((u) => new Promise((done) => { chrome.runtime.sendMessage({ type: 'SET_HEADERS', url: u, commands: [{operation: 'set', header: 'origin', value: 'https://victim.test'}], }).then(() => done('ok'), (e) => done('failed: ' + e)); - }, url); + }), url); expect(asked).toBe('ok'); // The page, within the rule's 5 seconds, then the player itself. await browser.switchFrame(null); - const pageFetch = await browser.executeAsync((u, done) => { + const pageFetch = await browser.execute((u) => new Promise((done) => { fetch(u, {cache: 'no-store'}).then((r) => done(r.status), (e) => done(String(e))); - }, url); + }), url); await intoFrame('#player'); - const playerFetch = await browser.executeAsync((u, done) => { + const playerFetch = await browser.execute((u) => new Promise((done) => { fetch(u, {cache: 'no-store'}).then((r) => done(r.status), (e) => done(String(e))); - }, url); + }), url); const seen = requests.get('/echo') || []; console.log(' fetches:', pageFetch, playerFetch, 'requests:', JSON.stringify(seen)); diff --git a/tests/e2e/ext-specs/keybinds-storage.e2e.mjs b/tests/e2e/ext-specs/keybinds-storage.e2e.mjs index 9a63ecb82..39afd974b 100644 --- a/tests/e2e/ext-specs/keybinds-storage.e2e.mjs +++ b/tests/e2e/ext-specs/keybinds-storage.e2e.mjs @@ -10,17 +10,17 @@ import {browser, expect} from '@wdio/globals'; import {openExtensionPage} from '../extension-page.mjs'; -const setStored = (options) => browser.executeAsync((value, done) => { +const setStored = (options) => browser.execute((value) => new Promise((done) => { chrome.storage.local.set({options: value === null ? undefined : JSON.stringify(value)}, () => done(true)); -}, options); +}), options); -const clearStored = () => browser.executeAsync((done) => { +const clearStored = () => browser.execute(() => new Promise((done) => { chrome.storage.local.remove('options', () => done(true)); -}); +})); -const getStored = () => browser.executeAsync((done) => { +const getStored = () => browser.execute(() => new Promise((done) => { chrome.storage.local.get('options', (result) => done(result.options ? JSON.parse(result.options) : null)); -}); +})); const rowKey = (action) => browser.execute((selector) => { const box = document.querySelector(`${selector} .keybind-input`); @@ -47,11 +47,11 @@ describe('keybinds saved by an earlier version, in chrome.storage', function() { await openExtensionPage('/player/options/index.html'); await setStored(legacy); - const options = await browser.executeAsync((done) => { + const options = await browser.execute(() => new Promise((done) => { import('/player/utils/Utils.mjs') .then(({Utils}) => Utils.getOptionsFromStorage()) .then(done, (e) => done({failed: String(e)})); - }); + })); expect(options.failed).toBeUndefined(); expect(options.keybindsVersion).toBe(4); @@ -110,11 +110,11 @@ describe('keybinds saved by an earlier version, in chrome.storage', function() { SeekForwardLarge: 'Period', SeekBackwardLarge: 'Comma', }}); - const options = await browser.executeAsync((done) => { + const options = await browser.execute(() => new Promise((done) => { import('/player/utils/Utils.mjs') .then(({Utils}) => Utils.getOptionsFromStorage()) .then(done, (e) => done({failed: String(e)})); - }); + })); expect(options.failed).toBeUndefined(); expect(options.keybindsVersion).toBe(4); expect(options.seekStepSize).toBe(5); @@ -139,11 +139,11 @@ describe('keybinds saved by an earlier version, in chrome.storage', function() { SaveVideo: 'KeyD', IncreasePlaybackRate: 'Shift+ArrowUp', DecreasePlaybackRate: 'Shift+ArrowDown', Mute: 'KeyS', }}); - const options = await browser.executeAsync((done) => { + const options = await browser.execute(() => new Promise((done) => { import('/player/utils/Utils.mjs') .then(({Utils}) => Utils.getOptionsFromStorage()) .then(done, (e) => done({failed: String(e)})); - }); + })); expect(options.failed).toBeUndefined(); expect(options.keybindsVersion).toBe(4); expect(options.keybinds.SaveVideo).toBe('Shift+KeyD'); @@ -156,11 +156,11 @@ describe('keybinds saved by an earlier version, in chrome.storage', function() { await openExtensionPage('/player/options/index.html'); await setStored({keybindsVersion: 3, keybinds: {WindowedFullscreen: 'KeyW'}}); - const options = await browser.executeAsync((done) => { + const options = await browser.execute(() => new Promise((done) => { import('/player/utils/Utils.mjs') .then(({Utils}) => Utils.getOptionsFromStorage()) .then(done, (e) => done({failed: String(e)})); - }); + })); expect(options.keybinds.WindowedFullscreen).toBe('KeyW'); // Through the version 4 step (S and D), which does not touch it. expect(options.keybindsVersion).toBe(4); diff --git a/tests/e2e/ext-specs/mpv.e2e.mjs b/tests/e2e/ext-specs/mpv.e2e.mjs index 7f7dd5e76..90c6b22c9 100644 --- a/tests/e2e/ext-specs/mpv.e2e.mjs +++ b/tests/e2e/ext-specs/mpv.e2e.mjs @@ -144,7 +144,7 @@ describe('MPV mode, end to end', function() { return false; }, {timeout: 20000, timeoutMsg: 'the extension page never opened'}); - const stored = await browser.executeAsync((site, site2, done) => { + const stored = await browser.execute((site, site2) => new Promise((done) => { chrome.storage.local.set({ options: JSON.stringify({ mpvMode: true, @@ -156,7 +156,7 @@ describe('MPV mode, end to end', function() { chrome.storage.local.get('options', (r) => done(r.options)); }); }); - }, SITE, SITE_B); + }), SITE, SITE_B); expect(JSON.parse(stored).mpvMode).toBe(true); // Give the background a moment to apply the reloaded options. @@ -299,7 +299,7 @@ describe('MPV mode, end to end', function() { return false; }, {timeout: 20000, timeoutMsg: 'the extension page never opened'}); - await browser.executeAsync((site, site2, done) => { + await browser.execute((site, site2) => new Promise((done) => { chrome.storage.local.set({ options: JSON.stringify({ mpvMode: true, @@ -312,7 +312,7 @@ describe('MPV mode, end to end', function() { done(true); }); }); - }, SITE, SITE_B); + }), SITE, SITE_B); await browser.pause(1000); const remaining = await browser.getWindowHandles(); diff --git a/tests/e2e/ext-specs/options-mpv.e2e.mjs b/tests/e2e/ext-specs/options-mpv.e2e.mjs index 47ada0911..4b89806b5 100644 --- a/tests/e2e/ext-specs/options-mpv.e2e.mjs +++ b/tests/e2e/ext-specs/options-mpv.e2e.mjs @@ -122,11 +122,11 @@ describe('options page: search and export/import cover MPV settings', function() // exactly this). // Each change is saved asynchronously (OptionsStore.replace), so wait until storage // holds the last of them rather than reading it once straight after the clicks. - const readStored = () => browser.executeAsync((done) => { + const readStored = () => browser.execute(() => new Promise((done) => { import('/player/utils/Utils.mjs').then((m) => { m.Utils.getOptionsFromStorage().then((opts) => done(opts)); }); - }); + })); await browser.waitUntil(async () => { const stored = await readStored(); return stored.mpvMode === true && stored.mpvFullscreen === true && @@ -188,14 +188,14 @@ describe('options page: search and export/import cover MPV settings', function() autoEnableURLs: ['https://old-site.example/'], }; - const merged = await browser.executeAsync((oldExport, done) => { + const merged = await browser.execute((oldExport) => new Promise((done) => { Promise.all([ import('/player/utils/Utils.mjs'), import('/player/options/defaults/DefaultOptions.mjs'), ]).then(([UtilsMod, DefaultsMod]) => { done(UtilsMod.Utils.mergeOptions(DefaultsMod.DefaultOptions, oldExport)); }); - }, preMpvExport); + }), preMpvExport); expect(merged.mpvMode).toBe(false); expect(merged.mpvAllowlist).toEqual([]); diff --git a/tests/e2e/ext-specs/player-peers-bfcache.e2e.mjs b/tests/e2e/ext-specs/player-peers-bfcache.e2e.mjs index 2cb7f7fa1..934522c09 100644 --- a/tests/e2e/ext-specs/player-peers-bfcache.e2e.mjs +++ b/tests/e2e/ext-specs/player-peers-bfcache.e2e.mjs @@ -39,7 +39,7 @@ async function openEmbed(query) { {timeout: 30000, timeoutMsg: 'the player never announced itself'}); if (BUILD === 'github') { // Stored once the request has ended (options.mjs). - await browser.waitUntil(async () => browser.executeAsync((done) => { + await browser.waitUntil(async () => browser.execute(() => new Promise((done) => { chrome.storage.local.get('updateData', (result) => { try { done(!!JSON.parse(result?.updateData || '{}').lastUpdateCheck); @@ -47,7 +47,7 @@ async function openEmbed(query) { done(false); } }); - }), {timeout: 30000, interval: 250, timeoutMsg: 'the update check of the GitHub build never ended'}); + })), {timeout: 30000, interval: 250, timeoutMsg: 'the update check of the GitHub build never ended'}); } await browser.switchFrame(null); } diff --git a/tests/e2e/ext-specs/save-transport.e2e.mjs b/tests/e2e/ext-specs/save-transport.e2e.mjs index a85251f2a..3a86458e6 100644 --- a/tests/e2e/ext-specs/save-transport.e2e.mjs +++ b/tests/e2e/ext-specs/save-transport.e2e.mjs @@ -188,7 +188,7 @@ describe('save transport on the installed extension', function() { it('downloads a blob URL created inside a partitioned player iframe', async function() { await openEmbeddedPlayer(); - const result = await browser.executeAsync((done) => { + const result = await browser.execute(() => new Promise((done) => { const blob = new Blob(['faststream iframe probe'], {type: 'text/plain'}); const url = URL.createObjectURL(blob); chrome.runtime.sendMessage({ @@ -203,7 +203,7 @@ describe('save transport on the installed extension', function() { downloadId: resp === undefined ? 'undefined' : resp, }); }); - }); + })); await browser.switchToParentFrame(); console.log(' iframe download:', JSON.stringify(result)); diff --git a/tests/e2e/extension-page.mjs b/tests/e2e/extension-page.mjs index 3a003ec09..db29de4e1 100644 --- a/tests/e2e/extension-page.mjs +++ b/tests/e2e/extension-page.mjs @@ -9,6 +9,7 @@ import {browser} from '@wdio/globals'; +import {executeWithDone} from './classic-helpers.mjs'; import {EXTENSION_UUID, OPENER_URL} from './wdio.extension.conf.mjs'; const PLAYER_PAGE = `moz-extension://${EXTENSION_UUID}/player/index.html`; @@ -66,7 +67,7 @@ export async function openExtensionPage(pagePath) { * @return {Promise} Resolves once addSource has been called. */ export async function addSourceInPlayer(url) { - const error = await browser.executeAsync((u, done) => { + const error = await browser.execute((u) => new Promise((done) => { Promise.all([ import('/player/VideoSource.mjs'), import('/player/enums/PlayerModes.mjs'), @@ -78,7 +79,7 @@ export async function addSourceInPlayer(url) { }); done(null); }, (e) => done(String(e))); - }, url); + }), url); if (error) { throw new Error('could not give the player its source: ' + error); } @@ -112,7 +113,7 @@ export async function inExtensionPage(fn, arg) { return false; }, {timeout: 20000, interval: 200, timeoutMsg: 'the extension page never opened'}); try { - return await browser.executeAsync(fn, arg); + return await executeWithDone(fn, arg); } finally { await browser.switchToWindow(handle); await browser.closeWindow(); diff --git a/tests/e2e/pbm-specs/private-browsing.e2e.mjs b/tests/e2e/pbm-specs/private-browsing.e2e.mjs index eca3f7cf0..c9dd96682 100644 --- a/tests/e2e/pbm-specs/private-browsing.e2e.mjs +++ b/tests/e2e/pbm-specs/private-browsing.e2e.mjs @@ -92,40 +92,42 @@ describe('the extension in a private window', function() { it('answers a background ping', async function() { await openExtensionPage('/player/index.html'); - const res = await browser.executeAsync((done) => { + const res = await browser.execute(() => new Promise((done) => { chrome.runtime.sendMessage({type: 'PING'}, (r) => done({ response: r, lastError: chrome.runtime.lastError ? String(chrome.runtime.lastError.message) : null, })); - }); + })); console.log(' background ping:', JSON.stringify(res)); expect(res.response).toBe('PONG'); }); it('offloads blobs to a real backend instead of dropping to memory', async function() { await openExtensionPage('/player/index.html'); - const res = await browser.executeAsync(async (done) => { - const out = {}; - try { - const {FSBlob} = await import('/player/modules/FSBlob.mjs'); - const store = new FSBlob(); - const payload = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); - const identifier = await store.saveBlobAsync(new Blob([payload])); - out.backend = store.opfsManager ? 'opfs' : + const res = await browser.execute(() => new Promise((done) => { + (async () => { + const out = {}; + try { + const {FSBlob} = await import('/player/modules/FSBlob.mjs'); + const store = new FSBlob(); + const payload = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]); + const identifier = await store.saveBlobAsync(new Blob([payload])); + out.backend = store.opfsManager ? 'opfs' : (store.cache ? 'cache' : (store.indexedDBManager ? 'indexeddb' : 'memory')); - // A promise for a blob the Cache API holds: it is read from there when asked for. - const readBack = new Uint8Array( - await (await store.getBlob(identifier)).arrayBuffer()); - out.roundTrips = readBack.length === payload.length && + // A promise for a blob the Cache API holds: it is read from there when asked for. + const readBack = new Uint8Array( + await (await store.getBlob(identifier)).arrayBuffer()); + out.roundTrips = readBack.length === payload.length && readBack.every((b, i) => b === payload[i]); - store.close(); - } catch (e) { - out.err = (e && e.stack) || String(e); - } - done(out); - }); + store.close(); + } catch (e) { + out.err = (e && e.stack) || String(e); + } + done(out); + })(); + })); console.log(' blob backend:', JSON.stringify(res)); expect(res.err).toBe(undefined); @@ -139,34 +141,36 @@ describe('the extension in a private window', function() { it('never rejects out of clear() or deleteBlob()', async function() { await openExtensionPage('/player/index.html'); - const res = await browser.executeAsync(async (done) => { - const out = {}; - const {FSBlob} = await import('/player/modules/FSBlob.mjs'); - const store = new FSBlob(); - try { - const identifier = await store.saveBlobAsync(new Blob([new Uint8Array([1])])); - await store.deleteBlob(identifier); - out.deleteBlob = 'resolved'; - } catch (e) { - out.deleteBlob = 'REJECTED: ' + (e && e.message); - } - try { - await store.clear(); - out.clear = 'resolved'; - } catch (e) { - out.clear = 'REJECTED: ' + (e && e.message); - } - // The exact call the player makes on every new source, and the one - // whose rejection used to abandon player creation. - try { - await window.fastStream.downloadManager.reset(); - out.downloadManagerReset = 'resolved'; - } catch (e) { - out.downloadManagerReset = 'REJECTED: ' + (e && e.message); - } - store.close(); - done(out); - }); + const res = await browser.execute(() => new Promise((done) => { + (async () => { + const out = {}; + const {FSBlob} = await import('/player/modules/FSBlob.mjs'); + const store = new FSBlob(); + try { + const identifier = await store.saveBlobAsync(new Blob([new Uint8Array([1])])); + await store.deleteBlob(identifier); + out.deleteBlob = 'resolved'; + } catch (e) { + out.deleteBlob = 'REJECTED: ' + (e && e.message); + } + try { + await store.clear(); + out.clear = 'resolved'; + } catch (e) { + out.clear = 'REJECTED: ' + (e && e.message); + } + // The exact call the player makes on every new source, and the one + // whose rejection used to abandon player creation. + try { + await window.fastStream.downloadManager.reset(); + out.downloadManagerReset = 'resolved'; + } catch (e) { + out.downloadManagerReset = 'REJECTED: ' + (e && e.message); + } + store.close(); + done(out); + })(); + })); console.log(' teardown calls:', JSON.stringify(res)); expect(res.deleteBlob).toBe('resolved'); @@ -211,22 +215,24 @@ describe('the extension in a private window', function() { let res = {}; await browser.waitUntil(async () => { - res = await browser.executeAsync(async (done) => { - const out = {}; - try { - const manager = window.fastStream.downloadManager; - const store = manager.blobStore; - out.memoryOnly = store.memoryOnly; - out.backend = store.opfsManager ? 'opfs' : + res = await browser.execute(() => new Promise((done) => { + (async () => { + const out = {}; + try { + const manager = window.fastStream.downloadManager; + const store = manager.blobStore; + out.memoryOnly = store.memoryOnly; + out.backend = store.opfsManager ? 'opfs' : (store.cache ? 'cache' : (store.indexedDBManager ? 'indexeddb' : 'memory')); - out.stored = store.blobStore.size; - out.ram = manager.ramBytes(); - out.canSpill = manager.canSpill(); - } catch (e) { - out.err = (e && e.stack) || String(e); - } - done(out); - }); + out.stored = store.blobStore.size; + out.ram = manager.ramBytes(); + out.canSpill = manager.canSpill(); + } catch (e) { + out.err = (e && e.stack) || String(e); + } + done(out); + })(); + })); return !!res.err || res.stored > 0; }, {timeout: 30000, interval: 1000}).catch(() => {}); @@ -282,7 +288,7 @@ describe('the extension in a private window', function() { // incognito context and saved the track straight under its name. Only the prompt is // checked: a patch to Utils.downloadURL made through an import() in this script never // saw that save, so it could not tell "saved without asking" from "nothing happened". - await browser.executeAsync((done) => { + await browser.execute(() => new Promise((done) => { import('/player/SubtitleTrack.mjs').then(({SubtitleTrack}) => { const client = window.fastStream; const manager = client.interfaceController.subtitlesManager; @@ -292,7 +298,7 @@ describe('the extension in a private window', function() { if (!manager.activeTracks.includes(loaded)) manager.activateTrack(loaded); done(); }); - }); + })); await browser.execute(() => document.querySelector('.subtitles_list .subtitle-download-tool') .dispatchEvent(new MouseEvent('click', {bubbles: true, cancelable: true}))); await browser.waitUntil( diff --git a/tests/e2e/specs/analyzer.e2e.mjs b/tests/e2e/specs/analyzer.e2e.mjs index 38c67fb87..a43f85455 100644 --- a/tests/e2e/specs/analyzer.e2e.mjs +++ b/tests/e2e/specs/analyzer.e2e.mjs @@ -22,7 +22,7 @@ describe('Background audio analyzer', function() { }), {timeout: 60000, timeoutMsg: 'the player never became ready'}); - const outcome = await browser.executeAsync((done) => { + const outcome = await browser.execute(() => new Promise((done) => { const errors = []; window.addEventListener('unhandledrejection', (e) => errors.push(String(e.reason?.message || e.reason))); window.addEventListener('error', (e) => errors.push(e.message)); @@ -39,7 +39,7 @@ describe('Background audio analyzer', function() { analyzer.stopBackgroundAnalyzer(); }, 500); }, (e) => done({failed: String((e && e.message) || e), errors})); - }); + })); expect(outcome.failed).toBeUndefined(); expect(outcome.errors).toEqual([]); diff --git a/tests/e2e/specs/archive-roundtrip.e2e.mjs b/tests/e2e/specs/archive-roundtrip.e2e.mjs index 7ace8dfed..c3dad5a82 100644 --- a/tests/e2e/specs/archive-roundtrip.e2e.mjs +++ b/tests/e2e/specs/archive-roundtrip.e2e.mjs @@ -83,7 +83,7 @@ describe('An archive written by "dump buffer"', function() { // The control: Resource Timing does see the player's manifest downloads. expect((await fetchedManifests(manifests)).length).toBeGreaterThan(0); - const written = await browser.executeAsync((done) => { + const written = await browser.execute(() => new Promise((done) => { (async () => { const client = window.fastStream; const {FastStreamArchiveUtils} = await import('/player/utils/FastStreamArchiveUtils.mjs'); @@ -104,7 +104,7 @@ describe('An archive written by "dump buffer"', function() { }); return {stored: entries.map((entry) => entry.url), size: file.size, base64: dataUrl.split(',')[1]}; })().then(done, (e) => done({error: String(e)})); - }); + })); console.log(' archive:', JSON.stringify({error: written.error, stored: written.stored?.length, size: written.size})); expect(written.error).toBeUndefined(); @@ -118,7 +118,7 @@ describe('An archive written by "dump buffer"', function() { await browser.url(`/player/index.html?t=${Date.now()}`); await browser.waitUntil(async () => browser.execute(() => !!window.fastStream?.interfaceController?.saveManager), {timeout: 30000, timeoutMsg: 'the empty player never set up'}); - const dropped = await browser.executeAsync((base64, done) => { + const dropped = await browser.execute((base64) => new Promise((done) => { (async () => { const client = window.fastStream; const {FastStreamArchiveUtils} = await import('/player/utils/FastStreamArchiveUtils.mjs'); @@ -134,7 +134,7 @@ describe('An archive written by "dump buffer"', function() { }); return {fromArchive: !!client.source?.loadedFromArchive}; })().then(done, (e) => done({error: String(e)})); - }, written.base64); + }), written.base64); console.log(' dropped:', JSON.stringify(dropped)); expect(dropped.error).toBeUndefined(); expect(dropped.fromArchive).toBe(true); diff --git a/tests/e2e/specs/client-setup.e2e.mjs b/tests/e2e/specs/client-setup.e2e.mjs index 61c309e13..9777a3e39 100644 --- a/tests/e2e/specs/client-setup.e2e.mjs +++ b/tests/e2e/specs/client-setup.e2e.mjs @@ -37,13 +37,13 @@ async function openEmptyPlayer() { * @return {Promise} */ async function addSource(url) { - const error = await browser.executeAsync((url, done) => { + const error = await browser.execute((url) => new Promise((done) => { Promise.all([import('/player/VideoSource.mjs'), import('/player/utils/URLUtils.mjs')]) .then(([{VideoSource}, {URLUtils}]) => { window.fastStream.addSource(new VideoSource(url, {}, URLUtils.getModeFromURL(url)), true); done(null); }).catch((e) => done(String(e))); - }, url); + }), url); expect(error).toBe(null); } @@ -62,11 +62,11 @@ describe('FastStreamClient setup', function() { afterEach(async function() { // Remembered times outlive the page in this browser session's IndexedDB, and a later // case on the same video would start at one. - await browser.executeAsync((done) => { + await browser.execute(() => new Promise((done) => { const manager = window.fastStream?.progressMemory?.indexedDbManager; if (!manager) return done(); manager.clearStorage().then(() => done(), () => done()); - }); + })); }); it('finishes setting up a video whose seek preview fails to build', async function() { @@ -151,7 +151,7 @@ describe('FastStreamClient setup', function() { await browser.waitUntil(async () => browser.execute(() => !!window.fastStream.progressMemory), {timeout: 30000, timeoutMsg: 'the progress memory never loaded'}); - const result = await browser.executeAsync((firstUrl, secondUrl, done) => { + const result = await browser.execute((firstUrl, secondUrl) => new Promise((done) => { (async () => { const [{VideoSource}, {URLUtils}] = await Promise.all([import('/player/VideoSource.mjs'), import('/player/utils/URLUtils.mjs')]); @@ -211,7 +211,7 @@ describe('FastStreamClient setup', function() { await client.addSource(second, true); return {secondRecord: hex(secondHashes.identifierHash)}; })().then(done, (e) => done({error: String(e)})); - }, missingUrl(), mp4Url()); + }), missingUrl(), mp4Url()); expect(result.error).toBe(undefined); await waitForPicture(); @@ -349,7 +349,7 @@ describe('FastStreamClient setup', function() { await addSource(mp4Url()); await waitForPicture(); - const state = await browser.executeAsync((done) => { + const state = await browser.execute(() => new Promise((done) => { const client = window.fastStream; const player = client.player; const play = player.play.bind(player); @@ -364,7 +364,7 @@ describe('FastStreamClient setup', function() { await playing; done({paused: client.currentVideo.paused, shown: client.state.playing}); }, 100); - }); + })); console.log(' state:', JSON.stringify(state)); expect(state.paused).toBe(true); expect(state.shown).toBe(false); @@ -375,7 +375,7 @@ describe('FastStreamClient setup', function() { await addSource(mp4Url()); await waitForPicture(); - const state = await browser.executeAsync((done) => { + const state = await browser.execute(() => new Promise((done) => { const client = window.fastStream; const player = client.player; client.currentVideo.muted = true; @@ -393,7 +393,7 @@ describe('FastStreamClient setup', function() { }, 100)); }, 100); }, (e) => done({error: String(e)})); - }); + })); console.log(' state:', JSON.stringify(state)); expect(state.paused).toBe(false); expect(state.shown).toBe(true); @@ -406,7 +406,7 @@ describe('FastStreamClient setup', function() { await addSource(mp4Url()); await waitForPicture(); - const state = await browser.executeAsync((done) => { + const state = await browser.execute(() => new Promise((done) => { const client = window.fastStream; client.currentVideo.muted = true; const context = client.audioContext; @@ -430,7 +430,7 @@ describe('FastStreamClient setup', function() { done({error: String(e)}); }); }, (e) => done({error: String(e)})); - }); + })); console.log(' state:', JSON.stringify(state)); expect(state.settled).toBe(true); expect(state.paused).toBe(false); @@ -442,7 +442,7 @@ describe('FastStreamClient setup', function() { it('ignores the keys and menus that act on the video while there is none', async function() { // Between two sources there is no player: a key pressed then threw. await openEmptyPlayer(); - const errors = await browser.executeAsync((done) => { + const errors = await browser.execute(() => new Promise((done) => { const client = window.fastStream; const errors = []; const attempt = async (name, action) => { @@ -462,7 +462,7 @@ describe('FastStreamClient setup', function() { await attempt('setCurrentAudioLevelID', () => client.setCurrentAudioLevelID('0')); done(errors); })(); - }); + })); expect(errors).toEqual([]); }); }); diff --git a/tests/e2e/specs/diagnostics.mjs b/tests/e2e/specs/diagnostics.mjs index f879020f4..0f06b53ac 100644 --- a/tests/e2e/specs/diagnostics.mjs +++ b/tests/e2e/specs/diagnostics.mjs @@ -10,7 +10,7 @@ import {browser} from '@wdio/globals'; * @return {Promise} */ export async function pageState() { - return browser.executeAsync((done) => { + return browser.execute(() => new Promise((done) => { const video = document.querySelector('video'); const state = { video: video ? { @@ -28,7 +28,7 @@ export async function pageState() { }, (e) => { state.opfs = 'unreadable: ' + e.message; }).finally(() => done(state)); - }); + })); } /** diff --git a/tests/e2e/specs/dialogs.e2e.mjs b/tests/e2e/specs/dialogs.e2e.mjs index 78f8b64bc..70a5c0baf 100644 --- a/tests/e2e/specs/dialogs.e2e.mjs +++ b/tests/e2e/specs/dialogs.e2e.mjs @@ -27,7 +27,7 @@ async function openPlayer() { * @param {...*} args */ async function open(method, ...args) { - await browser.executeAsync((method, args, done) => { + await browser.execute((method, args) => new Promise((done) => { window.__answer = undefined; import('/player/utils/AlertPolyfill.mjs').then(({AlertPolyfill}) => { AlertPolyfill[method](...args).then((answer) => { @@ -35,7 +35,7 @@ async function open(method, ...args) { }); done(); }); - }, method, args); + }), method, args); await browser.waitUntil(async () => browser.execute(() => !!document.querySelector('dialog.fs-dialog[open]')), {timeout: 5000, timeoutMsg: `the dialog of ${method} never opened`}); } @@ -111,7 +111,7 @@ describe('Dialogs', function() { it('shows markup in an error\'s message and in a toast as text', async function() { await openPlayer(); - const shown = await browser.executeAsync((done) => { + const shown = await browser.execute(() => new Promise((done) => { import('/player/utils/AlertPolyfill.mjs').then(async ({AlertPolyfill}) => { const markup = 'Invalid URL: x'; AlertPolyfill.errorSendToDeveloper(new Error(markup)); @@ -123,7 +123,7 @@ describe('Dialogs', function() { injected: !!document.getElementById('fs-injected'), }); }); - }); + })); expect(shown.injected).toBe(false); expect(shown.title).toContain('x'); expect(shown.toast).toContain('x'); @@ -173,7 +173,7 @@ describe('Dialogs', function() { it('runs a toast\'s time on when another comes, and stops it under the pointer', async function() { await openPlayer(); - const at = await browser.executeAsync((done) => { + const at = await browser.execute(() => new Promise((done) => { import('/player/utils/AlertPolyfill.mjs').then(async ({AlertPolyfill}) => { AlertPolyfill.toast('info', 'First'); await new Promise((resolve) => setTimeout(resolve, 1500)); @@ -181,13 +181,15 @@ describe('Dialogs', function() { await new Promise((resolve) => setTimeout(resolve, 100)); done(document.querySelector('.fs-toast .fs-toast-progress').getAnimations()[0].currentTime); }); - }); + })); // Showing the corner again on top for the second does not start the first's time over (a // CSS animation was not restarted either, measured: the corner is hidden and shown in one // task). The time is a Web Animation, the toast's only timer. expect(at).toBeGreaterThan(1400); - await (await browser.$('.fs-toast')).moveTo(); + // The first of the two, which the lines around read (querySelector): a plain $() of two + // is an error from WebdriverIO 10 on (strict selectors). + await (await browser.$$('.fs-toast'))[0].moveTo(); const paused = await browser.execute(() => document.querySelector('.fs-toast .fs-toast-progress').getAnimations()[0].playState); expect(paused).toBe('paused'); }); diff --git a/tests/e2e/specs/firefox.e2e.mjs b/tests/e2e/specs/firefox.e2e.mjs index 820a0291f..0a5fbd4ee 100644 --- a/tests/e2e/specs/firefox.e2e.mjs +++ b/tests/e2e/specs/firefox.e2e.mjs @@ -59,7 +59,7 @@ describe('Options page', function() { describe('Firefox audio at speed', function() { // Plays the fixture at one rate for two seconds and reports the level the audio reaches, // through an analyser that is not connected to the speakers, and how far the media moved. - const measure = (rate) => browser.executeAsync((source, rate, done) => { + const measure = (rate) => browser.execute((source, rate) => new Promise((done) => { const video = document.createElement('video'); video.preload = 'auto'; video.src = source; @@ -93,7 +93,7 @@ describe('Firefox audio at speed', function() { done({level: levels[Math.floor(levels.length / 2)], moved}); }, 2000); }, {once: true}); - }, globalThis.__E2E_FIXTURES_ORIGIN__ + '/fixtures/long-av.mp4', rate); + }), globalThis.__E2E_FIXTURES_ORIGIN__ + '/fixtures/long-av.mp4', rate); it('is heard up to 8x, which is where the player stops, and not beyond', async function() { await browser.setTimeout({script: 60000}); diff --git a/tests/e2e/specs/keybinds-menu.e2e.mjs b/tests/e2e/specs/keybinds-menu.e2e.mjs index 336503808..10c74ff49 100644 --- a/tests/e2e/specs/keybinds-menu.e2e.mjs +++ b/tests/e2e/specs/keybinds-menu.e2e.mjs @@ -58,9 +58,9 @@ const waitForSaved = (check) => browser.waitUntil( }, {timeout: 10000, timeoutMsg: 'the options were never saved'}); -const defaults = () => browser.executeAsync((done) => { +const defaults = () => browser.execute(() => new Promise((done) => { import('/player/options/defaults/DefaultKeybinds.mjs').then((m) => done(m.DefaultKeybinds)); -}); +})); describe('Keybinding menu', function() { beforeEach(async function() { @@ -86,7 +86,7 @@ describe('Keybinding menu', function() { }); it('names every row from its action, never with a raw action name', async function() { - const labels = await browser.executeAsync((done) => { + const labels = await browser.execute(() => new Promise((done) => { import('/player/options/KeybindUtils.mjs').then(({keybindLabel}) => { done([...document.querySelectorAll('.keybind-container')].map((container) => ({ action: container.dataset.keybind, @@ -94,7 +94,7 @@ describe('Keybinding menu', function() { expected: keybindLabel(container.dataset.keybind), }))); }); - }); + })); expect(labels.length).toBeGreaterThan(50); for (const {action, shown, expected} of labels) { // The action goes into the compared value so a failure says which row it was. diff --git a/tests/e2e/specs/keybinds.e2e.mjs b/tests/e2e/specs/keybinds.e2e.mjs index dff788110..80f09639d 100644 --- a/tests/e2e/specs/keybinds.e2e.mjs +++ b/tests/e2e/specs/keybinds.e2e.mjs @@ -870,17 +870,32 @@ describe('The 16x key', function() { expect(await browser.execute(() => window.fastStream.options.maxPlaybackRate)).toBe(8); await reset(); await pressKey('KeyH'); - const fast = await browser.executeAsync((done) => { + const fast = await browser.execute(() => new Promise((done) => { const video = window.fastStream.player.getVideo(); const osd = document.querySelector('.mainplayer .osd').textContent; - const from = video.currentTime; + const startedAt = video.currentTime; + const waitStart = performance.now(); video.play().catch(() => {}); - const start = performance.now(); - setTimeout(() => { - const seconds = (performance.now() - start) / 1000; - done({rate: video.playbackRate, osd, pace: (video.currentTime - from) / seconds}); - }, 1500); - }); + // Measured once it plays: timed from play(), a slow runner's start took most of the + // 1.5 s, and the pace read 1.3x on a Linux runner where the run before read 4.5x + // (2026-10-10, #422). Stuck at 1x it gets there too, half a second later. + const measure = () => { + const from = video.currentTime; + const start = performance.now(); + setTimeout(() => { + const seconds = (performance.now() - start) / 1000; + done({rate: video.playbackRate, osd, pace: (video.currentTime - from) / seconds}); + }, 2000); + }; + const playing = () => { + if (video.currentTime > startedAt + 0.5 || performance.now() - waitStart > 5000) { + measure(); + } else { + setTimeout(playing, 50); + } + }; + playing(); + })); console.log(` 16x key: ${JSON.stringify(fast)}`); expect(fast.rate).toBe(16); expect(fast.osd).toBe(`16× · ${en.player_speed_nosound.message}`); diff --git a/tests/e2e/specs/merger-repro.e2e.mjs b/tests/e2e/specs/merger-repro.e2e.mjs index af75b9098..d5c1b8ce6 100644 --- a/tests/e2e/specs/merger-repro.e2e.mjs +++ b/tests/e2e/specs/merger-repro.e2e.mjs @@ -14,7 +14,7 @@ describe('mp4merger finalize micro-repro', function() { async () => browser.execute(() => !!window.fastStream), {timeout: 30000, timeoutMsg: 'fastStream never appeared'}); - const result = await browser.executeAsync((done) => { + const result = await browser.execute(() => new Promise((done) => { (async () => { const out = {}; try { @@ -49,7 +49,7 @@ describe('mp4merger finalize micro-repro', function() { return out; } })().then(done); - }); + })); console.log(' micro-repro:', JSON.stringify(result)); expect(result.error).toBe(undefined); diff --git a/tests/e2e/specs/mp4-loading.e2e.mjs b/tests/e2e/specs/mp4-loading.e2e.mjs index d4f8a85a9..53ab9b51e 100644 --- a/tests/e2e/specs/mp4-loading.e2e.mjs +++ b/tests/e2e/specs/mp4-loading.e2e.mjs @@ -179,7 +179,7 @@ describe('MP4Player loading', function() { await browser.url(`/player/index.html?t=${Date.now()}`); await browser.waitUntil(async () => browser.execute(() => !!window.fastStream?.optionsApplied), {timeout: 30000, timeoutMsg: 'the player options never loaded'}); - await browser.executeAsync((url, done) => { + await browser.execute((url) => new Promise((done) => { Promise.all([ import('/player/players/mp4/MP4Player.mjs'), import('/player/VideoSource.mjs'), @@ -196,7 +196,7 @@ describe('MP4Player loading', function() { }); done(); }); - }, `${globalThis.__E2E_FIXTURES_ORIGIN__}/fixtures/mp4-shapes/long-fragmented.mp4`); + }), `${globalThis.__E2E_FIXTURES_ORIGIN__}/fixtures/mp4-shapes/long-fragmented.mp4`); await browser.waitUntil(async () => browser.execute(() => (window.fastStream?.currentVideo?.readyState || 0) >= 3), {timeout: 30000, interval: 200, timeoutMsg: 'the video never became playable'}); const resets = await browser.execute(() => window.__resets); diff --git a/tests/e2e/specs/options-fields.e2e.mjs b/tests/e2e/specs/options-fields.e2e.mjs index c6fb3eae1..b89306094 100644 --- a/tests/e2e/specs/options-fields.e2e.mjs +++ b/tests/e2e/specs/options-fields.e2e.mjs @@ -144,9 +144,9 @@ describe('Options page settings import', function() { }; it('says so when the file is no settings file', async function() { - const expected = await browser.executeAsync((done) => { + const expected = await browser.execute(() => new Promise((done) => { import('/player/modules/Localize.mjs').then(({Localize}) => done(Localize.getMessage('options_import_invalid'))); - }); + })); expect(expected).not.toBe('options_import_invalid'); expect(await importText('null')).toEqual([expected]); expect(await importText('[1, 2]')).toEqual([expected]); @@ -267,9 +267,9 @@ describe('Options page size fields, typed by hand', function() { * @param {string} key * @return {Promise} */ - const message = (key) => browser.executeAsync((key, done) => { + const message = (key) => browser.execute((key) => new Promise((done) => { import('/player/modules/Localize.mjs').then(({Localize}) => done(Localize.getMessage(key))); - }, key); + }), key); /** * Picks a unit as a person does. diff --git a/tests/e2e/specs/player-controls.e2e.mjs b/tests/e2e/specs/player-controls.e2e.mjs index 265ed0916..6b06c94f2 100644 --- a/tests/e2e/specs/player-controls.e2e.mjs +++ b/tests/e2e/specs/player-controls.e2e.mjs @@ -37,13 +37,13 @@ async function openEmptyPlayer() { * @return {Promise} */ async function addSource(url) { - const error = await browser.executeAsync((url, done) => { + const error = await browser.execute((url) => new Promise((done) => { Promise.all([import('/player/VideoSource.mjs'), import('/player/utils/URLUtils.mjs')]) .then(([{VideoSource}, {URLUtils}]) => { window.fastStream.addSource(new VideoSource(url, {}, URLUtils.getModeFromURL(url)), true); done(null); }).catch((e) => done(String(e))); - }, url); + }), url); expect(error).toBe(null); } @@ -124,7 +124,7 @@ describe('Player controls', function() { // then it kept the old volume. (From audio-tools.e2e.mjs, which went with the audio // settings window, 2026-10-10.) await openEmptyPlayer(); - const gains = await browser.executeAsync((done) => { + const gains = await browser.execute(() => new Promise((done) => { (async () => { const client = window.fastStream; const gain = () => Math.round((client.audioOutput.gain.gainNode?.gain.value ?? 1) * 100) / 100; @@ -138,14 +138,14 @@ describe('Player controls', function() { } return out; })().then(done, (e) => done({error: String(e)})); - }); + })); expect(gains).toEqual([{before: 0.5, after: 1}, {before: 1.5, after: 1}]); }); it('plays a video dropped together with a .json that is not JSON', async function() { // The .json threw, and the drop ended there, video and all. await openEmptyPlayer(); - const error = await browser.executeAsync((url, done) => { + const error = await browser.execute((url) => new Promise((done) => { fetch(url).then((response) => response.blob()).then((blob) => { const dataTransfer = new DataTransfer(); dataTransfer.items.add(new File(['{not json'], 'notes.json', {type: 'application/json'})); @@ -154,7 +154,7 @@ describe('Player controls', function() { new DragEvent('drop', {dataTransfer, bubbles: true, cancelable: true})); done(null); }).catch((e) => done(String(e))); - }, mp4Url()); + }), mp4Url()); expect(error).toBe(null); await waitForPicture(); const identifier = await browser.execute(() => window.fastStream.source?.identifier); @@ -228,7 +228,7 @@ describe('Player controls', function() { await openEmptyPlayer(); await addSource(hlsUrl()); await waitForPicture(); - const shown = await browser.executeAsync((done) => { + const shown = await browser.execute(() => new Promise((done) => { import('/player/enums/DownloadStatus.mjs').then(({DownloadStatus}) => { const client = window.fastStream; // A fragment that failed to download, as a network error leaves it. @@ -236,7 +236,7 @@ describe('Player controls', function() { client.interfaceController.updateFragmentsLoaded(); done(document.querySelector('.mainplayer .reset_failed').style.display !== 'none'); }).catch((e) => done('failed: ' + e)); - }); + })); expect(shown).toBe(true); // Its "all buffered" said too (review): the next video, buffered from the start, said nothing. await browser.execute(() => { @@ -301,19 +301,19 @@ describe('Player controls', function() { it('copies a link from the time readout without the login headers', async function() { // The copied link held the page's Cookie and Authorization in its query (#185). await openEmptyPlayer(); - const error = await browser.executeAsync((url, done) => { + const error = await browser.execute((url) => new Promise((done) => { Promise.all([import('/player/VideoSource.mjs'), import('/player/utils/URLUtils.mjs')]) .then(([{VideoSource}, {URLUtils}]) => { const headers = {Cookie: 'session=secret', Authorization: 'Bearer secret', Referer: 'https://site.example/'}; window.fastStream.addSource(new VideoSource(url, headers, URLUtils.getModeFromURL(url)), true); done(null); }).catch((e) => done(String(e))); - }, mp4Url()); + }), mp4Url()); expect(error).toBe(null); await waitForPicture(); // The copy goes through navigator.clipboard (WebUtils.copyText), asynchronously; caught // there, and at the old execCommand way it falls back to, should the first refuse. - const {copied, via} = await browser.executeAsync((done) => { + const {copied, via} = await browser.execute(() => new Promise((done) => { const clipboard = navigator.clipboard; const writeText = clipboard.writeText; const execCommand = document.execCommand; @@ -332,7 +332,7 @@ describe('Player controls', function() { }; setTimeout(() => finish({copied: null, via: 'nothing within 5 s'}), 5000); document.querySelector('.mainplayer .fluid_control_duration').click(); - }); + })); console.log(' copied:', copied, 'via', via); expect(via).toBe('clipboard'); const headers = JSON.parse(new URL(copied).searchParams.get('faststream-headers')); @@ -443,14 +443,14 @@ describe('Player controls', function() { await openEmptyPlayer(); await addSource(mp4Url()); await waitForPicture(); - const message = await browser.executeAsync((done) => { + const message = await browser.execute(() => new Promise((done) => { import('/player/utils/FastStreamArchiveUtils.mjs').then(async ({FastStreamArchiveUtils}) => { FastStreamArchiveUtils.writeFSAToStream = () => Promise.reject(new Error('no archive here')); const interfaceController = window.fastStream.interfaceController; await interfaceController.saveManager.dumpBuffer('probe'); done(interfaceController.statusManager.statusMessages.get('save-video').message); }).catch((e) => done('failed: ' + e)); - }); + })); expect(message).toBe(en.player_archiver_fail.message); }); @@ -492,7 +492,7 @@ describe('Player controls', function() { await waitForPicture(); // How often the player's time is updated in one second: the loop's frames, and the // video's own timeupdate events. - const perSecond = () => browser.executeAsync((done) => { + const perSecond = () => browser.execute(() => new Promise((done) => { const client = window.fastStream; const updateTime = client.updateTime; let calls = 0; @@ -504,7 +504,7 @@ describe('Player controls', function() { delete client.updateTime; done(calls); }, 1000); - }); + })); const paused = await perSecond(); await browser.execute(() => { window.fastStream.play(); diff --git a/tests/e2e/specs/player-menus.e2e.mjs b/tests/e2e/specs/player-menus.e2e.mjs index b76e0c550..f6c8b373b 100644 --- a/tests/e2e/specs/player-menus.e2e.mjs +++ b/tests/e2e/specs/player-menus.e2e.mjs @@ -37,13 +37,13 @@ async function openEmptyPlayer() { * @return {Promise} */ async function addSource(url) { - const error = await browser.executeAsync((url, done) => { + const error = await browser.execute((url) => new Promise((done) => { Promise.all([import('/player/VideoSource.mjs'), import('/player/utils/URLUtils.mjs')]) .then(([{VideoSource}, {URLUtils}]) => { window.fastStream.addSource(new VideoSource(url, {}, URLUtils.getModeFromURL(url)), true); done(null); }).catch((e) => done(String(e))); - }, url); + }), url); expect(error).toBe(null); } @@ -388,14 +388,14 @@ describe('Player menus', function() { // Back at 0 after a reload, the silence skipper skipped nothing (review, 2026-10-09). it('keeps the silence threshold the user dragged', async function() { await openEmptyPlayer(); - const loaded = await browser.executeAsync((done) => { + const loaded = await browser.execute(() => new Promise((done) => { const changer = window.fastStream.interfaceController.playbackRateChanger; changer.silenceThreshold = 0.42; changer.saveState().then(() => { changer.silenceThreshold = 0; return changer.loadState(); }).then(() => done(changer.silenceThreshold)); - }); + })); expect(loaded).toBeCloseTo(0.42, 5); }); @@ -429,14 +429,14 @@ describe('Player menus', function() { it('names a dropdown\'s new value when the keyboard changes it', async function() { // Only a click on an item renamed it, so a screen reader kept the old value. await openEmptyPlayer(); - const label = await browser.executeAsync((done) => { + const label = await browser.execute(() => new Promise((done) => { import('/player/ui/components/Dropdown.mjs').then(({createDropdown}) => { const dropdown = createDropdown('a', 'Mode', {a: 'Alpha', b: 'Beta'}, () => {}); document.body.appendChild(dropdown); dropdown.dispatchEvent(new KeyboardEvent('keydown', {key: 'ArrowDown', bubbles: true, cancelable: true})); done(dropdown.ariaLabel); }).catch((e) => done('failed: ' + e)); - }); + })); expect(label).toBe('Mode: Beta'); }); @@ -444,7 +444,7 @@ describe('Player menus', function() { // The type filter's label was the English "Type" in every language (the filter is gone // since 2026-10-09: a season or episode filled in makes an episode's search). await openEmptyPlayer(); - const shown = await browser.executeAsync((done) => { + const shown = await browser.execute(() => new Promise((done) => { // The web player takes its language from the browser; the search is built again // with the browser in German. Object.defineProperty(navigator, 'language', {value: 'de', configurable: true}); @@ -452,7 +452,7 @@ describe('Player menus', function() { const {subui} = new OpenSubtitlesSearch(window.fastStream.version); done({season: subui.seasonInput.ariaLabel, episode: subui.episodeInput.placeholder, year: subui.yearInput.placeholder}); }).catch((e) => done({error: String(e)})); - }); + })); console.log(' fields:', JSON.stringify(shown)); expect(shown).toEqual({season: de.player_opensubtitles_seasonnum.message, episode: de.player_opensubtitles_episodenum.message, year: de.player_opensubtitles_year.message}); diff --git a/tests/e2e/specs/ram-budget.e2e.mjs b/tests/e2e/specs/ram-budget.e2e.mjs index 8d17a48f3..2c7f05b2f 100644 --- a/tests/e2e/specs/ram-budget.e2e.mjs +++ b/tests/e2e/specs/ram-budget.e2e.mjs @@ -48,25 +48,27 @@ async function downloadAll(budget, playlist = '/long.m3u8', timeout = 120000, di * to disk, as leaving), on disk (files), and in all (total). */ async function holdings() { - return browser.executeAsync(async (done) => { - const client = window.fastStream; - const manager = client.downloadManager; - const store = manager.blobStore; - let files = 0; - try { - const root = await (await navigator.storage.getDirectory()).getDirectoryHandle('fsblob'); - for await (const [, dir] of root.entries()) { - for await (const [name] of dir.entries()) { - if (name !== '_meta.json') files++; + return browser.execute(() => new Promise((done) => { + (async () => { + const client = window.fastStream; + const manager = client.downloadManager; + const store = manager.blobStore; + let files = 0; + try { + const root = await (await navigator.storage.getDirectory()).getDirectoryHandle('fsblob'); + for await (const [, dir] of root.entries()) { + for await (const [name] of dir.entries()) { + if (name !== '_meta.json') files++; + } } + } catch (e) { + files = -1; } - } catch (e) { - files = -1; - } - const total = client.fragments.reduce((sum, fragment) => sum + (fragment?.dataSize || 0), 0); - done({ram: manager.ramBytes(), leaving: manager.spillingBytes(), total, files, opfs: !!store.opfsManager, - full: manager.memoryFull}); - }); + const total = client.fragments.reduce((sum, fragment) => sum + (fragment?.dataSize || 0), 0); + done({ram: manager.ramBytes(), leaving: manager.spillingBytes(), total, files, opfs: !!store.opfsManager, + full: manager.memoryFull}); + })(); + })); } describe('The RAM budget', function() { @@ -142,7 +144,7 @@ describe('The RAM budget', function() { expect(released).toBeGreaterThan(20); // The start, let go of long ago, is downloaded again and plays. - const played = await browser.executeAsync(async (done) => { + const played = await browser.execute(async () => { const client = window.fastStream; client.currentTime = 1; const video = client.player.getVideo(); @@ -152,7 +154,7 @@ describe('The RAM budget', function() { if (video.currentTime > 2 && !video.seeking) break; await new Promise((resolve) => setTimeout(resolve, 200)); } - done({time: video.currentTime, failed: !!client.interfaceController.failed}); + return {time: video.currentTime, failed: !!client.interfaceController.failed}; }); console.log(` downloaded again: ${JSON.stringify(played)}`); expect(played.failed).toBe(false); @@ -178,18 +180,20 @@ describe('The RAM budget', function() { expect(state.files).toBeGreaterThan(0); // The start, long written to disk, plays again. - const played = await browser.executeAsync(async (done) => { - const client = window.fastStream; - client.currentTime = 1; - const video = client.player.getVideo(); - video.play().catch(() => {}); - const start = Date.now(); - while (Date.now() - start < 15000) { - if (video.currentTime > 2 && !video.seeking) break; - await new Promise((resolve) => setTimeout(resolve, 200)); - } - done({time: video.currentTime, failed: !!client.interfaceController.failed}); - }); + const played = await browser.execute(() => new Promise((done) => { + (async () => { + const client = window.fastStream; + client.currentTime = 1; + const video = client.player.getVideo(); + video.play().catch(() => {}); + const start = Date.now(); + while (Date.now() - start < 15000) { + if (video.currentTime > 2 && !video.seeking) break; + await new Promise((resolve) => setTimeout(resolve, 200)); + } + done({time: video.currentTime, failed: !!client.interfaceController.failed}); + })(); + })); console.log(` from disk: ${JSON.stringify(played)}`); expect(played.failed).toBe(false); expect(played.time).toBeGreaterThan(2); diff --git a/tests/e2e/specs/save-fmp4.e2e.mjs b/tests/e2e/specs/save-fmp4.e2e.mjs index 1e5d930ca..974bdc6d0 100644 --- a/tests/e2e/specs/save-fmp4.e2e.mjs +++ b/tests/e2e/specs/save-fmp4.e2e.mjs @@ -296,7 +296,7 @@ async function saveAndInspectPhases(phase, prepare) { phase('downloaded'); if (prepare) await browser.execute(prepare); - const saved = await browser.executeAsync((done) => { + const saved = await browser.execute(() => new Promise((done) => { const info = { saveError: null, blobSize: null, decodeOk: null, duration: null, trakCount: null, mdatBytes: null, moofs: null, @@ -377,7 +377,7 @@ async function saveAndInspectPhases(phase, prepare) { info.saveError = ((e && e.message) ? e.message + '\n' : '') + ((e && e.stack) || String(e)); done(info); }); - }); + })); phase('saved'); return saved; } @@ -572,7 +572,7 @@ describe('Save video (locally generated fMP4)', function() { async () => browser.execute(() => document.querySelector('video')?.readyState >= 2), {timeout: 30000, timeoutMsg: 'video never reached HAVE_CURRENT_DATA'}); - const result = await browser.executeAsync((done) => { + const result = await browser.execute(() => new Promise((done) => { import('/player/enums/ReferenceTypes.mjs').then(async ({ReferenceTypes}) => { const client = window.fastStream; const player = client.player; @@ -594,7 +594,7 @@ describe('Save video (locally generated fMP4)', function() { .map((frag) => `${level}/${frag.sn}`)); done({saveError, pinned}); }).catch((e) => done({saveError: 'test setup: ' + e.message, pinned: null})); - }); + })); console.log(' result:', JSON.stringify(result)); expect(result.saveError).toBe('Failed to download fragment'); diff --git a/tests/e2e/specs/save-video.e2e.mjs b/tests/e2e/specs/save-video.e2e.mjs index f6809032c..6b89186d9 100644 --- a/tests/e2e/specs/save-video.e2e.mjs +++ b/tests/e2e/specs/save-video.e2e.mjs @@ -84,7 +84,7 @@ async function saveAndValidatePhases(phase) { await new Promise((r) => setTimeout(r, 6000)); phase('played'); - const saved = await browser.executeAsync((done) => { + const saved = await browser.execute(() => new Promise((done) => { const info = { saveError: null, blobSize: null, blobType: null, decodeOk: null, decodeError: null, duration: null, @@ -125,7 +125,7 @@ async function saveAndValidatePhases(phase) { info.saveError = ((e && e.message) ? e.message + '\n' : '') + ((e && e.stack) || String(e)); done(info); }); - }); + })); phase('saved'); return saved; } @@ -152,7 +152,7 @@ async function saveAndValidateStreamed() { await browser.execute(() => document.querySelector('video').play().catch(() => {})); await new Promise((r) => setTimeout(r, 3000)); - return browser.executeAsync((done) => { + return browser.execute(() => new Promise((done) => { const info = { saveError: null, blobSize: null, blobType: null, decodeOk: null, decodeError: null, duration: null, @@ -201,7 +201,7 @@ async function saveAndValidateStreamed() { info.saveError = (e && e.stack) || String(e); done(info); }); - }); + })); } describe('Save video (download)', function() { @@ -257,7 +257,7 @@ describe('Save video (download)', function() { async () => browser.execute(() => document.querySelector('video').readyState >= 2), {timeout: 60000, timeoutMsg: 'video never reached HAVE_CURRENT_DATA'}); - const result = await browser.executeAsync((done) => { + const result = await browser.execute(() => new Promise((done) => { let cancel; const filestream = new WritableStream({ write() {}, @@ -282,7 +282,7 @@ describe('Save video (download)', function() { }).catch((e) => { done({rejected: true, message: (e && e.message) || String(e), elapsed: performance.now() - start}); }); - }); + })); console.log(' result:', JSON.stringify(result)); expect(result.rejected).toBe(true); diff --git a/tests/e2e/specs/sources-browser-ui.e2e.mjs b/tests/e2e/specs/sources-browser-ui.e2e.mjs index fad7157b0..7e14c7d7d 100644 --- a/tests/e2e/specs/sources-browser-ui.e2e.mjs +++ b/tests/e2e/specs/sources-browser-ui.e2e.mjs @@ -230,7 +230,7 @@ describe('Sources Browser (Quellen Browser) UI', function() { await (await browser.$('.mainplayer .fluid_button_link')).click(); const url = await browser.$('.linkui-source-url'); await url.waitForDisplayed(); - const state = await browser.executeAsync((url, done) => { + const state = await browser.execute((url) => new Promise((done) => { const client = window.fastStream; client.setSource = async () => { window.__tried = true; @@ -246,7 +246,7 @@ describe('Sources Browser (Quellen Browser) UI', function() { const button = document.querySelector('.linkui-source-set-button'); done({loading: button.classList.contains('loading'), text: button.textContent, tried: window.__tried}); }, 500); - }, globalThis.__E2E_FIXTURES_ORIGIN__ + '/fixtures/missing.mp4'); + }), globalThis.__E2E_FIXTURES_ORIGIN__ + '/fixtures/missing.mp4'); console.log(' after a failed source:', JSON.stringify(state)); expect(state.tried).toBe(true); expect(state.loading).toBe(false); diff --git a/tests/e2e/specs/subtitle-editing.e2e.mjs b/tests/e2e/specs/subtitle-editing.e2e.mjs index 24a07fafa..53c0627f8 100644 --- a/tests/e2e/specs/subtitle-editing.e2e.mjs +++ b/tests/e2e/specs/subtitle-editing.e2e.mjs @@ -20,7 +20,7 @@ async function openPlayer() { // overrides below; it sets optionsApplied when it has. await browser.waitUntil(async () => browser.execute(() => !!window.fastStream.optionsApplied), {timeout: 30000, timeoutMsg: 'the player options never loaded'}); - const error = await browser.executeAsync((url, done) => { + const error = await browser.execute((url) => new Promise((done) => { window.fastStream.options.autoPlay = false; window.fastStream.options.storeProgress = false; Promise.all([import('/player/VideoSource.mjs'), import('/player/utils/URLUtils.mjs')]) @@ -28,7 +28,7 @@ async function openPlayer() { window.fastStream.addSource(new VideoSource(url, {}, URLUtils.getModeFromURL(url)), true); done(null); }).catch((e) => done(String(e))); - }, mp4Url()); + }), mp4Url()); expect(error).toBe(null); await browser.waitUntil(async () => browser.execute(() => { const client = window.fastStream; @@ -41,7 +41,7 @@ async function openPlayer() { * @return {Promise} */ async function editTrack() { - await browser.executeAsync((done) => { + await browser.execute(() => new Promise((done) => { import('/player/SubtitleTrack.mjs').then(({SubtitleTrack}) => { const client = window.fastStream; const track = new SubtitleTrack('Edit test', 'en'); @@ -51,7 +51,7 @@ async function editTrack() { client.interfaceController.subtitlesManager.subtitleSyncer.toggleTrack(window.__track); done(); }); - }); + })); } /** @@ -117,7 +117,7 @@ describe('Subtitle editing', function() { // The row has the focus while the mouse is over it, and took Shift+Backspace for its // own Backspace: the track was removed and the seek stayed. await openPlayer(); - const state = await browser.executeAsync((done) => { + const state = await browser.execute(() => new Promise((done) => { import('/player/SubtitleTrack.mjs').then(async ({SubtitleTrack}) => { const client = window.fastStream; const track = new SubtitleTrack('Row test', 'en'); @@ -133,7 +133,7 @@ describe('Subtitle editing', function() { await new Promise((resolve) => setTimeout(resolve, 300)); done({tracks: client.interfaceController.subtitlesManager.tracks.length, currentTime: client.currentTime}); }); - }); + })); console.log(' after Shift+Backspace:', JSON.stringify(state)); expect(state.tracks).toBe(1); expect(state.currentTime).toBeCloseTo(2, 1); @@ -143,7 +143,7 @@ describe('Subtitle editing', function() { // With two tracks on, a row reads "1: (en) Foo Bar", and the name offered was // "1:__(en)_Foo_Bar"; Firefox refuses a file name with a colon. await openPlayer(); - await browser.executeAsync((done) => { + await browser.execute(() => new Promise((done) => { import('/player/SubtitleTrack.mjs').then(({SubtitleTrack}) => { const client = window.fastStream; const manager = client.interfaceController.subtitlesManager; @@ -155,7 +155,7 @@ describe('Subtitle editing', function() { } done(); }); - }); + })); const rowText = await browser.execute(() => document.querySelector('.mainplayer .subtitles_list .subtitle-track-name').textContent); console.log(' first row:', JSON.stringify(rowText)); expect(rowText.startsWith('1: ')).toBe(true); @@ -173,7 +173,7 @@ describe('Subtitle editing', function() { // The track was looked up after the name prompt, so a new video's tracks by then had // another track saved under this one's name (and an emptied list threw). await openPlayer(); - await browser.executeAsync((done) => { + await browser.execute(() => new Promise((done) => { Promise.all([import('/player/SubtitleTrack.mjs'), import('/player/utils/Utils.mjs')]).then(([{SubtitleTrack}, {Utils}]) => { window.__saved = []; Utils.downloadURL = async (url, name) => { @@ -187,13 +187,13 @@ describe('Subtitle editing', function() { if (!manager.activeTracks.includes(loaded)) manager.activateTrack(loaded); done(); }); - }); + })); await browser.execute(() => document.querySelector('.mainplayer .subtitles_list .subtitle-download-tool') .dispatchEvent(new MouseEvent('click', {bubbles: true, cancelable: true}))); const popup = await browser.$('.fs-dialog'); await popup.waitForDisplayed({timeout: 5000}); // Another track takes the row's place while the prompt is open, as a new video's does. - await browser.executeAsync((done) => { + await browser.execute(() => new Promise((done) => { import('/player/SubtitleTrack.mjs').then(({SubtitleTrack}) => { const client = window.fastStream; client.interfaceController.subtitlesManager.clearTracks(); @@ -202,7 +202,7 @@ describe('Subtitle editing', function() { client.loadSubtitleTrack(other); done(); }); - }); + })); await (await browser.$('.fs-dialog-confirm')).click(); await browser.waitUntil(async () => browser.execute(() => window.__saved.length > 0), {timeout: 5000}).catch(() => {}); const saved = await browser.execute(() => window.__saved); @@ -216,7 +216,7 @@ describe('Subtitle editing', function() { // The file input kept the file it had read, and picking the same file again fires no // change event, so nothing happened. await openPlayer(); - const state = await browser.executeAsync((done) => { + const state = await browser.execute(() => new Promise((done) => { const input = document.querySelector('.mainplayer input[type=file][accept=".vtt, .srt"]'); const dataTransfer = new DataTransfer(); dataTransfer.items.add(new File(['1\n00:00:01,000 --> 00:00:03,000\nA line\n\n'], 'lines.srt')); @@ -233,7 +233,7 @@ describe('Subtitle editing', function() { } }; read(); - }); + })); console.log(' after one upload:', JSON.stringify(state)); expect(state.tracks).toBe(1); // An empty input fires change for any file picked next, the same one included. diff --git a/tests/e2e/specs/subtitle-empty-cue.e2e.mjs b/tests/e2e/specs/subtitle-empty-cue.e2e.mjs index 068869d87..d38f5ca3c 100644 --- a/tests/e2e/specs/subtitle-empty-cue.e2e.mjs +++ b/tests/e2e/specs/subtitle-empty-cue.e2e.mjs @@ -17,7 +17,7 @@ describe('A subtitle cue with no text', function() { }); it('leaves the tracks after it and the rest of the time update running', async function() { - const result = await browser.executeAsync((done) => { + const result = await browser.execute(() => new Promise((done) => { import('/player/SubtitleTrack.mjs').then(({SubtitleTrack}) => { const client = window.fastStream; const ui = client.interfaceController; @@ -63,7 +63,7 @@ describe('A subtitle cue with no text', function() { shown: document.querySelector('.mainplayer .fluid_subtitles_container').textContent, }); }, (e) => done({errors: [String(e)]})); - }); + })); console.log(' ', JSON.stringify(result)); // The case is the one described: the first track's cue at this time has no text. diff --git a/tests/e2e/specs/subtitle-load.e2e.mjs b/tests/e2e/specs/subtitle-load.e2e.mjs index eb5f81615..f47fa8f82 100644 --- a/tests/e2e/specs/subtitle-load.e2e.mjs +++ b/tests/e2e/specs/subtitle-load.e2e.mjs @@ -19,7 +19,7 @@ async function openPlayer() { await browser.url(`/player/index.html?t=${Date.now()}`); await browser.waitUntil(async () => browser.execute(() => !!window.fastStream?.optionsApplied), {timeout: 30000, timeoutMsg: 'the player never started'}); - const error = await browser.executeAsync((done) => { + const error = await browser.execute(() => new Promise((done) => { Promise.all([ import('/player/utils/AlertPolyfill.mjs'), import('/player/utils/RequestUtils.mjs'), @@ -35,7 +35,7 @@ async function openPlayer() { new Uint8Array(window.__urlBody).buffer : new TextEncoder().encode(window.__urlBody).buffer), 0); done(null); }).catch((e) => done(String(e))); - }); + })); expect(error).toBe(null); } @@ -68,7 +68,7 @@ async function pickFile(name, text) { * @return {Promise} */ async function addUrl(body) { - const error = await browser.executeAsync((body, done) => { + const error = await browser.execute((body) => new Promise((done) => { import('/player/modules/Localize.mjs').then(({Localize}) => { window.__toasts.length = 0; window.__promptAnswer = 'https://subs.example/track.vtt'; @@ -78,7 +78,7 @@ async function addUrl(body) { option.dispatchEvent(new MouseEvent('click', {bubbles: true})); done(null); }).catch((e) => done(String(e))); - }, body); + }), body); expect(error).toBe(null); await browser.pause(300); } diff --git a/tests/e2e/specs/subtitle-overlap.e2e.mjs b/tests/e2e/specs/subtitle-overlap.e2e.mjs index 5ed7af546..9575104d1 100644 --- a/tests/e2e/specs/subtitle-overlap.e2e.mjs +++ b/tests/e2e/specs/subtitle-overlap.e2e.mjs @@ -18,7 +18,7 @@ describe('Overlapping subtitle cues', function() { }); it('keeps a long cue on screen over shorter ones, and the lines in start order', async function() { - const result = await browser.executeAsync((done) => { + const result = await browser.execute(() => new Promise((done) => { import('/player/SubtitleTrack.mjs').then(({SubtitleTrack}) => { const client = window.fastStream; const manager = client.interfaceController.subtitlesManager; @@ -45,7 +45,7 @@ describe('Overlapping subtitle cues', function() { at11: shownAt(11), }); }, (e) => done({error: String(e)})); - }); + })); console.log(' ', JSON.stringify(result)); expect(result.error).toBeUndefined(); diff --git a/tests/e2e/specs/subtitle-search-titles.e2e.mjs b/tests/e2e/specs/subtitle-search-titles.e2e.mjs index d938d8d47..3487c1d97 100644 --- a/tests/e2e/specs/subtitle-search-titles.e2e.mjs +++ b/tests/e2e/specs/subtitle-search-titles.e2e.mjs @@ -33,7 +33,7 @@ async function openPlayer() { await browser.url('/player/index.html?t=' + Date.now()); await browser.waitUntil(async () => browser.execute(() => !!window.fastStream?.interfaceController?.subtitlesManager), {timeout: 15000, timeoutMsg: 'the player never appeared'}); - await browser.executeAsync((answers, done) => { + await browser.execute((answers) => new Promise((done) => { import('/player/utils/RequestUtils.mjs').then(({RequestUtils}) => { window.__requests = []; RequestUtils.request = async (options) => { @@ -49,7 +49,7 @@ async function openPlayer() { }; done(); }); - }, ANSWERS); + }), ANSWERS); } /** diff --git a/tests/e2e/specs/subtitle-search.e2e.mjs b/tests/e2e/specs/subtitle-search.e2e.mjs index 1fbae6be0..16707e114 100644 --- a/tests/e2e/specs/subtitle-search.e2e.mjs +++ b/tests/e2e/specs/subtitle-search.e2e.mjs @@ -40,7 +40,7 @@ async function openSearch() { {timeout: 30000, timeoutMsg: 'the player never started'}); await browser.waitUntil(async () => browser.execute(() => !!window.fastStream.optionsApplied), {timeout: 30000, timeoutMsg: 'the player options never loaded'}); - const error = await browser.executeAsync((done) => { + const error = await browser.execute(() => new Promise((done) => { import('/player/utils/RequestUtils.mjs').then(({RequestUtils}) => { window.__requests = []; RequestUtils.request = (options) => new Promise((resolve, reject) => { @@ -49,7 +49,7 @@ async function openSearch() { window.fastStream.interfaceController.subtitlesManager.openSubtitlesSearch.openUI(); done(null); }).catch((e) => done(String(e))); - }); + })); expect(error).toBe(null); } @@ -99,7 +99,7 @@ async function answerSearch(first, subtitles, list = subtitles) { * @return {Promise<{titles: string[], message: string, pages: number, requests: number}>} */ async function shown() { - return browser.executeAsync((done) => setTimeout(() => { + return browser.execute(() => new Promise((done) => setTimeout(() => { const subui = window.fastStream.interfaceController.subtitlesManager.openSubtitlesSearch.subui; done({ titles: Array.from(subui.results.querySelectorAll('.subtitle-result-title'), (el) => el.textContent), @@ -107,7 +107,7 @@ async function shown() { pages: subui.pages.children.length, requests: window.__requests.length, }); - }, 100)); + }, 100))); } describe('Subtitle search', function() { diff --git a/tests/e2e/specs/subtitle-sync.e2e.mjs b/tests/e2e/specs/subtitle-sync.e2e.mjs index 565f8f036..b36c3446b 100644 --- a/tests/e2e/specs/subtitle-sync.e2e.mjs +++ b/tests/e2e/specs/subtitle-sync.e2e.mjs @@ -30,79 +30,81 @@ describe('Subtitle resync tool', function() { }); it('draws only the cues in view, shifts by key without an error, and drags on the left button only', async function() { - const result = await browser.executeAsync(async (done) => { - const out = {uncaught: [], statuses: []}; - window.addEventListener('error', (e) => out.uncaught.push(e.message)); - window.addEventListener('unhandledrejection', (e) => { - out.uncaught.push(String((e.reason && e.reason.message) || e.reason)); - }); - const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + const result = await browser.execute(() => new Promise((done) => { + (async () => { + const out = {uncaught: [], statuses: []}; + window.addEventListener('error', (e) => out.uncaught.push(e.message)); + window.addEventListener('unhandledrejection', (e) => { + out.uncaught.push(String((e.reason && e.reason.message) || e.reason)); + }); + const wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); - const client = window.fastStream; - const controller = client.interfaceController; - const setStatus = controller.setStatusMessage.bind(controller); - controller.setStatusMessage = (key, message, ...rest) => { - if (key === 'subtitles' && message) out.statuses.push(message); - return setStatus(key, message, ...rest); - }; + const client = window.fastStream; + const controller = client.interfaceController; + const setStatus = controller.setStatusMessage.bind(controller); + controller.setStatusMessage = (key, message, ...rest) => { + if (key === 'subtitles' && message) out.statuses.push(message); + return setStatus(key, message, ...rest); + }; - // 300 one-second cues, one every 2 s: 600 s of subtitles on a 160 s video - const {SubtitleTrack} = await import('/player/SubtitleTrack.mjs'); - const time = (s) => new Date(s * 1000).toISOString().substring(11, 23).replace('.', ','); - let srt = ''; - for (let i = 0; i < 300; i++) { - srt += `${i + 1}\n${time(i * 2)} --> ${time(i * 2 + 1)}\nline ${i + 1}\n\n`; - } - const track = new SubtitleTrack('Resync test', 'en'); - track.loadText(srt); - client.loadSubtitleTrack(track); - out.cues = track.cues.length; + // 300 one-second cues, one every 2 s: 600 s of subtitles on a 160 s video + const {SubtitleTrack} = await import('/player/SubtitleTrack.mjs'); + const time = (s) => new Date(s * 1000).toISOString().substring(11, 23).replace('.', ','); + let srt = ''; + for (let i = 0; i < 300; i++) { + srt += `${i + 1}\n${time(i * 2)} --> ${time(i * 2 + 1)}\nline ${i + 1}\n\n`; + } + const track = new SubtitleTrack('Resync test', 'en'); + track.loadText(srt); + client.loadSubtitleTrack(track); + out.cues = track.cues.length; - const syncer = controller.subtitlesManager.subtitleSyncer; - syncer.toggleTrack(track); - controller.showControlBar(); - await wait(1000); - controller.showControlBar(); - await wait(300); - out.cueElements = document.querySelectorAll('.timeline_track_cue').length; + const syncer = controller.subtitlesManager.subtitleSyncer; + syncer.toggleTrack(track); + controller.showControlBar(); + await wait(1000); + controller.showControlBar(); + await wait(300); + out.cueElements = document.querySelectorAll('.timeline_track_cue').length; - // emit() catches a handler's error and hands it to console.error - const first = track.cues[0].startTime; - const consoleError = console.error; - console.error = (...args) => { - out.uncaught.push('console.error: ' + args.map((a) => (a && a.message) || String(a)).join(' ')); - consoleError(...args); - }; - client.keybindManager.emit('ShiftSubtitlesLater'); - console.error = consoleError; - out.keyShift = track.cues[0].startTime - first; + // emit() catches a handler's error and hands it to console.error + const first = track.cues[0].startTime; + const consoleError = console.error; + console.error = (...args) => { + out.uncaught.push('console.error: ' + args.map((a) => (a && a.message) || String(a)).join(' ')); + consoleError(...args); + }; + client.keybindManager.emit('ShiftSubtitlesLater'); + console.error = consoleError; + out.keyShift = track.cues[0].startTime - first; - const row = document.querySelector('.timeline_track'); - const box = document.querySelector('.fluid_controls_timeline_syncer').getBoundingClientRect(); - const x = box.left + box.width / 2; - const container = row.closest('.fluid_video_wrapper') || document.body; - const mouse = (target, type, clientX, button) => target.dispatchEvent( - new MouseEvent(type, {bubbles: true, cancelable: true, clientX, clientY: box.top + 60, button})); + const row = document.querySelector('.timeline_track'); + const box = document.querySelector('.fluid_controls_timeline_syncer').getBoundingClientRect(); + const x = box.left + box.width / 2; + const container = row.closest('.fluid_video_wrapper') || document.body; + const mouse = (target, type, clientX, button) => target.dispatchEvent( + new MouseEvent(type, {bubbles: true, cancelable: true, clientX, clientY: box.top + 60, button})); - // right button: no drag - const beforeRight = track.cues[0].startTime; - mouse(row, 'mousedown', x, 2); - mouse(container, 'mousemove', x + 150, 2); - out.rightDragShift = track.cues[0].startTime - beforeRight; - mouse(container, 'mouseup', x + 150, 2); + // right button: no drag + const beforeRight = track.cues[0].startTime; + mouse(row, 'mousedown', x, 2); + mouse(container, 'mousemove', x + 150, 2); + out.rightDragShift = track.cues[0].startTime - beforeRight; + mouse(container, 'mouseup', x + 150, 2); - // left button: the track follows the pointer, and the total is shown - const beforeLeft = track.cues[0].startTime; - mouse(row, 'mousedown', x, 0); - mouse(container, 'mousemove', x + 150, 0); - mouse(container, 'mouseup', x + 150, 0); - out.leftDragShift = track.cues[0].startTime - beforeLeft; - out.shiftTotal = track.shiftTotal; - out.lastStatus = out.statuses[out.statuses.length - 1] || null; + // left button: the track follows the pointer, and the total is shown + const beforeLeft = track.cues[0].startTime; + mouse(row, 'mousedown', x, 0); + mouse(container, 'mousemove', x + 150, 0); + mouse(container, 'mouseup', x + 150, 0); + out.leftDragShift = track.cues[0].startTime - beforeLeft; + out.shiftTotal = track.shiftTotal; + out.lastStatus = out.statuses[out.statuses.length - 1] || null; - syncer.toggleTrack(track); - done(out); - }); + syncer.toggleTrack(track); + done(out); + })(); + })); console.log(' result:', JSON.stringify(result)); expect(result.uncaught).toEqual([]); diff --git a/tests/e2e/specs/subtitle-toggle.e2e.mjs b/tests/e2e/specs/subtitle-toggle.e2e.mjs index a36f142ea..ecd495443 100644 --- a/tests/e2e/specs/subtitle-toggle.e2e.mjs +++ b/tests/e2e/specs/subtitle-toggle.e2e.mjs @@ -40,14 +40,14 @@ describe('Subtitle toggle', function() { * @param {string} label - The track's label. * @return {Promise} */ - const addTrack = (label) => browser.executeAsync((label, done) => { + const addTrack = (label) => browser.execute((label) => new Promise((done) => { import('/player/SubtitleTrack.mjs').then(({SubtitleTrack}) => { const track = new SubtitleTrack(label, 'en'); track.loadText(`WEBVTT\n\n00:00:00.000 --> 00:00:05.000\n${label}\n`); window.fastStream.loadSubtitleTrack(track); done(); }); - }, label); + }), label); it('does nothing, without an error, when there is no track', async function() { await pressC(); diff --git a/tests/e2e/specs/video-delay.e2e.mjs b/tests/e2e/specs/video-delay.e2e.mjs index 435c1314c..534dba7bd 100644 --- a/tests/e2e/specs/video-delay.e2e.mjs +++ b/tests/e2e/specs/video-delay.e2e.mjs @@ -27,7 +27,7 @@ describe('Video delay', function() { }), {timeout: 60000, timeoutMsg: 'video never became ready'}); - const result = await browser.executeAsync((done) => { + const result = await browser.execute(() => new Promise((done) => { const out = {uncaught: [], audioPlayers: null}; window.addEventListener('unhandledrejection', (e) => { out.uncaught.push(String((e.reason && e.reason.message) || e.reason)); @@ -44,7 +44,7 @@ describe('Video delay', function() { out.audioPlayers = client.syncedAudioPlayer ? client.syncedAudioPlayer.audioPlayers.length : null; done(out); }, 4000); - }); + })); console.log(' result:', JSON.stringify(result)); expect(result.uncaught).toEqual([]); diff --git a/tests/e2e/switchFrame.mjs b/tests/e2e/switchFrame.mjs new file mode 100644 index 000000000..ad5af21ad --- /dev/null +++ b/tests/e2e/switchFrame.mjs @@ -0,0 +1,50 @@ +// switchFrame(element) and switchFrame(null) for WebDriver BiDi sessions, as WebdriverIO 9 had +// them. +// +// WebdriverIO 10 removed switchFrame for BiDi sessions (the extension and playback suites): +// code in a frame is to go through browsing-context objects there (browser.url(), then +// .frame()). The specs switch into the player's frames and back some fifty times, with +// shared helpers that run in whatever frame is current. This puts back what 9 did for an +// element or null (its switchFrame, webdriverio 9.32.0 build/node.js): the frame's browsing +// context, read from the iframe's contentWindow in the page, becomes the one WebdriverIO's +// BiDi commands (execute, $) go to, and WebDriver classic's switchToFrame takes the classic +// commands (click, keys) there as well. null goes back to the tab itself. Sessions without +// BiDi (the classic and private-browsing suites) keep WebdriverIO's own switchFrame. + +import {getContextManager} from 'webdriverio'; + +const ELEMENT_KEY = 'element-6066-11e4-a52e-4f735466cecf'; + +/** + * Overwrites browser.switchFrame for BiDi sessions. Call it in the config's before hook, + * after the session has its BiDi connection (ensureBidi). + * @param {WebdriverIO.Browser} browser + */ +export function restoreSwitchFrame(browser) { + browser.overwriteCommand('switchFrame', async function(original, target) { + if (!browser.isBidi) { + return original(target); + } + const contexts = getContextManager(browser); + if (target === null) { + const handle = await browser.getWindowHandle(); + contexts.setCurrentContext(handle); + await browser.switchToFrame(null); + return handle; + } + if (!target || (typeof target !== 'object' && typeof target !== 'function')) { + throw new Error('switchFrame (tests/e2e/switchFrame.mjs) takes an iframe element or null'); + } + const element = typeof target.getElement === 'function' ? await target.getElement() : await target; + await element.waitForExist({ + timeoutMsg: `Can't switch to frame with selector ${element.selector} because it doesn't exist`, + }); + const frame = await browser.execute((iframe) => iframe.contentWindow, element); + if (!frame?.context) { + throw new Error(`switchFrame: ${element.selector} has no browsing context (not a frame?)`); + } + contexts.setCurrentContext(frame.context); + await browser.switchToFrame({[ELEMENT_KEY]: element.elementId}); + return frame.context; + }); +} diff --git a/tests/e2e/wdio.conf.mjs b/tests/e2e/wdio.conf.mjs index 48983ad7a..6dc485ce4 100644 --- a/tests/e2e/wdio.conf.mjs +++ b/tests/e2e/wdio.conf.mjs @@ -39,6 +39,7 @@ import {testTimeout} from './testTimeout.mjs'; import {listenOrStop} from './listen-or-stop.mjs'; import {speedAfterTest, speedBeforeTest} from './speedWatch.mjs'; import {ensureBidi} from './bidi.mjs'; +import {restoreSwitchFrame} from './switchFrame.mjs'; import {guardSetup, rootHooks} from './setupGuard.mjs'; import {ensureMp4Fixture} from './mp4Fixture.mjs'; import {ensureFixtures, readFileOrNothing} from './buildFixtures.mjs'; @@ -180,6 +181,8 @@ export const config = { // Each spec file's attempt starts with no downloads, as in wdio.extension.conf.mjs. resetDownloadDir(); await ensureBidi(); + // switchFrame for BiDi sessions, which WebdriverIO 10 dropped (switchFrame.mjs). + restoreSwitchFrame(browser); globalThis.__E2E_FIXTURES_ORIGIN__ = BASE_URL; }); }, diff --git a/tests/e2e/wdio.extension.conf.mjs b/tests/e2e/wdio.extension.conf.mjs index 6a6891105..558c66f44 100644 --- a/tests/e2e/wdio.extension.conf.mjs +++ b/tests/e2e/wdio.extension.conf.mjs @@ -35,6 +35,7 @@ import {mozLogHooks} from './mozLog.mjs'; import {speedAfterTest, speedBeforeTest} from './speedWatch.mjs'; import {testTimeout} from './testTimeout.mjs'; import {ensureBidi} from './bidi.mjs'; +import {restoreSwitchFrame} from './switchFrame.mjs'; import {guardSetup, rootHooks} from './setupGuard.mjs'; import {ensureMp4Fixture} from './mp4Fixture.mjs'; import {ensureFixtures} from './buildFixtures.mjs'; @@ -308,6 +309,8 @@ export const config = { // Before the add-on: a browser started again has none. (The classic and pbm suites // share this hook and ask for classic, which it leaves alone.) await ensureBidi(); + // switchFrame for BiDi sessions, which WebdriverIO 10 dropped (switchFrame.mjs). + restoreSwitchFrame(browser); // Temporary rather than permanent: the package is unsigned, and a // temporary install is exactly how a reviewer or a developer loads it. await browser.installAddOn(fs.readFileSync(XPI).toString('base64'), true); diff --git a/tests/e2e/wdio.pbm.conf.mjs b/tests/e2e/wdio.pbm.conf.mjs index caf5fbe03..3f2e329ee 100644 --- a/tests/e2e/wdio.pbm.conf.mjs +++ b/tests/e2e/wdio.pbm.conf.mjs @@ -69,7 +69,7 @@ async function grantPrivateBrowsing(hook, args) { await browser.setMozContext('chrome'); try { - const granted = await browser.executeAsync((extId, done) => { + const granted = await browser.execute((extId) => new Promise((done) => { (async () => { try { const {ExtensionPermissions} = ChromeUtils.importESModule( @@ -88,7 +88,7 @@ async function grantPrivateBrowsing(hook, args) { done({err: String(e)}); } })(); - }, EXTENSION_ID); + }), EXTENSION_ID); if (!granted || granted.privateBrowsingAllowed !== true) { throw new Error(