diff --git a/.changeset/wdio-v10-support.md b/.changeset/wdio-v10-support.md new file mode 100644 index 00000000..8bbe89a2 --- /dev/null +++ b/.changeset/wdio-v10-support.md @@ -0,0 +1,15 @@ +--- +"@wdio/browserstack-service": minor +--- + +Support WebdriverIO 10. WebdriverIO 9 stays supported. + +- The `@wdio/cli` peer range is now `^9.0.0 || ^10.0.0`. It allowed WebdriverIO 5 to 8, but the service needs WebdriverIO 9 or 10. WebdriverIO 7 and 8 users use the `v7` and `v8` release lines of the service. +- Multiremote sessions use `isMultiRemote` (v10) or `isMultiremote` (v9), and get each instance with `getInstance()`. +- Accessibility command wrapping passes `{ attachToElement: true }` to `overwriteCommand`, which works in v9 and v10. +- The CLI accessibility scripts run through `execute()`, because v10 removed `executeAsync()`. +- The BiDi `browserstack_executor` routing patches `executeAsync` only when the command exists (v9). +- On WebdriverIO 10 with Mocha, the tests that a failed `before` or `beforeEach` hook skipped are reported as failed with the hook error, as WebdriverIO 10 reports them. With `mochaOpts.failHookAffectedTests: false`, they stay skipped. +- On WebdriverIO 10, `execute` on a browsing context (from `browser.url()` or `browser.newWindow()`) also sends `browserstack_executor` scripts through the classic endpoint, and the accessibility auto-scan also runs before the browser commands of a browsing context, on that context. +- The CLI accessibility command wrapper no longer runs a failed command a second time. The error (for example a WebdriverIO 10 `StrictSelectorError`) goes to the test, and the service does not log it as its own error. +- On WebdriverIO 10, App Automate sessions that do not set an Appium version get `bstack:options.appiumVersion: '3.3.0'` (`browserstack.appium_version` for capabilities in the legacy format). App Automate uses Appium 1.22.0 by default, and WebdriverIO 10 supports Appium 3 only. diff --git a/.github/scripts/check-wdio-major.mjs b/.github/scripts/check-wdio-major.mjs new file mode 100644 index 00000000..fc7a4ade --- /dev/null +++ b/.github/scripts/check-wdio-major.mjs @@ -0,0 +1,27 @@ +// Confirms that a CI job tests the WebdriverIO major version that it claims to test. +// +// The service supports WebdriverIO 9 and 10, and ci.yml runs the tests once for each major. +// `npm ci` installs the WebdriverIO 10 dev dependencies from the lockfile. The WebdriverIO 9 job then +// replaces them with `npm install --no-save ...@^9`. If that install keeps a v10 copy, or leaves a +// mix of v9 and v10 packages, the job still passes, but it tests the wrong version. This script +// fails the job in that case, so a green WebdriverIO 9 job means that the tests pass on WebdriverIO 9. +// +// It reads the `npm ls` output of the service, prints each installed version, and fails when a +// package is not on , or when `npm ls` did not list exactly packages (for example, +// a package is missing from the tree). +// +// Usage: npm ls -w @wdio/browserstack-service --depth=0 --json | node check-wdio-major.mjs +import { readFileSync } from 'node:fs' + +const [major, count] = process.argv.slice(2) +const tree = JSON.parse(readFileSync(0, 'utf8')) +const deps = tree.dependencies['@wdio/browserstack-service'].dependencies ?? {} + +for (const [name, { version }] of Object.entries(deps)) { + console.log(`${name}@${version}`) +} +const wrong = Object.values(deps).filter(({ version }) => !version?.startsWith(`${major}.`)) +if (wrong.length > 0 || Object.keys(deps).length !== Number(count)) { + console.error(`Expected ${count} packages on WebdriverIO ${major}`) + process.exit(1) +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a2f0f9e6..2d41ad0d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,12 +31,15 @@ jobs: run: npm run lint build-test: - name: Build & test (node ${{ matrix.node-version }}) + name: Build & test (wdio ${{ matrix.wdio }}, node ${{ matrix.node-version }}) runs-on: ubuntu-latest strategy: fail-fast: false matrix: - node-version: ['18.20', '20', '22'] + include: + - { wdio: 10, node-version: '24' } + # WebdriverIO 9 compatibility: the service supports both majors + - { wdio: 9, node-version: '22' } steps: - name: Checkout uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 @@ -50,8 +53,24 @@ jobs: - name: Install run: npm ci + # The published build uses the WebdriverIO 10 types. Every job builds before it installs another + # WebdriverIO major, because the build also generates the gRPC client that the tests import. - name: Build run: npm run build + - name: Install WebdriverIO ${{ matrix.wdio }} + if: matrix.wdio != 10 + run: >- + npm install --no-save -w @wdio/browserstack-service + webdriverio@^${{ matrix.wdio }} @wdio/cli@^${{ matrix.wdio }} @wdio/globals@^${{ matrix.wdio }} + @wdio/logger@^${{ matrix.wdio }} @wdio/reporter@^${{ matrix.wdio }} @wdio/types@^${{ matrix.wdio }} + + - name: Check WebdriverIO version + run: >- + npm ls -w @wdio/browserstack-service webdriverio @wdio/types @wdio/reporter @wdio/logger --depth=0 --json + | node .github/scripts/check-wdio-major.mjs ${{ matrix.wdio }} 4 + - name: Test + env: + WDIO_MAJOR: ${{ matrix.wdio }} run: npm test diff --git a/package-lock.json b/package-lock.json index f9f67c78..b58b1956 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", @@ -42,6 +43,7 @@ "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -489,12 +491,12 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "aix" ], - "peer": true, "engines": { "node": ">=18" } @@ -506,12 +508,12 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -523,12 +525,12 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -540,12 +542,12 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -557,12 +559,12 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=18" } @@ -574,12 +576,12 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=18" } @@ -591,12 +593,12 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -608,12 +610,12 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -625,12 +627,12 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -642,12 +644,12 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -659,12 +661,12 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -676,12 +678,12 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -693,12 +695,12 @@ "cpu": [ "mips64el" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -710,12 +712,12 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -727,12 +729,12 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -744,12 +746,12 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -761,12 +763,12 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -778,12 +780,12 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -795,12 +797,12 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -812,12 +814,12 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -829,12 +831,12 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -846,12 +848,12 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "openharmony" ], - "peer": true, "engines": { "node": ">=18" } @@ -863,12 +865,12 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "sunos" ], - "peer": true, "engines": { "node": ">=18" } @@ -880,12 +882,12 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -897,12 +899,12 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -914,12 +916,12 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -1122,8 +1124,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -1132,8 +1134,8 @@ "version": "4.3.2", "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/core": "^10.3.2", @@ -1157,8 +1159,8 @@ "version": "5.1.21", "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.21.tgz", "integrity": "sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10" @@ -1179,8 +1181,8 @@ "version": "10.3.2", "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/figures": "^1.0.15", @@ -1207,8 +1209,8 @@ "version": "4.2.23", "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/external-editor": "^1.0.3", @@ -1230,8 +1232,8 @@ "version": "4.0.23", "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10", @@ -1253,6 +1255,7 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, "license": "MIT", "dependencies": { "chardet": "^2.1.1", @@ -1274,8 +1277,8 @@ "version": "1.0.15", "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -1284,8 +1287,8 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10" @@ -1306,8 +1309,8 @@ "version": "3.0.23", "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10" @@ -1328,8 +1331,8 @@ "version": "4.0.23", "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/core": "^10.3.2", @@ -1351,8 +1354,8 @@ "version": "7.10.1", "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.10.1.tgz", "integrity": "sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/checkbox": "^4.3.2", "@inquirer/confirm": "^5.1.21", @@ -1381,8 +1384,8 @@ "version": "4.1.11", "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/type": "^3.0.10", @@ -1404,8 +1407,8 @@ "version": "3.2.2", "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/core": "^10.3.2", "@inquirer/figures": "^1.0.15", @@ -1428,8 +1431,8 @@ "version": "4.4.2", "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/core": "^10.3.2", @@ -1453,8 +1456,8 @@ "version": "3.0.10", "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -1489,58 +1492,74 @@ } }, "node_modules/@jest/diff-sequences": { - "version": "30.4.0", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz", - "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==", + "version": "30.5.0", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.5.0.tgz", + "integrity": "sha512-OsqBjHXCn8cadasoAZBP6nWYvMsRhpMzGXTpxJ5aO04NlbdhIz+FVe3q49l0AwVhsz/cEmIpBes6gAFl1/dWQg==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/expect-utils": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz", - "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==", + "version": "30.5.2", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.5.2.tgz", + "integrity": "sha512-d/HOLSrJUlBIl6n3LzOa7cjW3xdePm5H5gQw2lixZ/0h157l9sw3mLblzFqnPcTbhDzhRQdBOX3A3KcG73nm5Q==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@jest/get-type": "30.1.0" + "@jest/get-type": "30.5.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/get-type": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", + "version": "30.5.0", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.5.0.tgz", + "integrity": "sha512-9/2VUPitAjmBzbvDvqrxmvB7BzWsBW0WmkkojX1ODuxX1NLGxx9gfaZpHB0z8DtJ9uhGNmZG/VXBhf8uO0OV8Q==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/@jest/pattern": { - "version": "30.4.0", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz", - "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==", + "version": "30.5.0", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.5.0.tgz", + "integrity": "sha512-HdNQYSdRTEBNrginaqzQtTjG0HRMfrra/z6Ok7uL3S87vSlarIVohEsJsSj5edu3MiHoHjAkvPROz5ZjoKai+w==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", - "jest-regex-util": "30.4.0" + "jest-regex-util": "30.5.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, + "node_modules/@jest/react-is-18": { + "name": "react-is", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jest/react-is-19": { + "name": "react-is", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.3.0.tgz", + "integrity": "sha512-UpMYezM4v5/18F28aC66AEsjXIgE02kyEMH6yLdgLXu/UTfa1Ntwck/nNLrbqJsEXW7gPb0coNO9FQse9WTovA==", + "dev": true, + "license": "MIT" + }, "node_modules/@jest/schemas": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz", - "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==", + "version": "30.5.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.5.0.tgz", + "integrity": "sha512-/hunigyNpc4RCjC0VaW3f5RCUZVM2+WQ65qP7z083Gmvac7or2LI50XVNOtE4YPgBpV0yxYiAgorAPGniCoJmg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -1549,14 +1568,14 @@ } }, "node_modules/@jest/types": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz", - "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==", + "version": "30.5.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.5.1.tgz", + "integrity": "sha512-LvVYn83nnXPl+Rg98nvcFgjx6nRMTArhSn6RAX/w3ELn54S8A42TYZvsCMdGUqTM8S0wyXbtlQdU6Hi6dykj9g==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@jest/pattern": "30.4.0", - "@jest/schemas": "30.4.1", + "@jest/pattern": "30.5.0", + "@jest/schemas": "30.5.0", "@types/istanbul-lib-coverage": "^2.0.6", "@types/istanbul-reports": "^3.0.4", "@types/node": "*", @@ -1571,8 +1590,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -1587,8 +1606,8 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -1601,9 +1620,10 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, "license": "MIT" }, "node_modules/@js-sdsl/ordered-map": { @@ -1681,18 +1701,6 @@ "node": ">=6 <7 || >=8" } }, - "node_modules/@nodable/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/nodable" - } - ], - "license": "MIT" - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1872,24 +1880,183 @@ "license": "BSD-3-Clause" }, "node_modules/@puppeteer/browsers": { - "version": "2.13.2", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.2.tgz", - "integrity": "sha512-5EUZSUIc37H6aIXyWO0Z4y8NlF8NnjgmqeQgOGiswAU7pY0HOo16ho4+alIWmSfdZnjqBRawMsP3I5YqLSn6kw==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-3.2.3.tgz", + "integrity": "sha512-2Bt3m6dDAJqmZehn0wiXSYyQmuhyiHHZ7FjvrVaS1W2RoFGV6eCK71UvKYkNJjkwC1Q9nobjhvTMhLQztFVINA==", "license": "Apache-2.0", "dependencies": { - "debug": "^4.4.3", - "extract-zip": "^2.0.1", - "progress": "^2.0.3", - "proxy-agent": "^6.5.0", - "semver": "^7.7.4", - "tar-fs": "^3.1.1", - "yargs": "^17.7.2" + "modern-tar": "^0.8.4", + "yargs": "^18.0.0" }, "bin": { - "browsers": "lib/cjs/main-cli.js" + "browsers": "lib/main-cli.js" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "proxy-agent": ">=8.0.1", + "yauzl": "^2.10.0 || ^3.4.0" + }, + "peerDependenciesMeta": { + "proxy-agent": { + "optional": true + }, + "yauzl": { + "optional": true + } + } + }, + "node_modules/@puppeteer/browsers/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@puppeteer/browsers/node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@puppeteer/browsers/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "license": "MIT" + }, + "node_modules/@puppeteer/browsers/node_modules/string-width": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", + "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@puppeteer/browsers/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@puppeteer/browsers/node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yargs": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", + "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@puppeteer/browsers/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" } }, "node_modules/@rollup/rollup-android-arm-eabi": { @@ -2253,22 +2420,22 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/@sinclair/typebox": { - "version": "0.34.49", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.49.tgz", - "integrity": "sha512-brySQQs7Jtn0joV8Xh9ZV/hZb9Ozb0pmazDIASBkYKCjXrXU3mpcFahmK/z4YDhGkQvP9mWJbVyahdtU5wQA+A==", - "license": "MIT", - "peer": true + "version": "0.34.52", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz", + "integrity": "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==", + "dev": true, + "license": "MIT" }, "node_modules/@sindresorhus/merge-streams": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -2317,15 +2484,15 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } @@ -2334,8 +2501,8 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/istanbul-lib-report": "*" } @@ -2360,6 +2527,7 @@ "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, "license": "MIT" }, "node_modules/@types/sinonjs__fake-timers": { @@ -2368,12 +2536,21 @@ "integrity": "sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ==", "license": "MIT" }, + "node_modules/@types/split2": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@types/split2/-/split2-4.2.3.tgz", + "integrity": "sha512-59OXIlfUsi2k++H6CHgUQKEb2HKRokUA39HY1i1dS8/AIcqVjtAAFdf8u+HxTWK/4FUHMJQlKSZ4I6irCBJ1Zw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/@types/tar": { "version": "7.0.87", @@ -2399,9 +2576,9 @@ "license": "MIT" }, "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "version": "8.18.2", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.2.tgz", + "integrity": "sha512-67MQl+fpWKVTT1NYdnmo3U4sc/xPo/zQBncVnI74qmQa0z/b+1g6iYqNmGCPbxO+zz2aklb08a0oHfegiVd0/w==", "license": "MIT", "dependencies": { "@types/node": "*" @@ -2411,8 +2588,8 @@ "version": "17.0.35", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/yargs-parser": "*" } @@ -2421,14 +2598,14 @@ "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/@types/yauzl": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" @@ -2723,13 +2900,13 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", - "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-5.0.3.tgz", + "integrity": "sha512-3hr5eV5z2ff0l2Q1vaKQbB4j3M3tElw8stDokEOTqXJB3Es5VrT8gBRIMY59gGuFp8Y61sWlWFK1aNUH5dHDYw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "tinyrainbow": "^1.2.0" + "tinyrainbow": "^3.1.1" }, "funding": { "url": "https://opencollective.com/vitest" @@ -2778,13 +2955,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/runner/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, "node_modules/@vitest/runner/node_modules/tinyrainbow": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", @@ -2796,20 +2966,31 @@ } }, "node_modules/@vitest/snapshot": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", - "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-5.0.3.tgz", + "integrity": "sha512-SK4YTg9jL+jwakHIZxfciuvyOQsbY15AueR3USGtsXzmy+3FbJgqjAbC+ac20mRLwfEDtO5sfTqK6bUCDTRVPA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@vitest/pretty-format": "2.1.9", - "magic-string": "^0.30.12", - "pathe": "^1.1.2" + "@vitest/pretty-format": "5.0.3", + "@vitest/utils": "5.0.3", + "magic-string": "^1.2.3", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/snapshot/node_modules/magic-string": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", + "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } + }, "node_modules/@vitest/spy": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.6.tgz", @@ -2824,65 +3005,43 @@ } }, "node_modules/@vitest/utils": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", - "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-5.0.3.tgz", + "integrity": "sha512-77fDTZv8eu1CzlIfgPU2yrl5MXSUaUAOvgdZEeVm3X3WIxUubIX2SforqS6176lT32HCF6uBVMYVqfHxw7o6eg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@vitest/pretty-format": "4.1.8", + "@vitest/pretty-format": "5.0.3", "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils/node_modules/@vitest/pretty-format": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", - "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", - "license": "MIT", - "peer": true, - "dependencies": { - "tinyrainbow": "^3.1.0" + "tinyrainbow": "^3.1.1" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/utils/node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@wdio/browserstack-service": { "resolved": "packages/browserstack-service", "link": true }, "node_modules/@wdio/cli": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-9.28.0.tgz", - "integrity": "sha512-jEKYdCvZ9ST8YQ4EvyV9lsEoRxhWenplGJppbiH9SKHiwPqrUapi/EE7f6CBDwkWP7NIlzj2PyTe+JRmkXILLw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@vitest/snapshot": "^2.1.1", - "@wdio/config": "9.28.0", - "@wdio/globals": "9.28.0", - "@wdio/logger": "9.18.0", - "@wdio/protocols": "9.28.0", - "@wdio/types": "9.28.0", - "@wdio/utils": "9.28.0", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-10.0.1.tgz", + "integrity": "sha512-L4Hv+9oyDlJjkeS9O7e4c/4hh5HNAEEo00jfecdnICrEKdxMsXcnzHC3CCYne9875dlCtqsVl3jtBXe7lYZK8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/snapshot": "^5.0.2", + "@wdio/config": "10.0.1", + "@wdio/globals": "10.0.1", + "@wdio/logger": "10.0.0", + "@wdio/protocols": "10.0.0", + "@wdio/session": "10.0.1", + "@wdio/types": "10.0.1", + "@wdio/utils": "10.0.1", "async-exit-hook": "^2.0.1", "chalk": "^5.4.1", "chokidar": "^4.0.0", - "create-wdio": "9.28.0", + "create-wdio": "10.0.1", "dotenv": "^17.2.0", "import-meta-resolve": "^4.0.0", "lodash.flattendeep": "^4.4.0", @@ -2890,33 +3049,196 @@ "lodash.union": "^4.6.0", "read-pkg-up": "^10.0.0", "tsx": "^4.7.2", - "webdriverio": "9.28.0", - "yargs": "^17.7.2" + "webdriverio": "10.0.1", + "yargs": "^18.2.0" }, "bin": { "wdio": "bin/wdio.js" }, "engines": { - "node": ">=18.20.0" + "node": ">=22.19.0" } }, - "node_modules/@wdio/config": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-9.28.0.tgz", - "integrity": "sha512-a2po2x0Gi0hNRCuqSYSAvgwC9RZsj1tH9mt4MeLk2hyJBQCyy9DjBBjwyd4AcnE11XhqVaIkMaIMBSRu2dJwLw==", + "node_modules/@wdio/cli/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "dev": true, "license": "MIT", - "dependencies": { - "@wdio/logger": "9.18.0", - "@wdio/types": "9.28.0", - "@wdio/utils": "9.28.0", - "deepmerge-ts": "^7.0.3", - "glob": "^10.2.2", - "import-meta-resolve": "^4.0.0", - "jiti": "^2.6.1" - }, "engines": { - "node": ">=18.20.0" - } + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@wdio/cli/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@wdio/cli/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@wdio/cli/node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wdio/cli/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@wdio/cli/node_modules/string-width": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", + "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wdio/cli/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@wdio/cli/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@wdio/cli/node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@wdio/cli/node_modules/yargs": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", + "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@wdio/cli/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@wdio/config": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-10.0.1.tgz", + "integrity": "sha512-n6elHE4ItKAtr4lwAaWIh/sAFuEFhyesLM27i5l9IWBJEK6VECdt0Pigj/4vUD2tliOoBtsWxphSW4oiNFXotg==", + "license": "MIT", + "dependencies": { + "@wdio/logger": "10.0.0", + "@wdio/types": "10.0.1", + "@wdio/utils": "10.0.1", + "deepmerge-ts": "^8.0.0", + "glob": "^10.2.2", + "import-meta-resolve": "^4.0.0", + "jiti": "^2.6.1" + }, + "engines": { + "node": ">=22.19.0" + } }, "node_modules/@wdio/config/node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -2936,9 +3258,9 @@ } }, "node_modules/@wdio/config/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", "license": "MIT", "engines": { "node": ">=12" @@ -2966,9 +3288,9 @@ "license": "MIT" }, "node_modules/@wdio/config/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -3096,17 +3418,32 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@wdio/display-server": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@wdio/display-server/-/display-server-10.0.1.tgz", + "integrity": "sha512-DMXpSCUpk/3KArcTFNlyEPI6O7EzeW9b3C4m54yZSlwaXFtix0P96c6ofiuvZ6bf/6bVd7s2GAxGk3yhmK8giw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@wdio/logger": "10.0.0", + "@wdio/types": "10.0.1" + }, + "engines": { + "node": ">=22.19.0" + } + }, "node_modules/@wdio/globals": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-9.28.0.tgz", - "integrity": "sha512-poYsF7Gbm8kfYX6tdsPG862anOQKyUT8roe+rwdXaKSorz/s4XDJBm4kJiid6LgWKeAMSXDYzFODzDxhYhugWg==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-10.0.1.tgz", + "integrity": "sha512-obFsOz6oc2brP4lQ6alxonoKw+f5GsNf7tsEVAiBvDdzvYKFzS4jP2kYUZ2iCcqWhzOfbQlYkwDtzg4JPTIASg==", + "dev": true, "license": "MIT", "engines": { - "node": ">=18.20.0" + "node": ">=22.19.0" }, "peerDependencies": { - "expect-webdriverio": "^5.6.5", - "webdriverio": "^9.0.0" + "expect-webdriverio": "^8.0.0-0 || ^8.0.0-alpha.3", + "webdriverio": "^10.0.0-0 || ^10.0.0" }, "peerDependenciesMeta": { "expect-webdriverio": { @@ -3118,9 +3455,9 @@ } }, "node_modules/@wdio/logger": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-9.18.0.tgz", - "integrity": "sha512-HdzDrRs+ywAqbXGKqe1i/bLtCv47plz4TvsHFH3j729OooT5VH38ctFn5aLXgECmiAKDkmH/A6kOq2Zh5DIxww==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-10.0.0.tgz", + "integrity": "sha512-kUIw0ZzMs1mvXOaLEcjrM1nqNXY1reaJKReKNcjDSnfhnfUIb0Q5pAsxiMeil2uR0WsSUPzn0r3D9v//7vTOkA==", "license": "MIT", "dependencies": { "chalk": "^5.1.2", @@ -3130,13 +3467,13 @@ "strip-ansi": "^7.1.0" }, "engines": { - "node": ">=18.20.0" + "node": ">=22.19.0" } }, "node_modules/@wdio/logger/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", "license": "MIT", "engines": { "node": ">=12" @@ -3161,427 +3498,436 @@ } }, "node_modules/@wdio/protocols": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-9.28.0.tgz", - "integrity": "sha512-bO9NeMCrtwfWI7q77GwfD68NlRNijnmwicW1OQ6p+7D3kZWEicfdhfvojPhjjf+e9XzqMDnUDGD5ni1lGMUBsg==", - "license": "MIT" + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-10.0.0.tgz", + "integrity": "sha512-426ENC79fH7N4ggoGRHhZBY/exkdHAndbBiXcs9pQkzGP356H8C7VkQUx8g78m4k1TUC0RhOl8Se8VE0Mutpqw==", + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } }, "node_modules/@wdio/repl": { - "version": "9.16.2", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-9.16.2.tgz", - "integrity": "sha512-FLTF0VL6+o5BSTCO7yLSXocm3kUnu31zYwzdsz4n9s5YWt83sCtzGZlZpt7TaTzb3jVUfxuHNQDTb8UMkCu0lQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-10.0.0.tgz", + "integrity": "sha512-wAC9ksdWWv2E3Sj86eNKxB3IUt0gEyw9rgDGAWjk/GWgi6K3e+/n3erGNweI9JMJyC/+RNe0qHgVPQpRJMRVpg==", "license": "MIT", "dependencies": { - "@types/node": "^20.1.0" + "@types/node": "^22.20.4" }, "engines": { - "node": ">=18.20.0" + "node": ">=22.19.0" } }, "node_modules/@wdio/reporter": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-9.28.0.tgz", - "integrity": "sha512-q9gG6SXNTn/9cKF6EJ+aa5sGZM5HAVNsDZ3YU5B0IHg9ufdBuJgfT0LiAsnehLiceEuivuzPyz85vbDb0SFiVA==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-10.0.1.tgz", + "integrity": "sha512-A917SeniucyosspqazR9tGH6t9JaecU5SRP3VzRGyIo0JBEKV6walJU1VBauMH+7n043huzgiQvNJ414Vi8dmQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "^20.1.0", - "@wdio/logger": "9.18.0", - "@wdio/types": "9.28.0", - "diff": "^8.0.2", + "@types/node": "^22.20.4", + "@wdio/types": "10.0.1", + "diff": "^8.0.4", "object-inspect": "^1.12.0" }, "engines": { - "node": ">=18.20.0" + "node": ">=22.19.0" } }, - "node_modules/@wdio/types": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-9.28.0.tgz", - "integrity": "sha512-75JPq39gifkPNqOSn5C4/A5ZSyXwF+dGr5jfsCubFN9Lk9dKBXfjdbWueSQNpJg0jmE6dVrbT7+9mnDNnO0HdQ==", + "node_modules/@wdio/session": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@wdio/session/-/session-10.0.1.tgz", + "integrity": "sha512-JUbBiTpd5yW5YFl0/rHmBZ6FZI6raHyInL1wTTMASHV0cidUdJsect1ZVk6hTrK6EEHmjTrYyTAQyCHpBBTYDg==", + "dev": true, "license": "MIT", "dependencies": { - "@types/node": "^20.1.0" + "@types/node": "^22.20.4", + "@wdio/config": "10.0.1", + "@wdio/display-server": "10.0.1", + "@wdio/logger": "10.0.0", + "@wdio/types": "10.0.1", + "@wdio/utils": "10.0.1", + "acorn": "^8.15.0", + "expect-webdriverio": "^8.0.0-alpha.3", + "get-port": "^7.1.0", + "image-size": "^2.0.4", + "picomatch": "^4.0.7", + "semver": "^7.7.4", + "tsx": "^4.23.15", + "webdriverio": "10.0.1", + "yargs": "^18.2.0" }, - "engines": { - "node": ">=18.20.0" - } - }, - "node_modules/@wdio/utils": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-9.28.0.tgz", - "integrity": "sha512-VDqUaXpR8oOZSs26dy06Y2LhmA8bldsXDHeZ36n8SfW+Bq0miG0RRxou7aqx7sifVbbsuxrbBPXvmK+40uAIbQ==", - "license": "MIT", - "dependencies": { - "@puppeteer/browsers": "^2.2.0", - "@wdio/logger": "9.18.0", - "@wdio/types": "9.28.0", - "decamelize": "^6.0.0", - "deepmerge-ts": "^7.0.3", - "edgedriver": "^6.1.2", - "geckodriver": "^6.1.0", - "get-port": "^7.0.0", - "import-meta-resolve": "^4.0.0", - "locate-app": "^2.2.24", - "mitt": "^3.0.1", - "safaridriver": "^1.0.0", - "split2": "^4.2.0", - "wait-port": "^1.1.0" + "bin": { + "wdio-session": "bin/wdio-session.js" }, "engines": { - "node": ">=18.20.0" - } - }, - "node_modules/@zip.js/zip.js": { - "version": "2.8.26", - "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.8.26.tgz", - "integrity": "sha512-RQ4h9F6DOiHxpdocUDrOl6xBM+yOtz+LkUol47AVWcfebGBDpZ7w7Xvz9PS24JgXvLGiXXzSAfdCdVy1tPlaFA==", - "license": "BSD-3-Clause", - "engines": { - "bun": ">=0.7.0", - "deno": ">=1.0.0", - "node": ">=18.0.0" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" + "node": ">=22.19.0" }, - "engines": { - "node": ">=6.5" + "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 + } } }, - "node_modules/acorn": { - "version": "8.17.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", - "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "node_modules/@wdio/session/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", "dev": true, "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, "engines": { - "node": ">=0.4.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "node_modules/@wdio/session/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", + "node_modules/@wdio/session/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", "dependencies": { - "debug": "4" + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">= 6.0.0" + "node": ">=20" } }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "node_modules/@wdio/session/node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ajv/node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "node_modules/@wdio/session/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "dev": true, "license": "MIT" }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "node_modules/@wdio/session/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { - "node": ">=6" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "node_modules/@wdio/session/node_modules/string-width": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", + "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "dev": true, "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, "engines": { - "node": ">=8" + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "node_modules/@wdio/session/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, "license": "MIT", - "peer": true, + "dependencies": { + "ansi-regex": "^6.2.2" + }, "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "node_modules/anynum": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.0.tgz", - "integrity": "sha512-xjR9/zBVnUOP6ztMIIgShjsxui80nQUQH+5xJnvrYLs+90bF25/KJqaAi8mk+B4RDtX1Nspi6fmp4YTEts8SfA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/archiver": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", - "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "node_modules/@wdio/session/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, "license": "MIT", "dependencies": { - "archiver-utils": "^5.0.2", - "async": "^3.2.4", - "buffer-crc32": "^1.0.0", - "readable-stream": "^4.0.0", - "readdir-glob": "^1.1.2", - "tar-stream": "^3.0.0", - "zip-stream": "^6.0.1" + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">= 14" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/archiver-utils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", - "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "node_modules/@wdio/session/node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, "license": "MIT", "dependencies": { - "glob": "^10.0.0", - "graceful-fs": "^4.2.0", - "is-stream": "^2.0.1", - "lazystream": "^1.0.0", - "lodash": "^4.17.15", - "normalize-path": "^3.0.0", - "readable-stream": "^4.0.0" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">= 14" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/archiver-utils/node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", + "node_modules/@wdio/session/node_modules/yargs": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", + "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "dev": true, + "license": "MIT", "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" }, "engines": { - "node": ">=12" + "node": "^20.19.0 || ^22.12.0 || >=23" } }, - "node_modules/archiver-utils/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "license": "MIT", + "node_modules/@wdio/session/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": "^20.19.0 || ^22.12.0 || >=23" } }, - "node_modules/archiver-utils/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "node_modules/@wdio/types": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-10.0.1.tgz", + "integrity": "sha512-HGP9lAYXCsLAXzugfT4khCh7tYByWOMIxZHCG+tUvgmpwNzi6lfIsxyIlWownXK1wYJUpk6tkDBX/iXwN+q0lg==", "license": "MIT", - "engines": { - "node": ">=12" + "dependencies": { + "@types/node": "^22.20.4" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": ">=22.19.0" } }, - "node_modules/archiver-utils/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/archiver-utils/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "node_modules/@wdio/utils": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-10.0.1.tgz", + "integrity": "sha512-aYvTtF+7IYsvkFdXdu32Q2vexEaXYK79R1wLTnAXrT2Q7fp0oCDL0UgBx8Jgo5Tn98ZLWx4JlUeZsDRAIkV4Zw==", "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@puppeteer/browsers": "^3.2.1", + "@types/split2": "^4.2.3", + "@wdio/logger": "10.0.0", + "@wdio/types": "10.0.1", + "aria-query": "^5.3.2", + "decamelize": "^6.0.0", + "deepmerge-ts": "^8.0.0", + "edgedriver": "^7.0.0", + "electron-to-chromium": "^1.5.439", + "geckodriver": "^7.0.0", + "get-port": "^7.0.0", + "import-meta-resolve": "^4.0.0", + "locate-app": "^2.2.24", + "mitt": "^3.0.1", + "safaridriver": "^2.0.0", + "split2": "^4.2.0", + "wait-port": "^1.1.0" + }, + "engines": { + "node": ">=22.19.0" } }, - "node_modules/archiver-utils/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" + "node_modules/@zip.js/zip.js": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.23.0.tgz", + "integrity": "sha512-K5obAqfrO7zhycnCupM9aaoZ9kx0SA0kv7+Z2oOUsdpWquv4s/Z4zyD7AdTqxrK3pgPM+ozaUi8kdRQbCWaIrQ==", + "license": "BSD-3-Clause", + "engines": { + "bun": ">=0.7.0", + "deno": ">=1.0.0", + "node": ">=18.0.0" + } }, - "node_modules/archiver-utils/node_modules/glob": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", - "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", - "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", - "license": "ISC", + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" + "event-target-shim": "^5.0.0" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "engines": { + "node": ">=6.5" } }, - "node_modules/archiver-utils/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=8" + "bin": { + "acorn": "bin/acorn" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": ">=0.4.0" } }, - "node_modules/archiver-utils/node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/archiver-utils/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "license": "ISC", + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", "dependencies": { - "brace-expansion": "^2.0.2" + "debug": "4" }, "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">= 6.0.0" } }, - "node_modules/archiver-utils/node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/archiver-utils/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "node_modules/ajv/node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6" } }, - "node_modules/archiver-utils/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, "engines": { - "node": ">=12" + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/archiver-utils/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "node_modules/archiver": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-8.0.0.tgz", + "integrity": "sha512-fV1orZfsnPn9BaSByR/qE67rJCLJEy2Ox5bq7nJh+jquWaNh6Sfec75kJ2T6PtdGUbPQlrVoSVCEOa5SdiTQ1g==", "license": "MIT", "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "is-stream": "^4.0.0", + "lazystream": "^1.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^3.0.0", + "tar-stream": "^3.0.0", + "zip-stream": "^7.0.2" }, "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + "node": ">=18" } }, "node_modules/argparse": { @@ -3764,8 +4110,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.12.0" } @@ -3815,9 +4161,9 @@ } }, "node_modules/b4a": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", - "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.9.0.tgz", + "integrity": "sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==", "license": "Apache-2.0", "peerDependencies": { "react-native-b4a": "*" @@ -3838,9 +4184,9 @@ } }, "node_modules/bare-events": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", - "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz", + "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==", "license": "Apache-2.0", "peerDependencies": { "bare-abort-controller": "*" @@ -3852,9 +4198,9 @@ } }, "node_modules/bare-fs": { - "version": "4.7.2", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.2.tgz", - "integrity": "sha512-aTvMFUWkBmjzKtEQMDGGDNF8bkfpD5N1b/FCwt7A3wrU4t1o/e/85Wzkluh6JlODCjqVESYCkQCdTXqZ9G7VFg==", + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.2.tgz", + "integrity": "sha512-+ZI68KHMUvosXfKbg/UOHK0tbCdRnegbvPEdEcZ3Nd6TetieQsJPRXBRXPdLyy8+3VSEbPXtsumTpEtt78xv9w==", "license": "Apache-2.0", "dependencies": { "bare-events": "^2.5.4", @@ -3864,7 +4210,7 @@ "fast-fifo": "^1.3.2" }, "engines": { - "bare": ">=1.16.0" + "bare": ">=1.28.0" }, "peerDependencies": { "bare-buffer": "*" @@ -3875,28 +4221,16 @@ } } }, - "node_modules/bare-os": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.1.tgz", - "integrity": "sha512-6M5XjcnsygQNPMCMPXSK379xrJFiZ/AEMNBmFEmQW8d/789VQATvriyi5r0HYTL9TkQ26rn3kgdTG3aisbrXkQ==", - "license": "Apache-2.0", - "engines": { - "bare": ">=1.14.0" - } - }, "node_modules/bare-path": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.1.tgz", - "integrity": "sha512-ghj2DSK/2e99a1anTVPCV4m4YIYtrbXhfM7V3D7XZLOTsybnYyaJloymGqssQc8l/or0UoDyRtNQkmkEF/ysgQ==", - "license": "Apache-2.0", - "dependencies": { - "bare-os": "^3.0.1" - } + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.2.tgz", + "integrity": "sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==", + "license": "Apache-2.0" }, "node_modules/bare-stream": { - "version": "2.13.3", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", - "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.4.tgz", + "integrity": "sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==", "license": "Apache-2.0", "dependencies": { "b4a": "^1.8.1", @@ -3921,9 +4255,9 @@ } }, "node_modules/bare-url": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz", - "integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.4.tgz", + "integrity": "sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==", "license": "Apache-2.0", "dependencies": { "bare-path": "^3.0.0" @@ -4243,6 +4577,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true, "license": "MIT" }, "node_modules/check-error": { @@ -4310,8 +4645,8 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "readdirp": "^4.0.1" }, @@ -4335,6 +4670,7 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", + "dev": true, "funding": [ { "type": "github", @@ -4373,8 +4709,8 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, "license": "ISC", - "peer": true, "engines": { "node": ">= 12" } @@ -4459,52 +4795,41 @@ "version": "14.0.3", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=20" } }, "node_modules/compress-commons": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", - "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-7.0.1.tgz", + "integrity": "sha512-g0S8KAD8qf4+V//pr3BfB1aBnARLXNz2Gx+jmHU0LEriUuoQUOPOulVquHKTJ8+EAIIO7fhseNDr9wK5Q9FKBQ==", "license": "MIT", "dependencies": { "crc-32": "^1.2.0", - "crc32-stream": "^6.0.0", - "is-stream": "^2.0.1", + "crc32-stream": "^7.0.1", + "is-stream": "^4.0.0", "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/compress-commons/node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18" } }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, "license": "MIT" }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/core-js-compat": { "version": "3.49.0", @@ -4539,24 +4864,24 @@ } }, "node_modules/crc32-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", - "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-7.0.1.tgz", + "integrity": "sha512-IBWsY8xznyQrcHn8h4bC8/4ErNke5elzgG8GcqF4RFPw6aHkWWRc7Tgw6upjaTX/CT/yQgqYENkxYsTYN+hW2g==", "license": "MIT", "dependencies": { "crc-32": "^1.2.0", "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 14" + "node": ">=18" } }, "node_modules/create-wdio": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/create-wdio/-/create-wdio-9.28.0.tgz", - "integrity": "sha512-3Oa7tGK5QA9z1bdTFonnEb3OTTyNJtI2np7YzEC6F9es+94PFsFLLn5nIWs+fhJSdoueQyLy8P2cSWBO3Ohijw==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/create-wdio/-/create-wdio-10.0.1.tgz", + "integrity": "sha512-rmsgMda5gmNNwov0EKDqTTRu/Xj/LuQJrq8y42wV6YwFTwek8Z8v8T8y3t7brAL1JkwWJnF24F42EEBqxEo3pQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "chalk": "^5.3.0", "commander": "^14.0.0", @@ -4567,16 +4892,178 @@ "inquirer": "^12.7.0", "normalize-package-data": "^7.0.0", "read-pkg-up": "^10.1.0", - "recursive-readdir": "^2.2.3", "semver": "^7.6.3", "type-fest": "^4.41.0", - "yargs": "^17.7.2" + "yargs": "^18.2.0" }, "bin": { "create-wdio": "bin/wdio.js" }, "engines": { - "node": ">=12.0.0" + "node": ">=22.19.0" + } + }, + "node_modules/create-wdio/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/create-wdio/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/create-wdio/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/create-wdio/node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/create-wdio/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-wdio/node_modules/string-width": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", + "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/create-wdio/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/create-wdio/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/create-wdio/node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/create-wdio/node_modules/yargs": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", + "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/create-wdio/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" } }, "node_modules/cross-spawn": { @@ -4734,6 +5221,7 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4747,12 +5235,22 @@ "license": "MIT" }, "node_modules/deepmerge-ts": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", - "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-8.0.2.tgz", + "integrity": "sha512-uqbvqLUMrc6p0MO+WBRtTxY55hmyh94WRwI5a++PZe54X+bfVh59FSN7uWCBCW1CCVjzjnrwzfI8zidE2obMMw==", + "funding": [ + { + "type": "ko-fi", + "url": "https://ko-fi.com/rebeccastevens" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/deepmerge-ts" + } + ], "license": "BSD-3-Clause", "engines": { - "node": ">=16.0.0" + "node": ">=16.9.0" } }, "node_modules/define-data-property": { @@ -4932,8 +5430,8 @@ "version": "17.4.2", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=12" }, @@ -4988,20 +5486,19 @@ } }, "node_modules/edgedriver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-6.3.0.tgz", - "integrity": "sha512-ggEQL+oEyIcM4nP2QC3AtCQ04o4kDNefRM3hja0odvlPSnsaxiruMxEZ93v3gDCKWYW6BXUr51PPradb+3nffw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-7.0.0.tgz", + "integrity": "sha512-4sJLhorcBSfzaqMN41q2VI9KVW9Qcpo7CHbUcBUDWeQ7uHiWnRWayV1KZOQr9jllSBOHD2aXzhUWrC2yCt+PRA==", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@wdio/logger": "^9.18.0", + "@wdio/logger": "^9.29.1", "@zip.js/zip.js": "^2.8.11", "decamelize": "^6.0.1", "edge-paths": "^3.0.5", - "fast-xml-parser": "^5.3.3", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", - "which": "^6.0.0" + "which": "^6.0.1" }, "bin": { "edgedriver": "bin/edgedriver.js" @@ -5010,6 +5507,22 @@ "node": ">=20.0.0" } }, + "node_modules/edgedriver/node_modules/@wdio/logger": { + "version": "9.29.1", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-9.29.1.tgz", + "integrity": "sha512-0ZAEIo6PNyMIJPlOGkIgyOJUjcd0pC8/QHlVAAe1c91/IcjZ1X+k0yidXHaboJdN7dq1XPUacmhRdtua0U5EZg==", + "license": "MIT", + "dependencies": { + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "safe-regex2": "^5.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18.20.0" + } + }, "node_modules/edgedriver/node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -5019,6 +5532,18 @@ "node": ">= 14" } }, + "node_modules/edgedriver/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/edgedriver/node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", @@ -5041,6 +5566,21 @@ "node": ">=20" } }, + "node_modules/edgedriver/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/edgedriver/node_modules/which": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", @@ -5060,8 +5600,8 @@ "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "jake": "^10.8.5" }, @@ -5073,10 +5613,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.387", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.387.tgz", - "integrity": "sha512-TaxwufTFDufvPEoXdhwVrA3UdFWBeWGkYoJ1K8ldF1xe6gKfth6iRNS5lTQ5JPNOHdGQm8PT1QYKUqFLCiUefQ==", - "dev": true, + "version": "1.5.444", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.444.tgz", + "integrity": "sha512-5ss/uJfoDYDHT0lfJzT6FbcskIzROIOPf0BbbFkGcvDzoJU7i//9GDrwwIHQVmIsrAGiF3ihpADBRIsrEFt1rQ==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -5110,15 +5649,6 @@ "node": ">=0.10.0" } }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, "node_modules/enquirer": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", @@ -5149,6 +5679,7 @@ "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" @@ -5332,9 +5863,9 @@ "version": "0.28.1", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "bin": { "esbuild": "bin/esbuild" }, @@ -5383,8 +5914,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -6122,8 +6653,8 @@ "version": "9.6.1", "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.1.tgz", "integrity": "sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sindresorhus/merge-streams": "^4.0.0", "cross-spawn": "^7.0.6", @@ -6146,18 +6677,18 @@ } }, "node_modules/expect": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.4.1.tgz", - "integrity": "sha512-PMARsyh/JtqC20HoGqlFcIlQAyqUtW4PlI1rup1uhYJtKuwAjbvWi3GQMAn+STdHum/dk8xrKfUM1+5SAwpolA==", + "version": "30.5.2", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.5.2.tgz", + "integrity": "sha512-0LNuMvs8/5mRYhnCR4k+lGV7fqPMs6Bnksda4S3zsCUmdxBpAn4zU4NNzXdq1pMKe/H4W5t/zVIDSJ/H3e0vDA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@jest/expect-utils": "30.4.1", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.4.1", - "jest-message-util": "30.4.1", - "jest-mock": "30.4.1", - "jest-util": "30.4.1" + "@jest/expect-utils": "30.5.2", + "@jest/get-type": "30.5.0", + "jest-matcher-utils": "30.5.2", + "jest-message-util": "30.5.1", + "jest-mock": "30.5.2", + "jest-util": "30.5.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -6174,83 +6705,30 @@ } }, "node_modules/expect-webdriverio": { - "version": "5.6.8", - "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-5.6.8.tgz", - "integrity": "sha512-VfLC9o84B40LEw+zX7UykUptKkscX1rPYY4jaAsQ6KyKL0X0ltDkWzKIUiY9g/u0ApNerrkhM/QEY0TDT8pJOQ==", + "version": "8.0.0-alpha.3", + "resolved": "https://registry.npmjs.org/expect-webdriverio/-/expect-webdriverio-8.0.0-alpha.3.tgz", + "integrity": "sha512-kqQKGhRSZhUWzJSv6RRSE27Tk0xh2boP5wXVzh84HS79rSH61YXxiJMTJmPVjr1tQXcrFWfsiCxed60NnYjuXg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@vitest/snapshot": "^4.1.7", + "@vitest/snapshot": "^5.0.2", "deep-eql": "^5.0.2", - "expect": "^30.4.1", - "jest-matcher-utils": "^30.4.1" + "expect": "^30.5.2", + "jest-matcher-utils": "^30.5.2" }, "engines": { - "node": ">=20" + "node": ">=22.19.0" }, "peerDependencies": { - "@wdio/globals": "^9.0.0", - "@wdio/logger": "^9.0.0", - "webdriverio": "^9.0.0" + "@wdio/types": "^10.0.0-0", + "webdriverio": "^10.0.0-0" }, "peerDependenciesMeta": { - "@wdio/globals": { - "optional": false - }, - "@wdio/logger": { - "optional": false - }, "webdriverio": { "optional": false } } }, - "node_modules/expect-webdriverio/node_modules/@vitest/pretty-format": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", - "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", - "license": "MIT", - "peer": true, - "dependencies": { - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/expect-webdriverio/node_modules/@vitest/snapshot": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", - "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "@vitest/pretty-format": "4.1.8", - "@vitest/utils": "4.1.8", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/expect-webdriverio/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "license": "MIT", - "peer": true - }, - "node_modules/expect-webdriverio/node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/extendable-error": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/extendable-error/-/extendable-error-0.1.7.tgz", @@ -6258,66 +6736,6 @@ "dev": true, "license": "MIT" }, - "node_modules/extract-zip": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", - "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", - "license": "BSD-2-Clause", - "dependencies": { - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - }, - "bin": { - "extract-zip": "cli.js" - }, - "engines": { - "node": ">= 10.17.0" - }, - "optionalDependencies": { - "@types/yauzl": "^2.9.1" - } - }, - "node_modules/extract-zip/node_modules/buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/extract-zip/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/extract-zip/node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "license": "MIT", - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==", - "license": "MIT" - }, "node_modules/fast-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", @@ -6355,44 +6773,6 @@ "dev": true, "license": "MIT" }, - "node_modules/fast-xml-builder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", - "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "path-expression-matcher": "^1.5.0", - "xml-naming": "^0.1.0" - } - }, - "node_modules/fast-xml-parser": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.8.0.tgz", - "integrity": "sha512-6bIM7fsJxeo3uXv7OncQYsBAMPJ7V16Slahl/6M98C/i2q+vB1+4a0MtrvYwDFEUrwDSbAmeLDRXsOBwrL7yAg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "@nodable/entities": "^2.1.0", - "fast-xml-builder": "^1.2.0", - "path-expression-matcher": "^1.5.0", - "strnum": "^2.3.0", - "xml-naming": "^0.1.0" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -6403,15 +6783,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fd-slicer": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", - "license": "MIT", - "dependencies": { - "pend": "~1.2.0" - } - }, "node_modules/fecha": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", @@ -6422,8 +6793,8 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "is-unicode-supported": "^2.0.0" }, @@ -6451,8 +6822,8 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "minimatch": "^5.0.1" } @@ -6461,15 +6832,15 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/filelist/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -6478,8 +6849,8 @@ "version": "5.1.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -6700,6 +7071,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -6754,9 +7126,9 @@ } }, "node_modules/geckodriver": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-6.1.0.tgz", - "integrity": "sha512-ZRXLa4ZaYTTgUO4Eefw+RsQCleugU2QLb1ME7qTYxxuRj51yAhfnXaItXNs5/vUzfIaDHuZ+YnSF005hfp07nQ==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-7.0.0.tgz", + "integrity": "sha512-RypjU7cNcyjWK3ruEriCkWMRa9nroCT3y2qwkPynGzKTK1wG0UCWubb4AfVDTXzG9BhzRTwg29DoeK7PCRnK1w==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -6765,7 +7137,7 @@ "decamelize": "^6.0.1", "http-proxy-agent": "^7.0.2", "https-proxy-agent": "^7.0.6", - "modern-tar": "^0.7.2" + "modern-tar": "^0.8.5" }, "bin": { "geckodriver": "bin/geckodriver.js" @@ -6774,6 +7146,22 @@ "node": ">=20.0.0" } }, + "node_modules/geckodriver/node_modules/@wdio/logger": { + "version": "9.29.1", + "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-9.29.1.tgz", + "integrity": "sha512-0ZAEIo6PNyMIJPlOGkIgyOJUjcd0pC8/QHlVAAe1c91/IcjZ1X+k0yidXHaboJdN7dq1XPUacmhRdtua0U5EZg==", + "license": "MIT", + "dependencies": { + "chalk": "^5.1.2", + "loglevel": "^1.6.0", + "loglevel-plugin-prefix": "^0.8.4", + "safe-regex2": "^5.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18.20.0" + } + }, "node_modules/geckodriver/node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -6783,6 +7171,18 @@ "node": ">= 14" } }, + "node_modules/geckodriver/node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/geckodriver/node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", @@ -6796,6 +7196,21 @@ "node": ">= 14" } }, + "node_modules/geckodriver/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/generator-function": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", @@ -6815,6 +7230,18 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -6868,8 +7295,8 @@ "version": "9.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@sec-ant/readable-stream": "^0.4.1", "is-stream": "^4.0.1" @@ -7051,6 +7478,7 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, "license": "ISC" }, "node_modules/grapheme-splitter": { @@ -7166,8 +7594,8 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "dev": true, "license": "ISC", - "peer": true, "dependencies": { "lru-cache": "^10.0.1" }, @@ -7261,8 +7689,8 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "dev": true, "license": "Apache-2.0", - "peer": true, "engines": { "node": ">=18.18.0" } @@ -7271,6 +7699,7 @@ "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "dev": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" @@ -7313,6 +7742,19 @@ "node": ">= 4" } }, + "node_modules/image-size": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.4.tgz", + "integrity": "sha512-QRUkFFsRV/6fuESxb9Vkq+a0LkSrgKXuc2NEqfikiXxxN/G3tjWt5EVUlMaImRBZRZK/jRBEbYvpPYZL8t08Zw==", + "dev": true, + "license": "MIT", + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", @@ -7404,8 +7846,8 @@ "version": "12.11.1", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-12.11.1.tgz", "integrity": "sha512-9VF7mrY+3OmsAfjH3yKz/pLbJ5z22E23hENKw3/LNSaA/sAt3v49bDRY+Ygct1xwuKT+U+cBfTzjCPySna69Qw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@inquirer/ansi": "^1.0.2", "@inquirer/core": "^10.3.2", @@ -7473,6 +7915,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, "license": "MIT" }, "node_modules/is-async-function": { @@ -7826,7 +8269,6 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -7902,8 +8344,8 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -7998,8 +8440,8 @@ "version": "10.9.4", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "async": "^3.2.6", "filelist": "^1.0.4", @@ -8013,16 +8455,16 @@ } }, "node_modules/jest-diff": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.4.1.tgz", - "integrity": "sha512-CRpFK0RtLriVDGcPPAnR6HMVI8bSR2jnUIgralhauzYQZIb4RH9AtEInTuQr65LmmGggGcRT6HIASxwqsVsmlA==", + "version": "30.5.2", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.5.2.tgz", + "integrity": "sha512-rBtHnI6BWTiWj3lM7fOLVfChx7R9B0u9VV3PRHawKs79x6ZjW1QoSrKenaigNGWlvhtoi2BqvDbABDie+Os3xQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@jest/diff-sequences": "30.4.0", - "@jest/get-type": "30.1.0", + "@jest/diff-sequences": "30.5.0", + "@jest/get-type": "30.5.0", "chalk": "^4.1.2", - "pretty-format": "30.4.1" + "pretty-format": "30.5.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -8032,8 +8474,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -8048,8 +8490,8 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8062,16 +8504,16 @@ } }, "node_modules/jest-matcher-utils": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.4.1.tgz", - "integrity": "sha512-zvYfX5CaeEkFrrLS9suWe9rvJrm9J1Iv3ua8kIBv9GEPzcnsfBf0bob37la7s67fs0nlBC3EuvkOLnXQKxtx4A==", + "version": "30.5.2", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.5.2.tgz", + "integrity": "sha512-lFkB365PTIHOhPzwrb9T6gvlDnPpOZ7/c3ewOKKB7bzztqjVrtlyRL2MkyfJXpyY2u2SKw001JKrTx14fBUFcQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@jest/get-type": "30.1.0", + "@jest/get-type": "30.5.0", "chalk": "^4.1.2", - "jest-diff": "30.4.1", - "pretty-format": "30.4.1" + "jest-diff": "30.5.2", + "pretty-format": "30.5.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" @@ -8081,8 +8523,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -8097,8 +8539,8 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8111,20 +8553,20 @@ } }, "node_modules/jest-message-util": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.4.1.tgz", - "integrity": "sha512-kwCKIvq0MCW1HzLoGola9Te6JUdzgV0loyKJ3Qghrkz9i5/RRIHsL95BMQc2HBBhlBKC4j22K9p11TGHH8RBpQ==", + "version": "30.5.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.5.1.tgz", + "integrity": "sha512-UdQlLdd9wL/Ys7xRErckqwD6wPlSZYueosSWuHc1r2ztGLwlgPvtSJq2+BPEgaEY13WLvfFbmhTj8pba0Sd1jg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", - "@jest/types": "30.4.1", + "@jest/types": "30.5.1", "@types/stack-utils": "^2.0.3", "chalk": "^4.1.2", "graceful-fs": "^4.2.11", - "jest-util": "30.4.1", + "jest-util": "30.5.1", "picomatch": "^4.0.3", - "pretty-format": "30.4.1", + "pretty-format": "30.5.1", "slash": "^3.0.0", "stack-utils": "^2.0.6" }, @@ -8136,8 +8578,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -8152,8 +8594,8 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8166,11 +8608,11 @@ } }, "node_modules/jest-message-util/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8179,38 +8621,39 @@ } }, "node_modules/jest-mock": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.4.1.tgz", - "integrity": "sha512-/i8SVb8/NSB7RfNi8gfqu8gxLV23KaL5EpAttyb9iz8qWRIqXRLflycz/32wXsYkOnaUlx8NAKnJYtpsmXUmfw==", + "version": "30.5.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.5.2.tgz", + "integrity": "sha512-KTHb37Fhj8xY8qPFvTFaFJHcdsumQp+ExsUlgoX232Agmc706gagBs53+CYvulV7MVQo9AgXOUxsqzzVBoJlrA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@jest/types": "30.4.1", + "@jest/expect-utils": "30.5.2", + "@jest/types": "30.5.1", "@types/node": "*", - "jest-util": "30.4.1" + "jest-util": "30.5.1" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-regex-util": { - "version": "30.4.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.4.0.tgz", - "integrity": "sha512-mWlvLviKIgIQ8VCuM1xRdD0TWp3zlzionlmDBjuXVBs+VkmXq6FgW9T4Emr7oGz/Rk6feDCGyiugolcQEyp3mg==", + "version": "30.5.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.5.0.tgz", + "integrity": "sha512-Mg0WK7A6xRHLSA1udJ8y9f3lM0uUhFTBnLKzwPmqB9AylvpleJ6BLemR8K9dK27DY+cesDryoA7yLZCAHsPG1A==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/jest-util": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.4.1.tgz", - "integrity": "sha512-vjQb1sACEiv13DKJMDToJpzVW0joCsIQrmbg0fi7CyOOt+g9jTuQl2A216pWRBYhOVt53XbL/2LbMKg1BECWOw==", + "version": "30.5.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.5.1.tgz", + "integrity": "sha512-yKuxmNy2rSbTXw+3SIPanJo+nV4/BS1p26v44IYBFMsswSQySfMMcPHErnOncda7i9HEz0q605rIhSTBVgrZTg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@jest/types": "30.4.1", + "@jest/types": "30.5.1", "@types/node": "*", "chalk": "^4.1.2", "ci-info": "^4.2.0", @@ -8225,8 +8668,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -8241,8 +8684,8 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -8255,11 +8698,11 @@ } }, "node_modules/jest-util/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -8280,6 +8723,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -8329,8 +8773,8 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-3.0.2.tgz", "integrity": "sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } @@ -8493,8 +8937,8 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-2.0.4.tgz", "integrity": "sha512-wM1+Z03eypVAVUCE7QdSqpVIvelbOakn1M0bPDoA4SGWPx3sNDVUiMo3L6To6WWGClB7VyXnhQ4Sn7gxiJbE6A==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } @@ -8545,12 +8989,6 @@ "node": ">=8" } }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "license": "MIT" - }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -8567,8 +9005,8 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz", "integrity": "sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -8581,8 +9019,8 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==", - "license": "MIT", - "peer": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.startcase": { "version": "4.4.0", @@ -8595,13 +9033,7 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", - "license": "MIT", - "peer": true - }, - "node_modules/lodash.zip": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.zip/-/lodash.zip-4.2.0.tgz", - "integrity": "sha512-C7IOaBBK/0gMORRBd8OETNx3kmOkgIWIPvyDpZSCTwUrpYmgZwJkjZeOD8ww4xbOUOs4/attY+pciKvadNfFbg==", + "dev": true, "license": "MIT" }, "node_modules/logform": { @@ -8663,6 +9095,7 @@ "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" @@ -8785,9 +9218,9 @@ "license": "MIT" }, "node_modules/modern-tar": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.7.6.tgz", - "integrity": "sha512-sweCIVXzx1aIGTCdzcMlSZt1h8k5Tmk08VNAuRk3IU28XamGiOH5ypi11g6De2CH7PhYqSSnGy2A/EFhbWnVKg==", + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.8.5.tgz", + "integrity": "sha512-snEhs+6G5Tjd4I7tLCDOaoln2RgE0bD19RzEKgvgK2hZ5VKy3MpLhLTZ2fWpXSTg4K2cyPwp+VHATFJhxfnOeA==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -8813,8 +9246,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz", "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==", + "dev": true, "license": "ISC", - "peer": true, "engines": { "node": "^18.17.0 || >=20.5.0" } @@ -8909,8 +9342,8 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-7.0.1.tgz", "integrity": "sha512-linxNAT6M0ebEYZOx2tO6vBEFsVgnPpv+AVjk0wJHfaUIbq31Jm3T6vvZaarnOeWDh8ShnwXuaAyM7WT3RzErA==", + "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "hosted-git-info": "^8.0.0", "semver": "^7.3.5", @@ -8933,8 +9366,8 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "path-key": "^4.0.0", "unicorn-magic": "^0.3.0" @@ -8950,8 +9383,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -9088,6 +9521,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, "license": "ISC", "dependencies": { "wrappy": "1" @@ -9291,8 +9725,8 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-7.1.1.tgz", "integrity": "sha512-SgOTCX/EZXtZxBE5eJ97P4yGM5n37BwRU+YMsH4vNzFqJV/oWFXXCmwFlgWUM4PrakybVOueJJ6pwHqSVhTFDw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.21.4", "error-ex": "^1.3.2", @@ -9311,8 +9745,8 @@ "version": "3.13.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "dev": true, "license": "(MIT OR CC0-1.0)", - "peer": true, "engines": { "node": ">=14.16" }, @@ -9324,8 +9758,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -9392,21 +9826,6 @@ "node": ">=8" } }, - "node_modules/path-expression-matcher": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", - "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -9469,11 +9888,11 @@ } }, "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "license": "MIT", - "peer": true + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" }, "node_modules/pathval": { "version": "2.0.1", @@ -9495,6 +9914,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -9596,27 +10016,27 @@ } }, "node_modules/pretty-format": { - "version": "30.4.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.4.1.tgz", - "integrity": "sha512-K6KiKMHTL4jjX4u3Kir2EW07nRfcqVTXIImx50wbjHQTcZPgg+gjVeNTIT3l3L1Rd4UefxfogquC9J37SoFyyw==", + "version": "30.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.5.1.tgz", + "integrity": "sha512-byhRAPguVKMQIj4kjJwJ5lAskVhfuiSdiYl/aLTWpgkGEmic2jYhJh1yE9ih8Ox44Xg9ccCT11/S6QrzSJuNrg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "@jest/schemas": "30.4.1", - "ansi-styles": "^5.2.0", - "react-is-18": "npm:react-is@^18.3.1", - "react-is-19": "npm:react-is@^19.2.5" + "@jest/react-is-18": "npm:react-is@^18.3.1", + "@jest/react-is-19": "npm:react-is@^19.2.5", + "@jest/schemas": "30.5.0", + "ansi-styles": "^5.2.0" }, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } }, "node_modules/pretty-ms": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", - "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.1.tgz", + "integrity": "sha512-HzMy3Geq23nVALD/M2LliU+F+M+gVNsvkQWWqeBZ8HDiCgzo6YPJ/Omrmtq24EFrIsk0a3EkQGEd7bDOo+IhGA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "parse-ms": "^4.0.0" }, @@ -9642,15 +10062,6 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT" }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/protobufjs": { "version": "7.6.4", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.4.tgz", @@ -9674,62 +10085,6 @@ "node": ">=12.0.0" } }, - "node_modules/proxy-agent": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", - "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.6", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.1.0", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.5" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/proxy-agent/node_modules/lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/proxy-agent/node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, "node_modules/proxy-from-env": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", @@ -9739,16 +10094,6 @@ "node": ">=10" } }, - "node_modules/pump": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", - "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -9803,28 +10148,12 @@ ], "license": "MIT" }, - "node_modules/react-is-18": { - "name": "react-is", - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT", - "peer": true - }, - "node_modules/react-is-19": { - "name": "react-is", - "version": "19.2.7", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz", - "integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==", - "license": "MIT", - "peer": true - }, "node_modules/read-pkg": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-8.1.0.tgz", "integrity": "sha512-PORM8AgzXeskHO/WEv312k9U03B8K9JSiWF/8N9sUuFjBa+9SF2u6K7VClzXwDXab51jCd8Nd36CNM+zR97ScQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/normalize-package-data": "^2.4.1", "normalize-package-data": "^6.0.0", @@ -9842,8 +10171,8 @@ "version": "10.1.0", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-10.1.0.tgz", "integrity": "sha512-aNtBq4jR8NawpKJQldrQcSW9y/d+KWH4v24HWkHljOZ7H0av+YTGANBzRh9A5pw7v/bLVsLVPpOhJ7gHNVy8lA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "find-up": "^6.3.0", "read-pkg": "^8.1.0", @@ -9860,8 +10189,8 @@ "version": "6.3.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "locate-path": "^7.1.0", "path-exists": "^5.0.0" @@ -9877,8 +10206,8 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-locate": "^6.0.0" }, @@ -9893,8 +10222,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "yocto-queue": "^1.0.0" }, @@ -9909,8 +10238,8 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "p-limit": "^4.0.0" }, @@ -9925,8 +10254,8 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" } @@ -9935,8 +10264,8 @@ "version": "7.0.2", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, "license": "ISC", - "peer": true, "dependencies": { "lru-cache": "^10.0.1" }, @@ -9948,8 +10277,8 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "hosted-git-info": "^7.0.0", "semver": "^7.3.5", @@ -10016,47 +10345,26 @@ } }, "node_modules/readdir-glob": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-3.0.0.tgz", + "integrity": "sha512-AhNB2KgKeVJr16nK9LLZbJNWnYoT23ZrumNKFDebHBdkC8KHSqWo871JAUhoWC/RtjEVdqNMFpM6qrwRbaUqpw==", "license": "Apache-2.0", "dependencies": { - "minimatch": "^5.1.0" - } - }, - "node_modules/readdir-glob/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/readdir-glob/node_modules/brace-expansion": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", - "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/readdir-glob/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" + "minimatch": "^10.2.2" }, "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/yqnn" } }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 14.18.0" }, @@ -10065,50 +10373,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/recursive-readdir": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", - "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", - "license": "MIT", - "peer": true, - "dependencies": { - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/recursive-readdir/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT", - "peer": true - }, - "node_modules/recursive-readdir/node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", - "license": "MIT", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/recursive-readdir/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "license": "ISC", - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -10228,15 +10492,6 @@ "node": ">=8" } }, - "node_modules/resq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/resq/-/resq-1.11.0.tgz", - "integrity": "sha512-G10EBz+zAAy3zUd/CDoBbXRL6ia9kOo3xRHrMDsHljI0GDkhYlyjwoCx5+3eCC4swi1uCoZQhskuJkj7Gp57Bw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^2.0.1" - } - }, "node_modules/ret": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", @@ -10350,8 +10605,8 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/run-async/-/run-async-4.0.6.tgz", "integrity": "sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.12.0" } @@ -10384,16 +10639,16 @@ "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "tslib": "^2.1.0" } }, "node_modules/safaridriver": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-1.0.1.tgz", - "integrity": "sha512-jkg4434cYgtrIF2AeY/X0Wmd2W73cK5qIEFE3hDrrQenJH/2SDJIXGvPAigfvQTcE9+H31zkiNHbUqcihEiMRA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-2.0.0.tgz", + "integrity": "sha512-LL7Ufa9p1cltLmw0WGIbM6FFXibWMoM9NjJuS5QFiAAtEnEwkwMxaXy1BRKCBZKSZ+brs9eYd3LHHiJfIgjmZg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -10529,6 +10784,7 @@ "version": "7.8.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -10727,6 +10983,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -10830,6 +11087,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, "license": "Apache-2.0", "dependencies": { "spdx-expression-parse": "^3.0.0", @@ -10840,12 +11098,14 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, "license": "CC-BY-3.0" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, "license": "MIT", "dependencies": { "spdx-exceptions": "^2.1.0", @@ -10856,6 +11116,7 @@ "version": "3.0.23", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, "license": "CC0-1.0" }, "node_modules/split2": { @@ -10878,8 +11139,8 @@ "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "escape-string-regexp": "^2.0.0" }, @@ -10916,9 +11177,9 @@ } }, "node_modules/streamx": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", - "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", + "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==", "license": "MIT", "dependencies": { "events-universal": "^1.0.0", @@ -11069,8 +11330,8 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -11124,21 +11385,6 @@ "dev": true, "license": "MIT" }, - "node_modules/strnum": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.0.tgz", - "integrity": "sha512-sHrVyWWdq28RbhjuJdZsA1SnGRJV6NiXbk6AXBxDOsgAcA+lmpUZCYjOdLBxkXMwis6RRe7dlZt4VlIWFVzkmg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "anynum": "^1.0.0" - } - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -11180,30 +11426,16 @@ "node": ">=18" } }, - "node_modules/tar-fs": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz", - "integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==", - "license": "MIT", - "dependencies": { - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - }, - "optionalDependencies": { - "bare-fs": "^4.0.1", - "bare-path": "^3.0.0" - } - }, "node_modules/tar-stream": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", - "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.2.tgz", + "integrity": "sha512-+8NeqHRjQWH9nYlwo2gamAMImZCVzI4UoEgDpWorBt9OEfppiZn+uSkskzQKPWIZyji/C8fpWO7u69G0DX0tbg==", "license": "MIT", "dependencies": { - "b4a": "^1.6.4", - "bare-fs": "^4.5.5", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" + "b4a": "^1.9.0", + "bare-fs": "^4.8.2", + "fast-fifo": "^1.3.2", + "streamx": "^2.28.1" } }, "node_modules/teex": { @@ -11317,11 +11549,11 @@ } }, "node_modules/tinyrainbow": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", - "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz", + "integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=14.0.0" } @@ -11445,11 +11677,11 @@ "license": "0BSD" }, "node_modules/tsx": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", - "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", + "version": "4.23.15", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", + "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "~0.28.0" }, @@ -11618,8 +11850,8 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -11716,6 +11948,7 @@ "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, "license": "Apache-2.0", "dependencies": { "spdx-correct": "^3.0.0", @@ -11820,13 +12053,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/vite-node/node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -12400,25 +12626,25 @@ } }, "node_modules/webdriver": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.28.0.tgz", - "integrity": "sha512-MmtC/n5rhOh/EYyYI1SbRBdEWctKaQouVeEAybv5SD/2bhTjg800q7mvGqHzhTXpqTPY5cdbOtT0PBdT89wz9w==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-10.0.1.tgz", + "integrity": "sha512-EFggYPwYTyP2o/NsZsoHsARo4x/YNmbpIp7HsS7bUF7suQgScD7OO0px4KeZmACb+t1q2Yqthxne8Y2hLMmQfw==", "license": "MIT", "dependencies": { - "@types/node": "^20.1.0", + "@types/node": "^22.20.4", "@types/ws": "^8.5.3", - "@wdio/config": "9.28.0", - "@wdio/logger": "9.18.0", - "@wdio/protocols": "9.28.0", - "@wdio/types": "9.28.0", - "@wdio/utils": "9.28.0", - "deepmerge-ts": "^7.0.3", + "@wdio/config": "10.0.1", + "@wdio/logger": "10.0.0", + "@wdio/protocols": "10.0.0", + "@wdio/types": "10.0.1", + "@wdio/utils": "10.0.1", + "deepmerge-ts": "^8.0.0", "https-proxy-agent": "^7.0.6", - "undici": "^6.21.3", - "ws": "^8.8.0" + "undici": "^8.11.2", + "ws": "^8.21.0" }, "engines": { - "node": ">=18.20.0" + "node": ">=22.19.0" } }, "node_modules/webdriver/node_modules/agent-base": { @@ -12443,21 +12669,30 @@ "node": ">= 14" } }, + "node_modules/webdriver/node_modules/undici": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.11.2.tgz", + "integrity": "sha512-u4UB2/IrKdU6lFxumHmmo1a3fCQO5tzQllRorfoRS63txhrB7xTpSn1PftwC4qEHkOaqP95fCWW4lJzwErwzhQ==", + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } + }, "node_modules/webdriverio": { - "version": "9.28.0", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.28.0.tgz", - "integrity": "sha512-ieFWi8dq57uZC6QMC2x6TllxKTRyInIMcOrVvwbHqVRYvJP8OLDtlH1bideGRIN0pgGHWStqplez2A95jS9bqA==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-10.0.1.tgz", + "integrity": "sha512-nITIsmcooGy50RLrol4BDhnzEn04uZ5WJWVlebIQOnErhw2A0ZwR6XClX7v1z+qHtvDZVVEQs5i3Yfz9LR5XPA==", "license": "MIT", "dependencies": { - "@types/node": "^20.11.30", + "@types/node": "^22.20.4", "@types/sinonjs__fake-timers": "^8.1.5", - "@wdio/config": "9.28.0", - "@wdio/logger": "9.18.0", - "@wdio/protocols": "9.28.0", - "@wdio/repl": "9.16.2", - "@wdio/types": "9.28.0", - "@wdio/utils": "9.28.0", - "archiver": "^7.0.1", + "@wdio/config": "10.0.1", + "@wdio/logger": "10.0.0", + "@wdio/protocols": "10.0.0", + "@wdio/repl": "10.0.0", + "@wdio/types": "10.0.1", + "@wdio/utils": "10.0.1", + "archiver": "^8.0.0", "aria-query": "^5.3.0", "cheerio": "^1.0.0-rc.12", "css-shorthand-properties": "^1.1.1", @@ -12467,19 +12702,17 @@ "is-plain-obj": "^4.1.0", "jszip": "^3.10.1", "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.0.0", - "webdriver": "9.28.0" + "webdriver": "10.0.1" }, "engines": { - "node": ">=18.20.0" + "node": ">=22.19.0" }, "peerDependencies": { - "puppeteer-core": ">=22.x || <=24.x" + "puppeteer-core": ">=24 <26" }, "peerDependenciesMeta": { "puppeteer-core": { @@ -12691,8 +12924,8 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -12739,8 +12972,8 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -12755,12 +12988,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, "license": "ISC" }, "node_modules/ws": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz", + "integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -12778,21 +13012,6 @@ } } }, - "node_modules/xml-naming": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", - "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "engines": { - "node": ">=16.0.0" - } - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -12854,8 +13073,8 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12.20" }, @@ -12864,11 +13083,11 @@ } }, "node_modules/yoctocolors": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", - "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.2.0.tgz", + "integrity": "sha512-xYqdZFUK/VYazNl/oCDYN+3WloWQwMfZxBoiNt6qNyk+xfOdi598muWE42rNZFp1kNOiqW936q5RhUdnpqElSg==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -12880,8 +13099,8 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", + "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -12890,22 +13109,22 @@ } }, "node_modules/zip-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", - "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-7.0.5.tgz", + "integrity": "sha512-dSvYKdvLsAHCDqPOhIwk/q5CvuWtTB3Dgpoe0uVEFjTzIOAmsQpprX25InCvrvJsirEbu1OHyy67n/kAj1Sw/w==", "license": "MIT", "dependencies": { - "archiver-utils": "^5.0.0", - "compress-commons": "^6.0.2", + "compress-commons": "^7.0.0", + "normalize-path": "^3.0.0", "readable-stream": "^4.0.0" }, "engines": { - "node": ">= 14" + "node": ">=18" } }, "packages/browserstack-service": { "name": "@wdio/browserstack-service", - "version": "9.37.0", + "version": "9.39.4", "license": "MIT", "dependencies": { "@browserstack/ai-sdk-node": "1.5.17", @@ -12915,9 +13134,9 @@ "@percy/selenium-webdriver": "^2.2.2", "@percy/webdriverio": "^3.3.4", "@types/gitconfiglocal": "^2.0.1", - "@wdio/logger": "^9.0.0", - "@wdio/reporter": "^9.0.0", - "@wdio/types": "^9.0.0", + "@wdio/logger": "^9.0.0 || ^10.0.0", + "@wdio/reporter": "^9.0.0 || ^10.0.0", + "@wdio/types": "^9.0.0 || ^10.0.0", "browserstack-local": "^1.5.1", "chalk": "^5.6.2", "csv-writer": "^1.6.0", @@ -12927,7 +13146,7 @@ "tar": "^7.5.11", "undici": "^6.24.0", "uuid": "^11.1.0", - "webdriverio": "^9.0.0", + "webdriverio": "^9.0.0 || ^10.0.0", "winston-transport": "^4.5.0", "yauzl": "^3.4.0" }, @@ -12937,22 +13156,23 @@ "@types/node": "^20.1.0", "@types/tar": "^7.0.87", "@types/yauzl": "^2.10.3", - "@wdio/globals": "^9.0.0", - "@wdio/logger": "^9.0.0", - "@wdio/reporter": "^9.0.0", - "@wdio/types": "^9.0.0", + "@wdio/cli": "^10.0.1", + "@wdio/globals": "^10.0.1", + "@wdio/logger": "^10.0.0", + "@wdio/reporter": "^10.0.1", + "@wdio/types": "^10.0.1", "esbuild": "^0.27.2", "rimraf": "^6.0.1", "ts-proto": "^2.7.5", "typescript": "^5.8.3", "vitest": "^3.2.4", - "webdriverio": "^9.0.0" + "webdriverio": "^10.0.1" }, "engines": { "node": ">=18.20.0" }, "peerDependencies": { - "@wdio/cli": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + "@wdio/cli": "^9.0.0 || ^10.0.0" } }, "packages/browserstack-service/node_modules/@esbuild/aix-ppc64": { @@ -13499,11 +13719,6 @@ "@esbuild/win32-x64": "0.27.7" } }, - "packages/browserstack-service/node_modules/pathe": { - "version": "2.0.3", - "dev": true, - "license": "MIT" - }, "packages/browserstack-service/node_modules/picomatch": { "version": "4.0.4", "dev": true, diff --git a/package.json b/package.json index 9a3520ee..407a537b 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,9 @@ "eslint-plugin-unicorn": "^55.0.0" }, "overrides": { - "@types/node": "^20.1.0" + "@types/node": "^20.1.0", + "@percy/webdriverio": { + "webdriverio": "^9.0.0 || ^10.0.0" + } } } diff --git a/packages/browserstack-service/README.md b/packages/browserstack-service/README.md index 1c0f8d5e..78634c93 100644 --- a/packages/browserstack-service/README.md +++ b/packages/browserstack-service/README.md @@ -14,6 +14,15 @@ npm install @wdio/browserstack-service --save-dev Instructions on how to install `WebdriverIO` can be found [here.](https://webdriver.io/docs/gettingstarted) +### Compatibility + +| WebdriverIO | Node.js | +|-------------|---------| +| 10 | 22.19.0 or later | +| 9 | 18.20.0 or later | + +WebdriverIO 10 supports Appium 3 only. BrowserStack App Automate uses Appium 1.22.0 when you do not set a version. On WebdriverIO 10, the service sets `appiumVersion` in `bstack:options` to `'3.3.0'` for App Automate sessions that do not set a version (`browserstack.appium_version` for capabilities in the legacy format, without `bstack:options`). Version 3.3.0 supports Android 8 or later and iOS 15 or later. To use a different 3.x version (for example `'3.5.2'`, which needs iOS 16 or later), set `appiumVersion` in `bstack:options`. See [Select Appium version](https://www.browserstack.com/docs/app-automate/appium/set-up-tests/set-appium-version). Percy web snapshots (`@percy/webdriverio`) on WebdriverIO 10 need a `@percy/webdriverio` release that supports WebdriverIO 10. + ## Configuration diff --git a/packages/browserstack-service/package.json b/packages/browserstack-service/package.json index a0cbb17d..b3c9d678 100644 --- a/packages/browserstack-service/package.json +++ b/packages/browserstack-service/package.json @@ -60,9 +60,9 @@ "@percy/selenium-webdriver": "^2.2.2", "@percy/webdriverio": "^3.3.4", "@types/gitconfiglocal": "^2.0.1", - "@wdio/logger": "^9.0.0", - "@wdio/reporter": "^9.0.0", - "@wdio/types": "^9.0.0", + "@wdio/logger": "^9.0.0 || ^10.0.0", + "@wdio/reporter": "^9.0.0 || ^10.0.0", + "@wdio/types": "^9.0.0 || ^10.0.0", "browserstack-local": "^1.5.1", "chalk": "^5.6.2", "csv-writer": "^1.6.0", @@ -72,12 +72,12 @@ "tar": "^7.5.11", "undici": "^6.24.0", "uuid": "^11.1.0", - "webdriverio": "^9.0.0", + "webdriverio": "^9.0.0 || ^10.0.0", "winston-transport": "^4.5.0", "yauzl": "^3.4.0" }, "peerDependencies": { - "@wdio/cli": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + "@wdio/cli": "^9.0.0 || ^10.0.0" }, "devDependencies": { "@bufbuild/buf": "^1.55.1", @@ -85,16 +85,17 @@ "@types/node": "^20.1.0", "@types/tar": "^7.0.87", "@types/yauzl": "^2.10.3", - "@wdio/globals": "^9.0.0", - "@wdio/logger": "^9.0.0", - "@wdio/reporter": "^9.0.0", - "@wdio/types": "^9.0.0", + "@wdio/cli": "^10.0.1", + "@wdio/globals": "^10.0.1", + "@wdio/logger": "^10.0.0", + "@wdio/reporter": "^10.0.1", + "@wdio/types": "^10.0.1", "esbuild": "^0.27.2", "rimraf": "^6.0.1", "ts-proto": "^2.7.5", "typescript": "^5.8.3", "vitest": "^3.2.4", - "webdriverio": "^9.0.0" + "webdriverio": "^10.0.1" }, "publishConfig": { "access": "public" diff --git a/packages/browserstack-service/src/Percy/PercyHelper.ts b/packages/browserstack-service/src/Percy/PercyHelper.ts index 384bb521..eb6621de 100644 --- a/packages/browserstack-service/src/Percy/PercyHelper.ts +++ b/packages/browserstack-service/src/Percy/PercyHelper.ts @@ -57,7 +57,7 @@ export const getBestPlatformForPercySnapshot = (capabilities?: Capabilities.Test }) return bestPlatformCaps } else if (typeof capabilities === 'object') { - Object.entries(capabilities as Capabilities.RequestedMultiremoteCapabilities).forEach(([, caps]) => { + Object.entries(capabilities as Capabilities.RequestedMultiRemoteCapabilities).forEach(([, caps]) => { let currBrowserName = (caps.capabilities as WebdriverIO.Capabilities).browserName if ((caps.capabilities as WebdriverIO.Capabilities)['bstack:options']) { currBrowserName = (caps.capabilities as WebdriverIO.Capabilities)['bstack:options']?.browserName || currBrowserName diff --git a/packages/browserstack-service/src/accessibility-handler.ts b/packages/browserstack-service/src/accessibility-handler.ts index 1055bb53..4f88b37e 100644 --- a/packages/browserstack-service/src/accessibility-handler.ts +++ b/packages/browserstack-service/src/accessibility-handler.ts @@ -58,6 +58,9 @@ import { isAccessibilityAutomationSession, isAppAccessibilityAutomationSession, isBrowserstackSession, + isMultiRemoteBrowser, + commandScopeOptions, + overwriteBrowsingContextCommand, o11yClassErrorHandler, shouldScanTestForAccessibility, validateCapsWithA11y, @@ -319,7 +322,15 @@ class _AccessibilityHandler { const orig = browser[command.name as keyof WebdriverIO.Browser] const prevImpl = orig ? orig.bind(browser) : undefined // @ts-expect-error fix type - browser.overwriteCommand(command.name, this.commandWrapper.bind(this, command, prevImpl), command.class === 'Element') + browser.overwriteCommand(command.name, this.commandWrapper.bind(this, command, prevImpl), commandScopeOptions(command.class === 'Element')) + if (command.class !== 'Element') { + // WebdriverIO v10 browsing contexts have their own browser commands; scan the + // context that runs the command + const handler = this + overwriteBrowsingContextCommand(browser, command.name, function (this: WebdriverIO.Browser, origFunction: Function, ...args: unknown[]) { + return handler.wrapCommand(this, command as CommandInfo, origFunction, args) + }) + } } catch (error) { BStackLogger.debug(`Exception in overwrite command ${command.name} - ${error}`) } @@ -336,7 +347,7 @@ class _AccessibilityHandler { private supportsPreTestWindow(): boolean { return AccessibilityHandler.PRE_TEST_SCAN_FRAMEWORKS.includes(this._framework as string) && - !this._browser?.isMultiremote + !isMultiRemoteBrowser(this._browser) } async beforeTest (suiteTitle: string | undefined, test: Frameworks.Test) { @@ -541,6 +552,13 @@ class _AccessibilityHandler { */ private async commandWrapper (command: CommandInfo, prevImpl: Function, origFunction: Function, ...args: unknown[]) { + return this.wrapCommand(this._browser, command, prevImpl || origFunction, args) + } + + /** + * @param scanTarget the browser, or the WebdriverIO v10 browsing context that runs the command + */ + private async wrapCommand (scanTarget: WebdriverIO.Browser | WebdriverIO.MultiRemoteBrowser, command: CommandInfo, impl: Function, args: unknown[]) { const skipScanForBidiWindowCommand = AccessibilityHandler.shouldSkipScanForBidiWindowCommand(this._browser, command) if ( this._sessionId && AccessibilityHandler._a11yScanSessionMap[this._sessionId] && @@ -559,12 +577,11 @@ class _AccessibilityHandler { if (!(this._browser as WebdriverIO.Browser)?.sessionId) { BStackLogger.debug('Skipping accessibility scan: the session has ended') } else { - await performA11yScan(this.isAppAutomate, this._browser, true, true, command.name, undefined, this._currentHookRunUuid, this.hasNoParent) + await performA11yScan(this.isAppAutomate, scanTarget, true, true, command.name, undefined, this._currentHookRunUuid, this.hasNoParent) } } else if (skipScanForBidiWindowCommand) { BStackLogger.debug(`SDK-5047: skipping accessibility scan for BiDi window/context command '${command.name}' to avoid racing the WebdriverIO ContextManager during session-start window churn`) } - const impl = prevImpl || origFunction return impl(...args) } @@ -658,8 +675,9 @@ class _AccessibilityHandler { return true } if (Array.isArray(b?.instances)) { - const children = b as unknown as Record - return b.instances.some((name) => children[name]?.isBidi === true) + // WebdriverIO v10 has no named instance properties; getInstance works in v9 and v10 + const multiRemote = b as unknown as WebdriverIO.MultiRemoteBrowser + return b.instances.some((name) => (multiRemote.getInstance(name) as { isBidi?: boolean } | undefined)?.isBidi === true) } return false } diff --git a/packages/browserstack-service/src/ai-handler.ts b/packages/browserstack-service/src/ai-handler.ts index 2664ed9f..2cca82a3 100644 --- a/packages/browserstack-service/src/ai-handler.ts +++ b/packages/browserstack-service/src/ai-handler.ts @@ -10,7 +10,7 @@ import type { Capabilities } from '@wdio/types' import type BrowserStackConfig from './config.js' import type { Options } from '@wdio/types' import type { BrowserstackHealing } from '@browserstack/ai-sdk-node' -import { getBrowserStackUserAndKey, isBrowserstackInfra } from './util.js' +import { getBrowserStackUserAndKey, isBrowserstackInfra, isMultiRemoteBrowser } from './util.js' import type { BrowserstackOptions } from './types.js' import PerformanceTester from './instrumentation/performance/performance-tester.js' import * as PERFORMANCE_SDK_EVENTS from './instrumentation/performance/constants.js' @@ -124,7 +124,7 @@ class AiHandler { config: Options.Testrunner, browserStackConfig: BrowserStackConfig, options: BrowserstackOptions, - caps: Capabilities.RequestedMultiremoteCapabilities, + caps: Capabilities.RequestedMultiRemoteCapabilities, browser: string ) { if ( caps[browser].capabilities && @@ -145,7 +145,7 @@ class AiHandler { config: Options.Testrunner, browserStackConfig: BrowserStackConfig, options: BrowserstackOptions, - caps: Capabilities.RequestedMultiremoteCapabilities, + caps: Capabilities.RequestedMultiRemoteCapabilities, ) { const browserNames = Object.keys(caps) for (let i = 0; i < browserNames.length; i++) { @@ -173,7 +173,7 @@ class AiHandler { this.updateCaps(authResult, options, caps) } else if (isMultiremote) { - this.handleMultiRemoteSetup(authResult, config, browserStackConfig, options, caps as unknown as Capabilities.RequestedMultiremoteCapabilities) + this.handleMultiRemoteSetup(authResult, config, browserStackConfig, options, caps as unknown as Capabilities.RequestedMultiRemoteCapabilities) } } @@ -217,12 +217,14 @@ class AiHandler { async selfHeal(options: BrowserstackOptions, caps: Capabilities.ResolvedTestrunnerCapabilities, browser: WebdriverIO.Browser) { try { - const multiRemoteBrowsers = Object.keys(caps).filter(e => Object.keys(browser).includes(e)) + // WebdriverIO v10 has no named instance properties; getInstance works in v9 and v10 + const multiRemoteBrowser = browser as unknown as WebdriverIO.MultiRemoteBrowser + const multiRemoteBrowsers = isMultiRemoteBrowser(multiRemoteBrowser) + ? Object.keys(caps).filter(e => multiRemoteBrowser.instances.includes(e)) + : [] if (multiRemoteBrowsers.length > 0) { for (let i = 0; i < multiRemoteBrowsers.length; i++) { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const remoteBrowser = (browser as any)[multiRemoteBrowsers[i]] - await this.handleSelfHeal(options, remoteBrowser) + await this.handleSelfHeal(options, multiRemoteBrowser.getInstance(multiRemoteBrowsers[i])) } } else { await this.handleSelfHeal(options, browser) diff --git a/packages/browserstack-service/src/cli/modules/accessibilityModule.ts b/packages/browserstack-service/src/cli/modules/accessibilityModule.ts index edcb1b3b..a3c11a47 100644 --- a/packages/browserstack-service/src/cli/modules/accessibilityModule.ts +++ b/packages/browserstack-service/src/cli/modules/accessibilityModule.ts @@ -10,7 +10,7 @@ import { AutomationFrameworkState } from '../states/automationFrameworkState.js' import { HookState } from '../states/hookState.js' import type { Command } from '../../scripts/accessibility-scripts.js' import accessibilityScripts from '../../scripts/accessibility-scripts.js' -import { _getParamsForAppAccessibility, formatString, getAppA11yResults, getAppA11yResultsSummary, shouldScanTestForAccessibility, validateCapsWithA11y, validateCapsWithAppA11y, isBrowserstackSession } from '../../util.js' +import { _getParamsForAppAccessibility, commandScopeOptions, overwriteBrowsingContextCommand, executeAccessibilityScript, formatString, getAppA11yResults, getAppA11yResultsSummary, shouldScanTestForAccessibility, validateCapsWithA11y, validateCapsWithAppA11y, isBrowserstackSession } from '../../util.js' import { AutomationFrameworkConstants } from '../frameworks/constants/automationFrameworkConstants.js' import util from 'node:util' import type { Accessibility } from '../../grpc/index.js' @@ -262,8 +262,16 @@ export default class AccessibilityModule extends BaseModule { // @ts-expect-error fix type command.name, this.commandWrapper.bind(this, command), - command.class === 'Element' + commandScopeOptions(command.class === 'Element') ) + if (command.class !== 'Element') { + // WebdriverIO v10 browsing contexts have their own browser commands; + // scan the context that runs the command + const accessibilityModule = this + overwriteBrowsingContextCommand(browser, command.name, function (this: WebdriverIO.Browser, originFunction: Function, ...args: unknown[]) { + return accessibilityModule.wrapCommand(this, command, originFunction, args) + }) + } } catch (wrapError) { this.logger.debug(`Skipping command wrap for ${command.name}: ${wrapError}`) } @@ -289,6 +297,14 @@ export default class AccessibilityModule extends BaseModule { } private async commandWrapper(command: Command, originFunction: Function, ...args: unknown[]) { + return this.wrapCommand(undefined, command, originFunction, args) + } + + /** + * @param scanTarget the WebdriverIO v10 browsing context that runs the command, or undefined to + * scan the tracked driver + */ + private async wrapCommand(scanTarget: WebdriverIO.Browser | undefined, command: Command, originFunction: Function, args: unknown[]) { try { const autoInstance: AutomationFrameworkInstance = AutomationFramework.getTrackedInstance() const sessionId = AutomationFramework.getState(autoInstance, AutomationFrameworkConstants.KEY_FRAMEWORK_SESSION_ID) @@ -302,11 +318,8 @@ export default class AccessibilityModule extends BaseModule { // driver is the only thing that knows: no sessionId, no session. if (!browser?.sessionId) { this.logger.debug('Skipping accessibility scan: the session has ended') - return await originFunction(...args) - } - - // Perform accessibility scan before command if script is available - if ( + } else if ( + // Perform accessibility scan before command if script is available !command.name.includes('execute') || !this.shouldPatchExecuteScript(args.length ? args[0] as string : null) ) { @@ -314,24 +327,21 @@ export default class AccessibilityModule extends BaseModule { // Parentless only before the framework has started anything: no hook run // and no test seen yet. The wrapper never knows which hook it is in, and // does not need to. - await this.performScanCli(browser, command.name, this.currentHookRunUuid, this.hasNoParent) + await this.performScanCli(scanTarget ?? browser, command.name, this.currentHookRunUuid, this.hasNoParent) this.logger.debug(`Accessibility scan performed after ${command.name} command`) } catch (scanError) { this.logger.debug(`Error performing accessibility scan after ${command.name}: ${scanError}`) } } } - - // Execute the original command - const result = await originFunction(...args) - - return result - } catch (error) { + // A failed scan setup must not stop the command: it still runs below this.logger.error(`Error in commandWrapper for ${command.name}: ${error}`) - // Still execute the original command even if accessibility scan fails - return await originFunction(...args) } + + // Execute the original command outside the try: its errors belong to the caller (for + // example a WebdriverIO 10 StrictSelectorError), and must not run the command again + return await originFunction(...args) } async onBeforeTest(args: Record) { @@ -565,11 +575,12 @@ export default class AccessibilityModule extends BaseModule { BStackLogger.debug(util.format(results as string)) return (results as Record | undefined) } - const results = await (browser as WebdriverIO.Browser).executeAsync( + // WebdriverIO v10 removed executeAsync; the helper runs the callback-style script through execute + return await executeAccessibilityScript | undefined>( + browser, this.scriptInstance.performScan as string, { 'method': commandName || '' } ) - return (results as Record | undefined) } catch (err: unknown) { this.logger.error('Accessibility Scan could not be performed : ' + err) return @@ -598,7 +609,7 @@ export default class AccessibilityModule extends BaseModule { } await PerformanceTester.measureWrapper(PERFORMANCE_SDK_EVENTS.A11Y_EVENTS.SAVE_RESULTS, async () => { - const results: unknown = await (browser as WebdriverIO.Browser).executeAsync(accessibilityScripts.saveTestResults as string, dataForExtension) + const results: unknown = await executeAccessibilityScript(browser, accessibilityScripts.saveTestResults as string, dataForExtension) this.logger.debug(`save results : ${util.format(results as string)}`) })() } catch (error) { @@ -617,7 +628,7 @@ export default class AccessibilityModule extends BaseModule { } this.logger.debug('Performing scan before getting results') await this.performScanCli(browser) - const results: Array> = await (browser as WebdriverIO.Browser).executeAsync(this.scriptInstance.getResults as string) + const results = await executeAccessibilityScript>>(browser, this.scriptInstance.getResults as string) return results } catch (error: unknown) { this.logger.error('No accessibility results were found.') @@ -639,7 +650,7 @@ export default class AccessibilityModule extends BaseModule { } this.logger.debug('Performing scan before getting results summary') await this.performScanCli(browser) - const summaryResults: Record = await (browser as WebdriverIO.Browser).executeAsync(this.scriptInstance.getResultsSummary as string) + const summaryResults = await executeAccessibilityScript>(browser, this.scriptInstance.getResultsSummary as string) return summaryResults } catch { this.logger.error('No accessibility summary was found.') diff --git a/packages/browserstack-service/src/cli/skipReporter.ts b/packages/browserstack-service/src/cli/skipReporter.ts index 4fff79d5..e57af31d 100644 --- a/packages/browserstack-service/src/cli/skipReporter.ts +++ b/packages/browserstack-service/src/cli/skipReporter.ts @@ -118,13 +118,13 @@ export function reportSkippedTest( identifier: string, test: Frameworks.Test, suiteTitle?: string, - options?: { immediate?: boolean } + options?: { immediate?: boolean, result?: Frameworks.TestResult } ): Promise { if (startedTests.has(identifier) || reportedSkips.has(identifier)) { return reportChain } reportedSkips.add(identifier) - const result = { passed: false, skipped: true } as Frameworks.TestResult + const result = options?.result ?? { passed: false, skipped: true } as Frameworks.TestResult const queued: QueuedSkip = { framework, test, result, suiteTitle } // SDK-7493: only the DETACHED caller needs deferring. `immediate` is for callers wdio @@ -163,6 +163,19 @@ export function reportSkippedTest( * belong to the hook/test being reported, so they must not slide to end-of-run. */ export async function reportSuiteSkipped(framework: TestFramework, suite: { tests?: unknown[], suites?: unknown[] }): Promise { + await reportSuiteResult(framework, suite) +} + +/** + * Mocha 12 (WebdriverIO 10) fails the tests that a failed BEFORE_ALL/BEFORE_EACH hook skipped + * (`failHookAffectedTests`). Report each state-undefined test as failed with that error, so the + * dashboard shows the status that WebdriverIO reports. Same walk and timing as reportSuiteSkipped. + */ +export async function reportSuiteFailed(framework: TestFramework, suite: { tests?: unknown[], suites?: unknown[] }, error: Error): Promise { + await reportSuiteResult(framework, suite, { passed: false, error, duration: 0, retries: { attempts: 0, limit: 0 } } as Frameworks.TestResult) +} + +async function reportSuiteResult(framework: TestFramework, suite: { tests?: unknown[], suites?: unknown[] }, result?: Frameworks.TestResult): Promise { for (const t of (suite.tests || []) as MochaRuntimeTest[]) { if (t.state !== undefined) { continue @@ -179,10 +192,10 @@ export async function reportSuiteSkipped(framework: TestFramework, suite: { test file: t.file, ctx: { test: { parent: t.parent } } } as unknown as Frameworks.Test - await reportSkippedTest(framework, identifier, synthetic, parentTitle, { immediate: true }) + await reportSkippedTest(framework, identifier, synthetic, parentTitle, { immediate: true, result }) } for (const sub of (suite.suites || []) as { tests?: unknown[], suites?: unknown[] }[]) { - await reportSuiteSkipped(framework, sub) + await reportSuiteResult(framework, sub, result) } } diff --git a/packages/browserstack-service/src/constants.ts b/packages/browserstack-service/src/constants.ts index fedba7b6..bd019239 100644 --- a/packages/browserstack-service/src/constants.ts +++ b/packages/browserstack-service/src/constants.ts @@ -42,6 +42,10 @@ export const DEFAULT_WAIT_TIMEOUT_FOR_PENDING_UPLOADS = 5000 // 5s export const DEFAULT_WAIT_INTERVAL_FOR_PENDING_UPLOADS = 100 // 100ms export const BSTACK_SERVICE_VERSION = bstackServiceVersion +/* WebdriverIO 10 supports Appium 3 only, and App Automate uses Appium 1.22.0 by default. + * 3.3.0 is the newest 3.x version that BrowserStack offers for iOS 15 (3.5.2 needs iOS 16+). */ +export const DEFAULT_APPIUM_3_VERSION = '3.3.0' + export const NOT_ALLOWED_KEYS_IN_CAPS = ['includeTagsInTestingScope', 'excludeTagsInTestingScope', 'testManagementOptions', 'skipAppOverride', 'preferScenarioName'] export const BROWSERSTACK_TEST_PLAN_ID = 'BROWSERSTACK_TEST_PLAN_ID' diff --git a/packages/browserstack-service/src/custom-tags-handler.ts b/packages/browserstack-service/src/custom-tags-handler.ts index 68fe0ab1..b3f839ad 100644 --- a/packages/browserstack-service/src/custom-tags-handler.ts +++ b/packages/browserstack-service/src/custom-tags-handler.ts @@ -2,7 +2,7 @@ import InsightsHandler from './insights-handler.js' import { CustomTagAccumulator } from './customTags.js' import type { CustomMetadata } from './customTags.js' -import { o11yClassErrorHandler } from './util.js' +import { isMultiRemoteBrowser, o11yClassErrorHandler } from './util.js' import { BStackLogger } from './bstackLogger.js' /** @@ -61,7 +61,7 @@ class _CustomTagsHandler { // Fan out to every MultiRemote instance (mirror service._executeCommand), // plus the top-level browser object the user calls directly. register(this._browser) - if ((this._browser as WebdriverIO.Browser).isMultiremote) { + if (isMultiRemoteBrowser(this._browser)) { const multiRemoteBrowser = this._browser as unknown as WebdriverIO.MultiRemoteBrowser Object.keys(this._caps).forEach((browserName) => { try { diff --git a/packages/browserstack-service/src/insights-handler.ts b/packages/browserstack-service/src/insights-handler.ts index 3542882b..6ce76ff8 100644 --- a/packages/browserstack-service/src/insights-handler.ts +++ b/packages/browserstack-service/src/insights-handler.ts @@ -13,7 +13,7 @@ import { frameworkSupportsHook, getCloudProvider, getFailureObject, getGitMetaData, - getHookType, getPlatformVersion, + getHookType, getPlatformVersion, createHookAffectedTestError, getResolvedDeviceName, getScenarioExamples, getTestTags, @@ -341,7 +341,11 @@ class _InsightsHandler { this.listener.hookStarted(this.getRunData(test, 'HookRunStarted')) } - async afterHook (test: Frameworks.Test|CucumberHook|undefined, result: Frameworks.TestResult) { + /** + * @param failHookAffectedTests true when Mocha fails the tests that a failed before/beforeEach + * hook skipped (Mocha 12, WebdriverIO 10). See mochaFailsHookAffectedTests(). + */ + async afterHook (test: Frameworks.Test|CucumberHook|undefined, result: Frameworks.TestResult, failHookAffectedTests = false) { if (!frameworkSupportsHook('after', this._framework)) { return } @@ -378,8 +382,12 @@ class _InsightsHandler { If any of the `beforeAll`, `beforeEach`, `afterEach` then the tests after the hook won't run in mocha (https://github.com/mochajs/mocha/issues/4392) So if any of this hook fails, then we are sending the next tests in the suite as skipped. This won't be needed for `afterAll`, as even if `afterAll` fails all the tests that we need are already run by then, so we don't need to send the stats for them separately + Mocha 12 (WebdriverIO 10) fails the tests that a failed `beforeAll` or `beforeEach` skipped, so they are sent as failed with the hook error. */ if (!result.passed && (hookType === 'BEFORE_EACH' || hookType === 'BEFORE_ALL' || hookType === 'AFTER_EACH')) { + const affectedTestResult = failHookAffectedTests && hookType !== 'AFTER_EACH' + ? { passed: false, error: createHookAffectedTestError(test.title, result.error), duration: 0, retries: { attempts: 0, limit: 0 } } as Frameworks.TestResult + : undefined // eslint-disable-next-line @typescript-eslint/no-explicit-any const sendTestSkip = async (skippedTest: any) => { @@ -391,7 +399,9 @@ class _InsightsHandler { startedAt: (new Date()).toISOString(), finishedAt: (new Date()).toISOString() } - this.listener.testFinished(this.getRunData(skippedTest, 'TestRunSkipped')) + this.listener.testFinished(affectedTestResult + ? this.getRunData(skippedTest, 'TestRunFinished', affectedTestResult) + : this.getRunData(skippedTest, 'TestRunSkipped')) } } diff --git a/packages/browserstack-service/src/launcher.ts b/packages/browserstack-service/src/launcher.ts index d9c95f02..0fdbd797 100644 --- a/packages/browserstack-service/src/launcher.ts +++ b/packages/browserstack-service/src/launcher.ts @@ -26,7 +26,8 @@ import { BROWSERSTACK_OBSERVABILITY, WDIO_NAMING_PREFIX, BROWSERSTACK_TEST_REPORTING, - TEST_REPORTING_PROJECT_NAME + TEST_REPORTING_PROJECT_NAME, + DEFAULT_APPIUM_3_VERSION } from './constants.js' import { launchTestSession, @@ -50,7 +51,9 @@ import { isValidEnabledValue, isMultiRemoteCaps, coerceStringBooleans, - validateSkipAppOverride + validateSkipAppOverride, + getWdioMajorVersion, + setDefaultAppiumVersion } from './util.js' import CrashReporter from './crash-reporter.js' import { initWdioConfigPath, isAutoCaptureLogsDisabled, publishAutoCaptureDisabled } from './configCapture.js' @@ -182,7 +185,7 @@ export default class BrowserstackLauncherService implements Services.ServiceInst } }) } else if (typeof capabilities === 'object') { - Object.entries(capabilities as Capabilities.RequestedMultiremoteCapabilities).forEach(([, caps]) => { + Object.entries(capabilities as Capabilities.RequestedMultiRemoteCapabilities).forEach(([, caps]) => { if (!(caps.capabilities as WebdriverIO.Capabilities)['bstack:options']) { if (isBStackSession(this._config)) { const extensionCaps = Object.keys(caps.capabilities).filter((cap) => cap.includes(':')) @@ -427,6 +430,8 @@ export default class BrowserstackLauncherService implements Services.ServiceInst } } + this._setDefaultAppiumVersion(capabilities as Capabilities.TestrunnerCapabilities) + /** * buildIdentifier in service options will take precedence over specified in capabilities */ @@ -916,12 +921,43 @@ export default class BrowserstackLauncherService implements Services.ServiceInst }) .forEach(strip) } else { - Object.entries(capabilities as Capabilities.RequestedMultiremoteCapabilities).forEach(([, caps]) => { + Object.entries(capabilities as Capabilities.RequestedMultiRemoteCapabilities).forEach(([, caps]) => { strip(caps.capabilities as WebdriverIO.Capabilities) }) } } + /** + * WebdriverIO 10 supports Appium 3 only, but App Automate uses Appium 1.22.0 when no version + * is set. Set a default Appium 3 version for App Automate sessions on WebdriverIO 10. + */ + _setDefaultAppiumVersion(capabilities: Capabilities.TestrunnerCapabilities) { + if (!this.browserStackConfig.appAutomate) { + return + } + const wdioMajor = getWdioMajorVersion() + if (!wdioMajor || wdioMajor < 10) { + return + } + + const capabilityList = Array.isArray(capabilities) + ? capabilities.flatMap((c) => { + if ('alwaysMatch' in c) { + return c.alwaysMatch as WebdriverIO.Capabilities + } + if (Object.values(c).length > 0 && Object.values(c).every(c => typeof c === 'object' && c.capabilities)) { + return Object.values(c).map((o) => o.capabilities) as WebdriverIO.Capabilities[] + } + return c as WebdriverIO.Capabilities + }) + : Object.values(capabilities as Capabilities.RequestedMultiRemoteCapabilities).map((o) => o.capabilities as WebdriverIO.Capabilities) + + const updated = capabilityList.filter((capability) => setDefaultAppiumVersion(capability)).length + if (updated) { + BStackLogger.info(`WebdriverIO ${wdioMajor} needs Appium 3. Set appiumVersion to ${DEFAULT_APPIUM_3_VERSION} in ${updated} capabilities. Set bstack:options.appiumVersion to use a different version.`) + } + } + _updateObjectTypeCaps(capabilities?: Capabilities.TestrunnerCapabilities | WebdriverIO.Capabilities, capType?: string, value?: { [key: string]: unknown }) { try { if (Array.isArray(capabilities)) { @@ -983,7 +1019,7 @@ export default class BrowserstackLauncherService implements Services.ServiceInst } }) } else if (typeof capabilities === 'object') { - Object.entries(capabilities as Capabilities.RequestedMultiremoteCapabilities).forEach(([, caps]) => { + Object.entries(capabilities as Capabilities.RequestedMultiRemoteCapabilities).forEach(([, caps]) => { if ( validateCapsWithNonBstackA11y( (caps.capabilities as WebdriverIO.Capabilities).browserName, @@ -1108,7 +1144,7 @@ export default class BrowserstackLauncherService implements Services.ServiceInst } }) } else if (typeof capabilities === 'object') { - Object.entries(capabilities as Capabilities.RequestedMultiremoteCapabilities).forEach(([, caps]) => { + Object.entries(capabilities as Capabilities.RequestedMultiRemoteCapabilities).forEach(([, caps]) => { if (!(caps.capabilities as WebdriverIO.Capabilities)['bstack:options']) { const extensionCaps = Object.keys(caps.capabilities).filter((cap) => cap.includes(':')) if (extensionCaps.length) { diff --git a/packages/browserstack-service/src/service.ts b/packages/browserstack-service/src/service.ts index 8854e15c..7761053a 100644 --- a/packages/browserstack-service/src/service.ts +++ b/packages/browserstack-service/src/service.ts @@ -6,12 +6,17 @@ import { isBrowserstackCapability, getParentSuiteName, isBrowserstackSession, + isMultiRemoteBrowser, patchConsoleLogs, isTrue, isFalse, getUniqueIdentifier, getHookType, - isBrowserstackExecutorScript + isBrowserstackExecutorScript, + getWdioMajorVersion, + mochaFailsHookAffectedTests, + createHookAffectedTestError, + overwriteBrowsingContextCommand } from './util.js' import type { BrowserstackConfig, BrowserstackOptions, MultiRemoteAction } from './types.js' import type { Pickle, Feature, ITestCaseHookParameter, CucumberHook } from './cucumber-types.js' @@ -35,7 +40,7 @@ import PerformanceTester from './instrumentation/performance/performance-tester. import * as PERFORMANCE_SDK_EVENTS from './instrumentation/performance/constants.js' import { EVENTS } from './instrumentation/performance/constants.js' import { BrowserstackCLI } from './cli/index.js' -import { drainSkipReports, markTestStarted, reportSuiteSkipped } from './cli/skipReporter.js' +import { drainSkipReports, markTestStarted, reportSuiteFailed, reportSuiteSkipped } from './cli/skipReporter.js' import { CLIUtils } from './cli/cliUtils.js' import { _fetch as fetch } from './fetchWrapper.js' @@ -56,6 +61,7 @@ export default class BrowserstackService implements Services.ServiceInstance { private _sessionBaseUrl = 'https://api.browserstack.com/automate/sessions' private _failReasons: string[] = [] private _hookFailReasons: string[] = [] + private _failHookAffectedTests?: boolean private _pureTestFailReasons: string[] = [] private _scenariosThatRan: string[] = [] private _lastScenarioName?: string // Track last scenario for preferScenarioName feature @@ -144,7 +150,7 @@ export default class BrowserstackService implements Services.ServiceInstance { } _updateCaps (fn: (caps: WebdriverIO.Capabilities) => void) { - const multiRemoteCap = this._caps as Capabilities.RequestedMultiremoteCapabilities + const multiRemoteCap = this._caps as Capabilities.RequestedMultiRemoteCapabilities if (multiRemoteCap.capabilities) { return Object.entries(multiRemoteCap).forEach(([, caps]) => fn(caps.capabilities as WebdriverIO.Capabilities)) @@ -268,7 +274,7 @@ export default class BrowserstackService implements Services.ServiceInstance { } } - if (this._browser.isMultiremote) { + if (isMultiRemoteBrowser(this._browser)) { const multiRemoteBrowser = this._browser as unknown as WebdriverIO.MultiRemoteBrowser Object.keys(this._caps).forEach((browserName) => { patchBidiExecutorRouting(() => multiRemoteBrowser.getInstance(browserName), browserName) @@ -498,6 +504,15 @@ export default class BrowserstackService implements Services.ServiceInstance { await this._accessibilityHandler?.beforeHook(test as Frameworks.Test, context, this._insightsHandler?.getCurrentHook()?.uuid) } + /** + * true when Mocha fails the tests that a failed before/beforeEach hook skipped + * (Mocha 12, WebdriverIO 10). Read once: the WebdriverIO version comes from the file system. + */ + private get failHookAffectedTests(): boolean { + this._failHookAffectedTests ??= mochaFailsHookAffectedTests(this._config, getWdioMajorVersion()) + return this._failHookAffectedTests + } + @PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_HOOK, { hookType: 'afterHook' }) async afterHook(test: Frameworks.Test | CucumberHook, context: unknown, result: Frameworks.TestResult) { // The Mocha hook window is closed — clear the tracker (see beforeHook). @@ -548,13 +563,16 @@ export default class BrowserstackService implements Services.ServiceInstance { const hookType = getHookType((test as Frameworks.Test).title) const suite = (test as Frameworks.Test).ctx?.test?.parent if (result && !result.passed && ['BEFORE_ALL', 'BEFORE_EACH', 'AFTER_EACH'].includes(hookType) && suite) { - await reportSuiteSkipped(framework, suite) + // Mocha 12 (WebdriverIO 10) fails these tests after a before/beforeEach hook + await (hookType !== 'AFTER_EACH' && this.failHookAffectedTests + ? reportSuiteFailed(framework, suite, createHookAffectedTestError((test as Frameworks.Test).title, result.error)) + : reportSuiteSkipped(framework, suite)) } } return } - await this._insightsHandler?.afterHook(test, result) + await this._insightsHandler?.afterHook(test, result, this.failHookAffectedTests) await this._accessibilityHandler?.afterHook() } @@ -1185,7 +1203,7 @@ export default class BrowserstackService implements Services.ServiceInstance { failureReason = hasReasons ? this._failReasons.join('\n') : undefined } - if (!this._browser.isMultiremote) { + if (!isMultiRemoteBrowser(this._browser)) { BStackLogger.info(`Update (reloaded) job with sessionId ${oldSessionId}, ${sessionStatus}`) } else { const browserName = (this._browser as unknown as WebdriverIO.MultiRemoteBrowser).instances.filter( @@ -1254,7 +1272,22 @@ export default class BrowserstackService implements Services.ServiceInstance { return originalExecute(script, ...args) }) - browser.overwriteCommand('executeAsync', async (originalExecuteAsync, script, ...args) => { + // WebdriverIO v10: `execute` on a browsing context (from browser.url() or newWindow()) does not + // go through the browser overwrite. Executor commands act on the session, not on one context. + overwriteBrowsingContextCommand(browser, 'execute', async (originalExecute: (...args: unknown[]) => unknown, script: string, ...args: Parameters[1]) => { + if (isBrowserstackExecutorScript(script)) { + return browser.executeScript(script, args) + } + return originalExecute(script, ...args) + }) + + // WebdriverIO v10 removed executeAsync; only v9 has the command to overwrite. + if (typeof (browser as { executeAsync?: unknown }).executeAsync !== 'function') { + return + } + + // @ts-expect-error executeAsync is in the WebdriverIO v9 types only + browser.overwriteCommand('executeAsync', async (originalExecuteAsync: (...args: unknown[]) => unknown, script: string, ...args: Parameters[1]) => { if (isBrowserstackExecutorScript(script)) { return browser.executeAsyncScript(script, args) } @@ -1267,7 +1300,7 @@ export default class BrowserstackService implements Services.ServiceInstance { return Promise.resolve() } - if (!this._browser.isMultiremote) { + if (!isMultiRemoteBrowser(this._browser)) { return action(this._browser.sessionId) } @@ -1400,7 +1433,7 @@ export default class BrowserstackService implements Services.ServiceInstance { const cmd = { action, ...(args ? { arguments: args } : {}) } const script = `browserstack_executor: ${JSON.stringify(cmd)}` - if (this._browser.isMultiremote) { + if (isMultiRemoteBrowser(this._browser)) { const multiRemoteBrowser = this._browser as unknown as WebdriverIO.MultiRemoteBrowser return Promise.all(Object.keys(this._caps).map(async (browserName) => { const browser = multiRemoteBrowser.getInstance(browserName) diff --git a/packages/browserstack-service/src/util.ts b/packages/browserstack-service/src/util.ts index 5aa1e9bb..c502492d 100644 --- a/packages/browserstack-service/src/util.ts +++ b/packages/browserstack-service/src/util.ts @@ -5,6 +5,7 @@ import zlib from 'node:zlib' import { format, promisify } from 'node:util' import path from 'node:path' import util from 'node:util' +import { createRequire } from 'node:module' import type { Capabilities, Frameworks, Options } from '@wdio/types' import type { BeforeCommandArgs, AfterCommandArgs } from '@wdio/reporter' @@ -48,7 +49,8 @@ import { STOP_BUILD_MAX_ATTEMPTS, STOP_BUILD_ATTEMPT_TIMEOUT_MS, STOP_BUILD_TOTAL_BUDGET_MS, - STOP_BUILD_BACKOFF_BASE_MS + STOP_BUILD_BACKOFF_BASE_MS, + DEFAULT_APPIUM_3_VERSION } from './constants.js' import CrashReporter from './crash-reporter.js' import { BStackLogger } from './bstackLogger.js' @@ -119,6 +121,125 @@ export function getBrowserDescription(cap: WebdriverIO.Capabilities) { .join(' ') } +/** + * check for a multiremote browser. WebdriverIO v10 renamed `isMultiremote` to `isMultiRemote`, + * and the service supports both v9 and v10. + * @param browser browser object + */ +export function isMultiRemoteBrowser(browser: unknown): browser is WebdriverIO.MultiRemoteBrowser { + const flags = browser as { isMultiRemote?: boolean, isMultiremote?: boolean } | undefined + return Boolean(flags?.isMultiRemote || flags?.isMultiremote) +} + +/** + * third argument for `overwriteCommand` that works in WebdriverIO v9 and v10. v10 throws on a + * boolean, and v9 attaches the command to elements for any truthy value. + * @param attachToElement true to overwrite an element command + */ +export function commandScopeOptions(attachToElement: boolean): { attachToElement: true } | undefined { + return attachToElement ? { attachToElement: true } : undefined +} + +/** + * overwrite a command of every WebdriverIO 10 browsing context. `browser.url()` and + * `browser.newWindow()` return a browsing context with its own commands (`execute`, `refresh`, ...), + * which a browser-level `overwriteCommand` does not reach. WebdriverIO 9 has no browsing contexts + * and reads any third argument as "attach to elements", so this does nothing there. + * @param browser browser object + * @param name command name + * @param fn overwrite function, called with the context as `this` + * @returns true if the command was overwritten, false on WebdriverIO 9 or for a command that a browsing context does not have + */ +export function overwriteBrowsingContextCommand(browser: WebdriverIO.Browser, name: string, fn: Function): boolean { + if (typeof (browser as { browsingContexts?: unknown }).browsingContexts !== 'function') { + return false + } + try { + // @ts-expect-error attachToBrowsingContext is in the WebdriverIO v10 types only + browser.overwriteCommand(name, fn, { attachToBrowsingContext: true }) + return true + } catch { + return false + } +} + +/** + * major version of the `@wdio/cli` that runs the tests. `@wdio/cli` is a peer dependency, so it + * resolves to the user's copy. `webdriverio` does not: `@percy/webdriverio` can install a second, + * older copy next to the service. + * @param from file or file URL to resolve `@wdio/cli` from + * @returns the major version, or undefined if `@wdio/cli` is not found + */ +export function getWdioMajorVersion(from: string = import.meta.url): number | undefined { + try { + // `@wdio/cli` does not export its package.json, so go up from its entry point + let dir = path.dirname(createRequire(from).resolve('@wdio/cli')) + while (dir !== path.dirname(dir)) { + const manifestPath = path.join(dir, 'package.json') + if (fs.existsSync(manifestPath)) { + const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) + if (manifest.name === '@wdio/cli') { + const major = parseInt(manifest.version, 10) + return Number.isNaN(major) ? undefined : major + } + } + dir = path.dirname(dir) + } + } catch (err) { + BStackLogger.debug(`Cannot find the @wdio/cli version: ${err}`) + } + return undefined +} + +/** + * set `bstack:options.appiumVersion` (or `browserstack.appium_version` for legacy capabilities) to the + * default Appium 3 version, unless the user set a version + * @param capability capability to update + * @returns true if the version was set + */ +export function setDefaultAppiumVersion(capability: WebdriverIO.Capabilities): boolean { + const legacyVersion = (capability as Record)['browserstack.appium_version'] + if (capability['bstack:options']?.appiumVersion || legacyVersion) { + return false + } + // WebdriverIO rejects a capability that mixes extension keys (`x:y`) with legacy keys, so a + // capability without extension keys gets the legacy key, as in launcher._updateCaps + const hasExtensionCaps = Object.keys(capability).some((cap) => cap.includes(':')) + if (!hasExtensionCaps) { + (capability as Record)['browserstack.appium_version'] = DEFAULT_APPIUM_3_VERSION + return true + } + capability['bstack:options'] = { ...capability['bstack:options'], appiumVersion: DEFAULT_APPIUM_3_VERSION } + return true +} + +/** + * Mocha 12 (WebdriverIO 10) fails the tests that a failed `before` or `beforeEach` hook skipped, + * unless `mochaOpts.failHookAffectedTests` is false. The Mocha of WebdriverIO 9 has no such option. + * @param config testrunner config + * @param wdioMajor major version of WebdriverIO, from getWdioMajorVersion() + */ +export function mochaFailsHookAffectedTests(config: Options.Testrunner | undefined, wdioMajor: number | undefined): boolean { + if (config?.framework !== 'mocha' || !wdioMajor || wdioMajor < 10) { + return false + } + const mochaOpts = (config as { mochaOpts?: { failHookAffectedTests?: boolean } }).mochaOpts + return mochaOpts?.failHookAffectedTests !== false +} + +/** + * the error that Mocha 12 gives a test that a failed hook skipped + * @param hookTitle title of the failed hook + * @param hookError error of the failed hook + */ +export function createHookAffectedTestError(hookTitle: string, hookError?: Error): Error { + const error = new Error(`Test skipped due to failure in hook "${hookTitle}": ${hookError?.message || 'Hook failed'}`) + if (hookError?.stack) { + error.stack = hookError.stack + } + return error +} + /** * get correct browser capabilities object in both multiremote and normal setups * @param browser browser object @@ -126,11 +247,11 @@ export function getBrowserDescription(cap: WebdriverIO.Capabilities) { * @param browserName browser name in case of multiremote */ export function getBrowserCapabilities(browser: WebdriverIO.Browser | WebdriverIO.MultiRemoteBrowser, caps?: Capabilities.ResolvedTestrunnerCapabilities, browserName?: string) { - if (!browser.isMultiremote) { + if (!isMultiRemoteBrowser(browser)) { return { ...browser.capabilities, ...caps } as WebdriverIO.Capabilities } - const multiCaps = caps as Capabilities.RequestedMultiremoteCapabilities + const multiCaps = caps as Capabilities.RequestedMultiRemoteCapabilities const globalCap = browserName && browser.getInstance(browserName) ? browser.getInstance(browserName).capabilities : {} const cap = browserName && multiCaps[browserName] ? multiCaps[browserName].capabilities : {} return { ...globalCap, ...cap } as WebdriverIO.Capabilities @@ -1154,10 +1275,11 @@ export function getCloudProvider(browser: WebdriverIO.Browser | WebdriverIO.Mult // `isLoadTestingSession() ? 'browserstack' : getCloudProvider(browser)` // (see reporter.ts, insights-handler.ts). if (browser && 'instances' in browser) { - // Loop through all instances + // Loop through all instances. WebdriverIO v10 has no named instance properties; + // getInstance works in v9 and v10 for (const instanceName of browser.instances) { - const instance = (browser as any)[instanceName] as WebdriverIO.Browser - if (instance.options && instance.options.hostname && instance.options.hostname.includes('browserstack')) { + const instance = browser.getInstance(instanceName) + if (instance?.options?.hostname?.includes('browserstack')) { return 'browserstack' } } diff --git a/packages/browserstack-service/tests/accessibility-handler.test.ts b/packages/browserstack-service/tests/accessibility-handler.test.ts index 8f2f6925..2affba81 100644 --- a/packages/browserstack-service/tests/accessibility-handler.test.ts +++ b/packages/browserstack-service/tests/accessibility-handler.test.ts @@ -9,6 +9,7 @@ import AccessibilityHandler from '../src/accessibility-handler.js' import type { BrowserstackConfig, BrowserstackOptions } from '../src/types.js' import type { Options } from '@wdio/types' import * as utils from '../src/util.js' +import accessibilityScripts from '../src/scripts/accessibility-scripts.js' import type { Capabilities } from '@wdio/types' import * as bstackLogger from '../src/bstackLogger.js' @@ -53,14 +54,20 @@ describe('shouldSkipScanForBidiWindowCommand (SDK-5047)', () => { expect(skip({ isBidi: true }, {})).toBe(false) }) + // WebdriverIO v10 multiremote: no instance properties, only getInstance() + const multiRemote = (children: Record) => ({ + instances: Object.keys(children), + getInstance: (name: string) => children[name] + }) + it('skips for window commands when any multiremote child instance is BiDi', () => { - const multi = { instances: ['chromeA', 'chromeB'], chromeA: { isBidi: false }, chromeB: { isBidi: true } } + const multi = multiRemote({ chromeA: { isBidi: false }, chromeB: { isBidi: true } }) expect(skip(multi, { name: 'getWindowHandle', class: 'Browser' })).toBe(true) expect(skip(multi, { name: 'switchToWindow', class: 'Browser' })).toBe(true) }) it('does not skip on multiremote when no child instance is BiDi', () => { - const multi = { instances: ['chromeA', 'chromeB'], chromeA: { isBidi: false }, chromeB: {} } + const multi = multiRemote({ chromeA: { isBidi: false }, chromeB: {} }) expect(skip(multi, { name: 'getWindowHandle', class: 'Browser' })).toBe(false) }) }) @@ -483,6 +490,83 @@ describe('scans ahead of the first test (config-level hooks)', () => { expect(AccessibilityHandler['_a11yScanSessionMap']['session-multi']).toBeUndefined() }) + it('skips multiremote with the WebdriverIO v10 flag', async () => { + const handler = handlerFor('mocha') + handler['_browser'] = { ...browser, isMultiremote: false, isMultiRemote: true } as any + + await handler.before('session-multi-v10') + + expect(AccessibilityHandler['_a11yScanSessionMap']['session-multi-v10']).toBeUndefined() + }) + + it('wraps element commands with an options object and browser commands without one', async () => { + const handler = handlerFor('mocha') + const overwriteCommand = vi.fn() + handler['_browser'] = { ...browser, overwriteCommand } as any + const savedCommands = accessibilityScripts.commandsToWrap + accessibilityScripts.commandsToWrap = [{ name: 'click', class: 'Element' }, { name: 'url', class: 'Browser' }] as any + + try { + await handler.before('session-wrap') + } finally { + accessibilityScripts.commandsToWrap = savedCommands + } + + expect(overwriteCommand).toHaveBeenCalledWith('click', expect.any(Function), { attachToElement: true }) + expect(overwriteCommand).toHaveBeenCalledWith('url', expect.any(Function), undefined) + }) + + // WebdriverIO 10: browser.url() and browser.newWindow() return a browsing context with its own + // commands, which a browser-level overwriteCommand does not reach + it('also wraps browser commands on WebdriverIO 10 browsing contexts, and scans that context', async () => { + const handler = handlerFor('mocha') + const overwriteCommand = vi.fn() + handler['_browser'] = { ...browser, overwriteCommand, browsingContexts: vi.fn() } as any + handler['_sessionId'] = 'session-context' + const savedCommands = accessibilityScripts.commandsToWrap + accessibilityScripts.commandsToWrap = [{ name: 'click', class: 'Element' }, { name: 'refresh', class: 'Browser' }] as any + + try { + await handler.before('session-context') + } finally { + accessibilityScripts.commandsToWrap = savedCommands + } + + expect(overwriteCommand).toHaveBeenCalledWith('refresh', expect.any(Function), { attachToBrowsingContext: true }) + expect(overwriteCommand).not.toHaveBeenCalledWith('click', expect.any(Function), { attachToBrowsingContext: true }) + + const contextOverwrite = overwriteCommand.mock.calls + .find(([name, , options]) => name === 'refresh' && options?.attachToBrowsingContext)?.[1] as Function + vi.spyOn(utils, 'shouldScanTestForAccessibility').mockReturnValue(true) + const scanSpy = vi.spyOn(utils, 'performA11yScan').mockResolvedValue(undefined) + const context = { contextId: 'tab-2', execute: vi.fn() } + const original = vi.fn().mockResolvedValue('refreshed') + + await expect(contextOverwrite.call(context, original, 'arg')).resolves.toBe('refreshed') + + expect(original).toHaveBeenCalledTimes(1) + expect(original).toHaveBeenCalledWith('arg') + // performA11yScan(isAppAutomate, browser, ...): the scan runs on the context, not on the browser + expect(scanSpy.mock.calls.at(-1)?.[1]).toBe(context) + }) + + it('does not wrap browsing context commands on WebdriverIO 9', async () => { + const handler = handlerFor('mocha') + const overwriteCommand = vi.fn() + handler['_browser'] = { ...browser, overwriteCommand } as any + const savedCommands = accessibilityScripts.commandsToWrap + accessibilityScripts.commandsToWrap = [{ name: 'refresh', class: 'Browser' }] as any + + try { + await handler.before('session-v9') + } finally { + accessibilityScripts.commandsToWrap = savedCommands + } + + expect(overwriteCommand).toHaveBeenCalledTimes(1) + expect(overwriteCommand).toHaveBeenCalledWith('refresh', expect.any(Function), undefined) + }) + // Stateless rule: parentless only when neither a framework hook run nor a test can own it. it('sends no test run uuid when no hook run and no test can own the scan', async () => { const handler = handlerFor('mocha') diff --git a/packages/browserstack-service/tests/ai-handler.test.ts b/packages/browserstack-service/tests/ai-handler.test.ts index 48b2140c..96ee0850 100644 --- a/packages/browserstack-service/tests/ai-handler.test.ts +++ b/packages/browserstack-service/tests/ai-handler.test.ts @@ -651,7 +651,11 @@ describe('AiHandler', () => { } } + // WebdriverIO v9 multiremote browser: named instance properties and getInstance browserMock = { + isMultiremote: true, + instances: ['myChromeBrowser', 'myFirefoxBrowser'], + getInstance: (name: string) => browserMock[name], myChromeBrowser: { sessionId: 'chrome-session-id', capabilities: { browserName: 'chrome' }, @@ -705,6 +709,30 @@ describe('AiHandler', () => { expect(installFirefoxExtensionSpy).toHaveBeenCalledWith(browserMock.myFirefoxBrowser) }) + it('should call handleSelfHeal for each instance of a WebdriverIO v10 multiremote browser', async () => { + AiHandler['authResult'] = { + isAuthenticated: true, + sessionToken: 'mock-session-token', + defaultLogDataEnabled: true, + isHealingEnabled: true + } as any + vi.spyOn(AiHandler, 'setToken') + vi.spyOn(AiHandler, 'installFirefoxExtension') + const handleSelfHealSpy = vi.spyOn(AiHandler, 'handleSelfHeal') + // v10 has no named instance properties on the multiremote browser + const v10Browser = { + isMultiRemote: true, + instances: ['myChromeBrowser', 'myFirefoxBrowser'], + getInstance: (name: string) => browserMock[name] + } as any + + await AiHandler.selfHeal(config, caps, v10Browser) + + expect(handleSelfHealSpy).toHaveBeenCalledTimes(2) + expect(handleSelfHealSpy).toHaveBeenCalledWith(config, browserMock.myChromeBrowser) + expect(handleSelfHealSpy).toHaveBeenCalledWith(config, browserMock.myFirefoxBrowser) + }) + it('should skip setup for multiremote session if accessKey is not present', async () => { config.key = '' const authenticateUserSpy = vi.spyOn(AiHandler, 'authenticateUser') diff --git a/packages/browserstack-service/tests/cli/modules/accessibilityModule.test.ts b/packages/browserstack-service/tests/cli/modules/accessibilityModule.test.ts index d4114756..4756df7c 100644 --- a/packages/browserstack-service/tests/cli/modules/accessibilityModule.test.ts +++ b/packages/browserstack-service/tests/cli/modules/accessibilityModule.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest' +import type * as UtilModule from '../../../src/util.js' vi.mock('../../../src/cli/frameworks/testFramework.js', () => ({ default: class MockTestFramework { @@ -28,17 +29,24 @@ vi.mock('../../../src/scripts/accessibility-scripts.js', () => ({ } })) -vi.mock('../../../src/util.js', () => ({ - validateCapsWithA11y: vi.fn().mockReturnValue(true), - validateCapsWithAppA11y: vi.fn().mockReturnValue(true), - shouldScanTestForAccessibility: vi.fn().mockReturnValue(true), - getAppA11yResults: vi.fn().mockResolvedValue([]), - getAppA11yResultsSummary: vi.fn().mockResolvedValue({}), - _getParamsForAppAccessibility: vi.fn().mockReturnValue('{}'), - formatString: vi.fn().mockReturnValue('formatted-script'), - o11yClassErrorHandler: vi.fn().mockImplementation((cls) => cls), - isBrowserstackSession: vi.fn().mockReturnValue(true) -})) +vi.mock('../../../src/util.js', async (importOriginal) => { + const actual = await importOriginal() + return { + // real helpers: the tests check the script and the options that reach the browser + executeAccessibilityScript: actual.executeAccessibilityScript, + commandScopeOptions: actual.commandScopeOptions, + overwriteBrowsingContextCommand: actual.overwriteBrowsingContextCommand, + validateCapsWithA11y: vi.fn().mockReturnValue(true), + validateCapsWithAppA11y: vi.fn().mockReturnValue(true), + shouldScanTestForAccessibility: vi.fn().mockReturnValue(true), + getAppA11yResults: vi.fn().mockResolvedValue([]), + getAppA11yResultsSummary: vi.fn().mockResolvedValue({}), + _getParamsForAppAccessibility: vi.fn().mockReturnValue('{}'), + formatString: vi.fn().mockReturnValue('formatted-script'), + o11yClassErrorHandler: vi.fn().mockImplementation((cls) => cls), + isBrowserstackSession: vi.fn().mockReturnValue(true) + } +}) vi.mock('../../../src/cli/grpcClient.js', () => ({ GrpcClient: { @@ -79,7 +87,7 @@ describe('AccessibilityModule', () => { mockBrowser = { // a live session: the wrapper skips scanning when the driver has no sessionId sessionId: 'session-w', - executeAsync: vi.fn().mockResolvedValue([]), + // WebdriverIO v10 browser: executeAsync was removed execute: vi.fn().mockResolvedValue({}), overwriteCommand: vi.fn() } @@ -242,6 +250,69 @@ describe('AccessibilityModule', () => { expect(_getParamsForAppAccessibility).not.toHaveBeenCalled() }) + // WebdriverIO 10: browser.url() and browser.newWindow() return a browsing context with + // its own commands, which a browser-level overwriteCommand does not reach + it('also wraps browser commands on WebdriverIO 10 browsing contexts, and scans that context', async () => { + withA11yCaps() + accessibilityModule.isAppAccessibility = true + mockBrowser.browsingContexts = vi.fn() + accessibilityScripts.commandsToWrap = [ + { name: 'click', class: 'Element' }, + { name: 'refresh', class: 'Browser' } + ] + try { + await accessibilityModule.onBeforeExecute() + } finally { + accessibilityScripts.commandsToWrap = [] + } + + expect(mockBrowser.overwriteCommand).toHaveBeenCalledWith('refresh', expect.any(Function), { attachToBrowsingContext: true }) + expect(mockBrowser.overwriteCommand).not.toHaveBeenCalledWith('click', expect.any(Function), { attachToBrowsingContext: true }) + + const contextOverwrite = vi.mocked(mockBrowser.overwriteCommand).mock.calls + .find(([name, , options]: any[]) => name === 'refresh' && options?.attachToBrowsingContext)?.[1] as Function + mockBrowser.execute.mockClear() + const context = { contextId: 'tab-2', execute: vi.fn().mockResolvedValue({}) } + const original = vi.fn().mockResolvedValue('refreshed') + + await expect(contextOverwrite.call(context, original, 'arg')).resolves.toBe('refreshed') + + expect(original).toHaveBeenCalledTimes(1) + // the scan runs on the context, not on the browser + expect(context.execute).toHaveBeenCalled() + expect(mockBrowser.execute).not.toHaveBeenCalled() + }) + + // WebdriverIO 10 throws StrictSelectorError from inside an element command when `$` + // matches more than one element. That error belongs to the test, not to the wrapper. + it('runs a failing command once and gives its error to the caller', async () => { + withA11yCaps() + await accessibilityModule.onBeforeExecute() + const strictError = Object.assign(new Error('strict mode violation: `$("button")` resolved to 2 elements, expected 1.'), { name: 'StrictSelectorError' }) + const orig = vi.fn().mockRejectedValue(strictError) + const errorSpy = vi.spyOn(accessibilityModule.logger, 'error') + + await expect((accessibilityModule as any).commandWrapper({ name: 'click', class: 'Element' }, orig, 'arg')) + .rejects.toBe(strictError) + + expect(orig).toHaveBeenCalledTimes(1) + expect(errorSpy).not.toHaveBeenCalledWith(expect.stringContaining('Error in commandWrapper')) + }) + + it('still runs the command once when the scan setup throws', async () => { + withA11yCaps() + await accessibilityModule.onBeforeExecute() + vi.mocked(AutomationFramework.getTrackedInstance).mockImplementation(() => { + throw new Error('no tracked instance') + }) + const orig = vi.fn().mockResolvedValue('ok') + + await expect((accessibilityModule as any).commandWrapper({ name: 'click', class: 'Element' }, orig, 'arg')) + .resolves.toBe('ok') + + expect(orig).toHaveBeenCalledTimes(1) + }) + it('leaves the gate open after a test, so afterSuite/after still scan', async () => { withA11yCaps() vi.mocked(shouldScanTestForAccessibility).mockReturnValue(true) @@ -427,7 +498,7 @@ describe('AccessibilityModule', () => { await accessibilityModule.onAfterTest() - expect(mockBrowser.executeAsync).not.toHaveBeenCalled() + expect(mockBrowser.execute).not.toHaveBeenCalled() }) it('should return early when accessibility scan was not started', async () => { @@ -438,7 +509,7 @@ describe('AccessibilityModule', () => { await accessibilityModule.onAfterTest() - expect(mockBrowser.executeAsync).not.toHaveBeenCalled() + expect(mockBrowser.execute).not.toHaveBeenCalled() }) }) @@ -450,7 +521,7 @@ describe('AccessibilityModule', () => { expect(result).toBeUndefined() expect(mockBrowser.execute).not.toHaveBeenCalled() - expect(mockBrowser.executeAsync).not.toHaveBeenCalled() + expect(mockBrowser.execute).not.toHaveBeenCalled() }) it('should call execute for app accessibility', async () => { @@ -464,38 +535,37 @@ describe('AccessibilityModule', () => { expect(result).toEqual({ success: true }) }) - it('should call executeAsync for web accessibility', async () => { + it('should run the web scan script through execute', async () => { accessibilityModule.accessibility = true accessibilityModule.isAppAccessibility = false - mockBrowser.executeAsync.mockResolvedValue({ violations: [] }) + mockBrowser.execute.mockResolvedValue({ violations: [] }) const result = await (accessibilityModule as any).performScanCli(mockBrowser) - expect(mockBrowser.executeAsync).toHaveBeenCalled() + expect(mockBrowser.execute).toHaveBeenCalledWith(expect.stringContaining('mock-perform-scan-script')) expect(result).toEqual({ violations: [] }) }) it('should handle errors gracefully', async () => { accessibilityModule.accessibility = true accessibilityModule.isAppAccessibility = false - mockBrowser.executeAsync.mockRejectedValue(new Error('Scan failed')) + mockBrowser.execute.mockRejectedValue(new Error('Scan failed')) const result = await (accessibilityModule as any).performScanCli(mockBrowser) expect(result).toBeUndefined() }) - it('should pass command name to executeAsync for web accessibility', async () => { + it('should pass command name to the web scan script', async () => { accessibilityModule.accessibility = true accessibilityModule.isAppAccessibility = false const commandName = 'click' - mockBrowser.executeAsync.mockResolvedValue({}) + mockBrowser.execute.mockResolvedValue({}) await (accessibilityModule as any).performScanCli(mockBrowser, commandName) - expect(mockBrowser.executeAsync).toHaveBeenCalledWith( - 'mock-perform-scan-script', - { method: commandName } + expect(mockBrowser.execute).toHaveBeenCalledWith( + expect.stringMatching(/mock-perform-scan-script[\s\S]*\{"method":"click"\}/) ) }) }) @@ -507,7 +577,7 @@ describe('AccessibilityModule', () => { const result = await accessibilityModule.getA11yResults(mockBrowser) expect(result).toBeUndefined() - expect(mockBrowser.executeAsync).not.toHaveBeenCalled() + expect(mockBrowser.execute).not.toHaveBeenCalled() }) it('should return accessibility results when accessibility is enabled', async () => { @@ -516,18 +586,18 @@ describe('AccessibilityModule', () => { { id: 'test-1', impact: 'serious', description: 'Test violation' }, { id: 'test-2', impact: 'moderate', description: 'Another violation' } ] - mockBrowser.executeAsync.mockResolvedValue(mockResults) + mockBrowser.execute.mockResolvedValue(mockResults) const result = await accessibilityModule.getA11yResults(mockBrowser) - expect(mockBrowser.executeAsync).toHaveBeenCalledWith('mock-perform-scan-script', { method: '' }) - expect(mockBrowser.executeAsync).toHaveBeenCalledWith('mock-get-results-script') + expect(mockBrowser.execute).toHaveBeenCalledWith(expect.stringMatching(/mock-perform-scan-script[\s\S]*\{"method":""\}/)) + expect(mockBrowser.execute).toHaveBeenCalledWith(expect.stringContaining('mock-get-results-script')) expect(result).toEqual(mockResults) }) it('should handle errors gracefully and return empty array', async () => { accessibilityModule.accessibility = true - mockBrowser.executeAsync.mockRejectedValue(new Error('Script execution failed')) + mockBrowser.execute.mockRejectedValue(new Error('Script execution failed')) const result = await accessibilityModule.getA11yResults(mockBrowser) @@ -542,7 +612,7 @@ describe('AccessibilityModule', () => { const result = await accessibilityModule.getA11yResultsSummary(mockBrowser) expect(result).toBeUndefined() - expect(mockBrowser.executeAsync).not.toHaveBeenCalled() + expect(mockBrowser.execute).not.toHaveBeenCalled() }) it('should return accessibility results summary when accessibility is enabled', async () => { @@ -553,18 +623,18 @@ describe('AccessibilityModule', () => { moderateViolations: 3, url: 'https://example.com' } - mockBrowser.executeAsync.mockResolvedValue(mockSummary) + mockBrowser.execute.mockResolvedValue(mockSummary) const result = await accessibilityModule.getA11yResultsSummary(mockBrowser) - expect(mockBrowser.executeAsync).toHaveBeenCalledWith('mock-perform-scan-script', { method: '' }) - expect(mockBrowser.executeAsync).toHaveBeenCalledWith('mock-get-results-summary-script') + expect(mockBrowser.execute).toHaveBeenCalledWith(expect.stringMatching(/mock-perform-scan-script[\s\S]*\{"method":""\}/)) + expect(mockBrowser.execute).toHaveBeenCalledWith(expect.stringContaining('mock-get-results-summary-script')) expect(result).toEqual(mockSummary) }) it('should handle errors gracefully and return empty object', async () => { accessibilityModule.accessibility = true - mockBrowser.executeAsync.mockRejectedValue(new Error('Script execution failed')) + mockBrowser.execute.mockRejectedValue(new Error('Script execution failed')) const result = await accessibilityModule.getA11yResultsSummary(mockBrowser) @@ -616,7 +686,7 @@ describe('AccessibilityModule', () => { expect(validateCapsWithA11y).not.toHaveBeenCalled() }) - it('engages the app performScan path (execute, not web executeAsync)', async () => { + it('engages the app performScan path (formatted app script, not the web scan script)', async () => { vi.mocked(validateCapsWithAppA11y).mockReturnValue(true) vi.mocked(validateCapsWithA11y).mockReturnValue(true) vi.mocked(AutomationFramework.getState).mockImplementation(appGetState as any) @@ -626,10 +696,25 @@ describe('AccessibilityModule', () => { const result = await mockBrowser.performScan() expect(mockBrowser.execute).toHaveBeenCalled() - expect(mockBrowser.executeAsync).not.toHaveBeenCalled() + expect(mockBrowser.execute).not.toHaveBeenCalledWith(expect.stringContaining('mock-perform-scan-script')) expect(result).toEqual({ scanned: true }) }) + it('wraps element commands with an options object and browser commands without one', async () => { + vi.mocked(validateCapsWithAppA11y).mockReturnValue(true) + vi.mocked(validateCapsWithA11y).mockReturnValue(true) + accessibilityScripts.commandsToWrap = [ + { name: 'click', class: 'Element' }, + { name: 'url', class: 'Browser' } + ] + vi.mocked(AutomationFramework.getState).mockImplementation(appGetState as any) + + await accessibilityModule.onBeforeExecute() + + expect(mockBrowser.overwriteCommand).toHaveBeenCalledWith('click', expect.any(Function), { attachToElement: true }) + expect(mockBrowser.overwriteCommand).toHaveBeenCalledWith('url', expect.any(Function), undefined) + }) + it('does not abort onBeforeExecute when an individual command wrap throws', async () => { vi.mocked(validateCapsWithAppA11y).mockReturnValue(true) vi.mocked(validateCapsWithA11y).mockReturnValue(true) diff --git a/packages/browserstack-service/tests/cli/skipReporter.test.ts b/packages/browserstack-service/tests/cli/skipReporter.test.ts index 6846e985..2f28c25c 100644 --- a/packages/browserstack-service/tests/cli/skipReporter.test.ts +++ b/packages/browserstack-service/tests/cli/skipReporter.test.ts @@ -5,7 +5,7 @@ import type { Frameworks } from '@wdio/types' vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger'))) -import { drainSkipReports, markTestStarted, reportSkippedTest, reportSuiteSkipped, resolveSpecFile } from '../../src/cli/skipReporter.js' +import { drainSkipReports, markTestStarted, reportSkippedTest, reportSuiteFailed, reportSuiteSkipped, resolveSpecFile } from '../../src/cli/skipReporter.js' import { TestFrameworkState } from '../../src/cli/states/testFrameworkState.js' import { HookState } from '../../src/cli/states/hookState.js' import type TestFramework from '../../src/cli/frameworks/testFramework.js' @@ -91,6 +91,34 @@ describe('skipReporter', () => { expect(reported).toEqual(['undetermined', 'nested undetermined']) }) + it('reports the undetermined tests of a suite as failed with the hook error', async () => { + const framework = makeFramework() + const parent = { title: 'failed hook suite' } + const suite = { + tests: [ + { title: 'passed before the hook', state: 'passed', parent }, + { title: 'affected by the hook', parent, file: '/spec/b.spec.js' }, + ], + suites: [{ + tests: [{ title: 'nested affected by the hook', parent: { title: 'inner failed hook suite' } }], + suites: [], + }], + } + const error = new Error('Test skipped due to failure in hook "x": boom') + + await reportSuiteFailed(framework, suite, error) + + const results = vi.mocked(framework.trackEvent).mock.calls + .filter(([state, hook]) => state === TestFrameworkState.TEST && hook === HookState.POST) + .map(([, , args]) => args as { test: { title: string }, result: Frameworks.TestResult }) + expect(results.map(({ test }) => test.title)).toEqual(['affected by the hook', 'nested affected by the hook']) + for (const { result } of results) { + expect(result.passed).toBe(false) + expect(result.skipped).toBeUndefined() + expect(result.error).toBe(error) + } + }) + it('resolves spec file from the runner spec when the test has none', () => { expect(resolveSpecFile('/abs/spec.js', 'file:///runner/spec.js')).toBe('/abs/spec.js') expect(resolveSpecFile(undefined, 'file:///runner/spec.js')).toBe('/runner/spec.js') diff --git a/packages/browserstack-service/tests/custom-tags-handler.test.ts b/packages/browserstack-service/tests/custom-tags-handler.test.ts new file mode 100644 index 00000000..70bd8d87 --- /dev/null +++ b/packages/browserstack-service/tests/custom-tags-handler.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from 'vitest' + +import CustomTagsHandler from '../src/custom-tags-handler.js' + +describe('CustomTagsHandler.before', () => { + const multiRemoteBrowser = (flags: Record) => { + const instances: Record> = { browserA: {}, browserB: {} } + return { + browser: { ...flags, getInstance: (name: string) => instances[name] } as Record, + instances + } + } + + it('registers setCustomTags on each instance of a WebdriverIO v10 multiremote browser', () => { + const { browser, instances } = multiRemoteBrowser({ isMultiRemote: true }) + + new CustomTagsHandler(browser as any, { browserA: {}, browserB: {} }, 'mocha').before() + + expect(browser.setCustomTags).toEqual(expect.any(Function)) + expect(instances.browserA.setCustomTags).toEqual(expect.any(Function)) + expect(instances.browserB.setCustomTags).toEqual(expect.any(Function)) + }) + + it('registers setCustomTags on each instance of a WebdriverIO v9 multiremote browser', () => { + const { browser, instances } = multiRemoteBrowser({ isMultiremote: true }) + + new CustomTagsHandler(browser as any, { browserA: {}, browserB: {} }, 'mocha').before() + + expect(instances.browserA.setCustomTags).toEqual(expect.any(Function)) + expect(instances.browserB.setCustomTags).toEqual(expect.any(Function)) + }) + + it('registers setCustomTags on a single browser only', () => { + const { browser, instances } = multiRemoteBrowser({ isMultiRemote: false }) + + new CustomTagsHandler(browser as any, { browserName: 'chrome' }, 'mocha').before() + + expect(browser.setCustomTags).toEqual(expect.any(Function)) + expect(instances.browserA.setCustomTags).toBeUndefined() + }) +}) diff --git a/packages/browserstack-service/tests/insights-handler.test.ts b/packages/browserstack-service/tests/insights-handler.test.ts index 7034c976..f8aab3ce 100644 --- a/packages/browserstack-service/tests/insights-handler.test.ts +++ b/packages/browserstack-service/tests/insights-handler.test.ts @@ -614,6 +614,51 @@ describe('afterHook', () => { expect(insightsHandler['_tests']).toEqual({ 'test title': { finishedAt: '2020-01-01T00:00:00.000Z', } }) expect(insightsHandler['getRunData']).toBeCalledTimes(1) }) + + describe('tests affected by a failed hook', () => { + const hookError = new Error('login failed') + const failedHook = (title: string) => { + const suite = { title: 'suite', tests: [] as unknown[], suites: [] as unknown[] } + suite.tests.push({ title: 'passed before the hook', state: 'passed', parent: suite }) + suite.tests.push({ title: 'affected', parent: suite }) + return { title, parent: 'suite', ctx: { test: { parent: suite } } } as any + } + const affectedRunData = () => vi.mocked(insightsHandler['getRunData']).mock.calls + .filter(([test]) => (test as { title: string }).title === 'affected') + + beforeEach(() => { + insightsHandler['_tests'] = {} + vi.spyOn(insightsHandler.listener, 'testFinished').mockImplementation(() => {}) + }) + + it('reports them as skipped by default', async () => { + await insightsHandler.afterHook(failedHook('"before all" hook for "a"'), { passed: false, error: hookError } as any) + + expect(affectedRunData()).toEqual([[expect.objectContaining({ title: 'affected' }), 'TestRunSkipped']]) + }) + + it('reports them as failed with the hook error when Mocha fails them', async () => { + await insightsHandler.afterHook(failedHook('"before all" hook for "a"'), { passed: false, error: hookError } as any, true) + + const [[, eventType, result]] = affectedRunData() + expect(eventType).toBe('TestRunFinished') + expect(result).toEqual(expect.objectContaining({ passed: false })) + expect(result.error.message).toBe('Test skipped due to failure in hook ""before all" hook for "a"": login failed') + expect(insightsHandler.listener.testFinished).toHaveBeenCalledTimes(1) + }) + + it('reports them as failed after a failed beforeEach hook', async () => { + await insightsHandler.afterHook(failedHook('"before each" hook for "a"'), { passed: false, error: hookError } as any, true) + + expect(affectedRunData()[0][1]).toBe('TestRunFinished') + }) + + it('still reports them as skipped after a failed afterEach hook, which Mocha does not fail', async () => { + await insightsHandler.afterHook(failedHook('"after each" hook for "a"'), { passed: false, error: hookError } as any, true) + + expect(affectedRunData()[0][1]).toBe('TestRunSkipped') + }) + }) }) describe('cucumber', () => { diff --git a/packages/browserstack-service/tests/launcher.test.ts b/packages/browserstack-service/tests/launcher.test.ts index f6216c7a..2a435ee0 100644 --- a/packages/browserstack-service/tests/launcher.test.ts +++ b/packages/browserstack-service/tests/launcher.test.ts @@ -118,6 +118,14 @@ describe('onPrepare', () => { expect(warnSpy).not.toHaveBeenCalledWith(expect.stringContaining('BrowserStack credential mismatch')) }) + it('sets the default Appium version for the capabilities', async () => { + const service = new BrowserstackLauncher({ testObservability: false } as any, caps, config) + const appiumSpy = vi.spyOn(service, '_setDefaultAppiumVersion') + await service.onPrepare(config, caps) + + expect(appiumSpy).toHaveBeenCalledWith(caps) + }) + it('should not try to upload app is app is undefined', async () => { const service = new BrowserstackLauncher({ testObservability: false } as any, caps, config) await service.onPrepare(config, caps) @@ -930,6 +938,101 @@ describe('constructor', () => { }) }) +describe('_setDefaultAppiumVersion', () => { + const config = { + user: 'foobaruser', + key: '12345678901234567890', + capabilities: [] + } + const appCaps = () => [{ 'appium:app': 'bs://app', 'bstack:options': { deviceName: 'Pixel 8' } }] + + // BrowserStackConfig is a singleton, so restore its flag after each test + let restoreAppAutomate: (() => void) | undefined + let wdioMajorSpy: ReturnType> + + beforeEach(() => { + wdioMajorSpy = vi.spyOn(utils, 'getWdioMajorVersion') + }) + + const launcherFor = (appAutomate: boolean, wdioMajor?: number) => { + const service = new BrowserstackLauncher({} as any, [{}], config) + const previous = service.browserStackConfig.appAutomate + restoreAppAutomate = () => { service.browserStackConfig.appAutomate = previous } + service.browserStackConfig.appAutomate = appAutomate + wdioMajorSpy.mockReturnValue(wdioMajor) + return service + } + + afterEach(() => { + restoreAppAutomate?.() + wdioMajorSpy.mockRestore() + }) + + it('sets Appium 3 for App Automate on WebdriverIO 10', () => { + const caps: any = appCaps() + launcherFor(true, 10)._setDefaultAppiumVersion(caps) + + expect(caps[0]['bstack:options'].appiumVersion).toBe('3.3.0') + }) + + it('does not set a version on WebdriverIO 9', () => { + const caps: any = appCaps() + launcherFor(true, 9)._setDefaultAppiumVersion(caps) + + expect(caps[0]['bstack:options'].appiumVersion).toBeUndefined() + }) + + it('does not set a version when the WebdriverIO version is unknown', () => { + const caps: any = appCaps() + launcherFor(true, undefined)._setDefaultAppiumVersion(caps) + + expect(caps[0]['bstack:options'].appiumVersion).toBeUndefined() + }) + + it('does not set a version for Automate sessions', () => { + const caps: any = [{ browserName: 'chrome', 'bstack:options': {} }] + launcherFor(false, 10)._setDefaultAppiumVersion(caps) + + expect(caps[0]['bstack:options'].appiumVersion).toBeUndefined() + }) + + it('keeps the appiumVersion that the user set', () => { + const caps: any = [{ 'appium:app': 'bs://app', 'bstack:options': { appiumVersion: '3.5.2' } }] + launcherFor(true, 10)._setDefaultAppiumVersion(caps) + + expect(caps[0]['bstack:options'].appiumVersion).toBe('3.5.2') + }) + + it('sets the version in alwaysMatch capabilities', () => { + const caps: any = [{ alwaysMatch: { 'appium:app': 'bs://app' } }] + launcherFor(true, 10)._setDefaultAppiumVersion(caps) + + expect(caps[0].alwaysMatch['bstack:options'].appiumVersion).toBe('3.3.0') + }) + + it('sets the version for each multiremote instance', () => { + const caps: any = { + android: { capabilities: { 'appium:app': 'bs://app' } }, + ios: { capabilities: { 'appium:app': 'bs://app', 'bstack:options': { appiumVersion: '3.5.2' } } } + } + launcherFor(true, 10)._setDefaultAppiumVersion(caps) + + expect(caps.android.capabilities['bstack:options'].appiumVersion).toBe('3.3.0') + expect(caps.ios.capabilities['bstack:options'].appiumVersion).toBe('3.5.2') + }) + + it('sets the version for each instance of a parallel multiremote capability', () => { + const caps: any = [{ + android: { capabilities: { 'appium:app': 'bs://app' } }, + ios: { capabilities: { 'appium:app': 'bs://app' } } + }] + launcherFor(true, 10)._setDefaultAppiumVersion(caps) + + expect(caps[0].android.capabilities['bstack:options'].appiumVersion).toBe('3.3.0') + expect(caps[0].ios.capabilities['bstack:options'].appiumVersion).toBe('3.3.0') + }) +}) + describe('_updateCaps', () => { const options: BrowserstackConfig = { browserstackLocal: true } const caps: any = [{}] diff --git a/packages/browserstack-service/tests/service.hookAffectedTests.test.ts b/packages/browserstack-service/tests/service.hookAffectedTests.test.ts new file mode 100644 index 00000000..c7a9de6f --- /dev/null +++ b/packages/browserstack-service/tests/service.hookAffectedTests.test.ts @@ -0,0 +1,121 @@ +import path from 'node:path' + +import { describe, expect, it, vi, beforeEach } from 'vitest' + +import BrowserstackService from '../src/service.js' +import { reportSuiteFailed, reportSuiteSkipped } from '../src/cli/skipReporter.js' +import { BrowserstackCLI } from '../src/cli/index.js' +import * as utils from '../src/util.js' + +vi.mock('@wdio/logger', () => import(path.join(process.cwd(), '__mocks__', '@wdio/logger'))) + +vi.mock('../src/cli/skipReporter.js', () => ({ + drainSkipReports: vi.fn().mockResolvedValue(undefined), + markTestStarted: vi.fn(), + reportSuiteSkipped: vi.fn().mockResolvedValue(undefined), + reportSuiteFailed: vi.fn().mockResolvedValue(undefined), + reportSkippedTest: vi.fn().mockResolvedValue(undefined), + resolveSpecFile: vi.fn() +})) + +vi.mock('../src/instrumentation/performance/performance-tester.js', () => ({ + default: { + start: vi.fn(), + end: vi.fn(), + startMonitoring: vi.fn(), + stopAndGenerate: vi.fn().mockResolvedValue(undefined), + calculateTimes: vi.fn(), + measureWrapper: vi.fn().mockImplementation((_name: string, fn: Function) => fn), + Measure: vi.fn().mockImplementation(() => (_t: any, _k: string, d: PropertyDescriptor) => d) + } +})) + +vi.mock('../src/cli/index.js', () => ({ + BrowserstackCLI: { + getInstance: vi.fn() + } +})) + +/** + * Mocha 12 (WebdriverIO 10) fails the tests that a failed before/beforeEach hook skipped. + * The service must report them with the same status, on the CLI and the legacy path. + */ +describe('afterHook — tests affected by a failed hook', () => { + const suite = { title: 'suite', tests: [], suites: [] } + const hookTest = { title: '"before all" hook for "logs in"', parent: 'suite', ctx: { test: { parent: suite } } } as any + const failedResult = { passed: false, error: new Error('login failed') } as any + const framework = { trackEvent: vi.fn().mockResolvedValue(undefined) } + + const makeService = (mochaOpts?: Record) => + new BrowserstackService({} as any, [] as any, { user: 'foo', key: 'bar', framework: 'mocha', mochaOpts } as any) + + const cliRunning = (running: boolean) => { + vi.mocked(BrowserstackCLI.getInstance).mockReturnValue({ + isRunning: () => running, + getTestFramework: () => framework + } as never) + } + + beforeEach(() => { + vi.clearAllMocks() + vi.spyOn(utils, 'getWdioMajorVersion').mockReturnValue(10) + }) + + it('reports them as failed with the hook error on the CLI path', async () => { + cliRunning(true) + await makeService().afterHook(hookTest, undefined, failedResult) + + expect(reportSuiteSkipped).not.toHaveBeenCalled() + expect(reportSuiteFailed).toHaveBeenCalledWith(framework, suite, expect.any(Error)) + const error = vi.mocked(reportSuiteFailed).mock.calls[0][2] + expect(error.message).toBe('Test skipped due to failure in hook ""before all" hook for "logs in"": login failed') + }) + + it('reports them as skipped on the CLI path when the user turned the option off', async () => { + cliRunning(true) + await makeService({ failHookAffectedTests: false }).afterHook(hookTest, undefined, failedResult) + + expect(reportSuiteFailed).not.toHaveBeenCalled() + expect(reportSuiteSkipped).toHaveBeenCalledWith(framework, suite) + }) + + it('reports them as skipped on the CLI path on WebdriverIO 9', async () => { + vi.spyOn(utils, 'getWdioMajorVersion').mockReturnValue(9) + cliRunning(true) + await makeService().afterHook(hookTest, undefined, failedResult) + + expect(reportSuiteFailed).not.toHaveBeenCalled() + expect(reportSuiteSkipped).toHaveBeenCalledWith(framework, suite) + }) + + it('reports them as skipped on the CLI path after a failed afterEach hook', async () => { + cliRunning(true) + const afterEachHook = { ...hookTest, title: '"after each" hook for "logs in"' } + await makeService().afterHook(afterEachHook, undefined, failedResult) + + expect(reportSuiteFailed).not.toHaveBeenCalled() + expect(reportSuiteSkipped).toHaveBeenCalledWith(framework, suite) + }) + + it('tells the insights handler to fail them on the legacy path', async () => { + cliRunning(false) + const service = makeService() + const afterHook = vi.fn().mockResolvedValue(undefined) + service['_insightsHandler'] = { afterHook } as any + + await service.afterHook(hookTest, undefined, failedResult) + + expect(afterHook).toHaveBeenCalledWith(hookTest, failedResult, true) + }) + + it('tells the insights handler to skip them on the legacy path when the user turned the option off', async () => { + cliRunning(false) + const service = makeService({ failHookAffectedTests: false }) + const afterHook = vi.fn().mockResolvedValue(undefined) + service['_insightsHandler'] = { afterHook } as any + + await service.afterHook(hookTest, undefined, failedResult) + + expect(afterHook).toHaveBeenCalledWith(hookTest, failedResult, false) + }) +}) diff --git a/packages/browserstack-service/tests/service.test.ts b/packages/browserstack-service/tests/service.test.ts index aa32efbc..3aca325d 100644 --- a/packages/browserstack-service/tests/service.test.ts +++ b/packages/browserstack-service/tests/service.test.ts @@ -406,6 +406,27 @@ describe('_multiRemoteAction', () => { }) }) +describe('_executeCommand', () => { + it('should run the executor script on each instance with the WebdriverIO v10 flag', async () => { + const browserA = { executeScript: vi.fn() } + const browserB = { executeScript: vi.fn() } + const multiRemoteBrowser = { + isMultiRemote: true, + executeScript: vi.fn(), + getInstance: vi.fn().mockImplementation((name: string) => name === 'browserA' ? browserA : browserB) + } + const tmpService = new BrowserstackService({} as any, { browserA: {}, browserB: {} } as any, { user: 'foo', key: 'bar' } as any) + tmpService['_browser'] = multiRemoteBrowser as any + vi.spyOn(utils, 'isBrowserstackSession').mockReturnValueOnce(true) + + await tmpService['_executeCommand']('annotate') + + expect(browserA.executeScript).toHaveBeenCalledWith('browserstack_executor: {"action":"annotate"}', []) + expect(browserB.executeScript).toHaveBeenCalledWith('browserstack_executor: {"action":"annotate"}', []) + expect(multiRemoteBrowser.executeScript).not.toHaveBeenCalled() + }) +}) + describe('_update', () => { describe('should call fetch with put method', () => { const getCloudProviderSpy = vi.spyOn(utils, 'getCloudProvider').mockReturnValue('browserstack') @@ -457,6 +478,19 @@ describe('_printSessionURL', () => { expect(isBrowserstackSessionSpy).toHaveBeenCalled() }) + it('should get and log multi remote session details with the WebdriverIO v10 flag', async () => { + browser.isMultiremote = false + ;(browser as any).isMultiRemote = true + service['_browser'] = browser + const logInfoSpy = vi.spyOn(log, 'info').mockImplementation((string) => string) + vi.spyOn(utils, 'isBrowserstackSession').mockReturnValue(true) + await service._printSessionURL() + expect(fetch).toHaveBeenCalledWith(`${sessionBaseUrl}/${sessionIdA}.json`, { method: 'GET', headers }) + expect(logInfoSpy).toHaveBeenCalledWith( + 'Windows 10 chrome session: https://www.browserstack.com/automate/builds/1/sessions/2' + ) + }) + describe('if cant print', () => { describe('no browser object', () => { const logInfoSpy = vi.spyOn(log, 'info').mockImplementation((string) => string) @@ -772,6 +806,8 @@ describe('before', () => { it('should overwrite executeAsync command to route browserstack_executor via executeAsyncScript', async () => { (browser as any).isBidi = true + // WebdriverIO v9 browser: executeAsync exists + ;(browser as any).executeAsync = vi.fn() const service = new BrowserstackService({} as any, [{}] as any, { user: 'foo', key: 'bar', capabilities: {} }) service['_routeBidiExecutorToHttp'](browser) @@ -790,6 +826,39 @@ describe('before', () => { expect(originalExecuteAsync).toHaveBeenCalledWith('arguments[0](1)', extraArg) }) + it('should not overwrite executeAsync on WebdriverIO v10, which removed the command', async () => { + (browser as any).isBidi = true + const service = new BrowserstackService({} as any, [{}] as any, { user: 'foo', key: 'bar', capabilities: {} }) + service['_routeBidiExecutorToHttp'](browser) + + expect(browser.overwriteCommand).toHaveBeenCalledTimes(1) + expect(browser.overwriteCommand).toHaveBeenCalledWith('execute', expect.any(Function)) + }) + + it('should also route executor scripts that run on a WebdriverIO 10 browsing context', async () => { + (browser as any).isBidi = true + ;(browser as any).browsingContexts = vi.fn() + try { + const service = new BrowserstackService({} as any, [{}] as any, { user: 'foo', key: 'bar', capabilities: {} }) + service['_routeBidiExecutorToHttp'](browser) + } finally { + delete (browser as any).browsingContexts + } + + expect(browser.overwriteCommand).toHaveBeenCalledWith('execute', expect.any(Function), { attachToBrowsingContext: true }) + const contextOverwrite = vi.mocked(browser.overwriteCommand).mock.calls + .find(([, , options]) => (options as { attachToBrowsingContext?: boolean })?.attachToBrowsingContext)?.[1] as Function + const context = { contextId: 'tab-2' } + const originalExecute = vi.fn() + + await contextOverwrite.call(context, originalExecute, 'browserstack_executor: {"action":"annotate"}') + expect(browser.executeScript).toHaveBeenCalledWith('browserstack_executor: {"action":"annotate"}', []) + expect(originalExecute).not.toHaveBeenCalled() + + await contextOverwrite.call(context, originalExecute, 'return document.title') + expect(originalExecute).toHaveBeenCalledWith('return document.title') + }) + it('should not overwrite execute command for non-BrowserStack BiDi sessions', async () => { (browser as any).isBidi = true const service = new BrowserstackService({} as any, [{}] as any, { user: 'foo', key: 'bar', capabilities: {} }) @@ -829,6 +898,24 @@ describe('before', () => { expect(originalExecuteA).toHaveBeenCalledWith('return arguments[0]', extraArg) }) + it('should overwrite execute on each instance for multiremote with the WebdriverIO v10 flag', async () => { + const browserA = { executeScript: vi.fn(), overwriteCommand: vi.fn(), sessionId: 'sessionA', isBidi: true } + const browserB = { executeScript: vi.fn(), overwriteCommand: vi.fn(), sessionId: 'sessionB', isBidi: true } + const multiRemoteBrowser = { + ...browser, + isMultiRemote: true, + getInstance: vi.fn().mockImplementation((name: string) => name === 'browserA' ? browserA : browserB) + } as unknown as WebdriverIO.MultiRemoteBrowser + + const service = new BrowserstackService({} as any, { browserA: {}, browserB: {} } as any, { + user: 'foo', key: 'bar' + }) + await service.before(service['_config'] as any, [], multiRemoteBrowser as any) + + expect(browserA.overwriteCommand).toHaveBeenCalledWith('execute', expect.any(Function)) + expect(browserB.overwriteCommand).toHaveBeenCalledWith('execute', expect.any(Function)) + }) + it('should keep patching remaining multiremote instances when one instance fails to resolve', async () => { const browserA = { executeScript: vi.fn(), executeAsyncScript: vi.fn(), overwriteCommand: vi.fn(), sessionId: 'sessionA', isBidi: true } const browserB = { executeScript: vi.fn(), executeAsyncScript: vi.fn(), overwriteCommand: vi.fn(), sessionId: 'sessionB', isBidi: true } diff --git a/packages/browserstack-service/tests/util.appiumVersion.test.ts b/packages/browserstack-service/tests/util.appiumVersion.test.ts new file mode 100644 index 00000000..5b7e2b33 --- /dev/null +++ b/packages/browserstack-service/tests/util.appiumVersion.test.ts @@ -0,0 +1,88 @@ +import fs from 'node:fs' +import os from 'node:os' +import path from 'node:path' +import { pathToFileURL } from 'node:url' + +import { describe, expect, it, beforeAll, afterAll } from 'vitest' + +import { DEFAULT_APPIUM_3_VERSION } from '../src/constants.js' +import { getWdioMajorVersion, setDefaultAppiumVersion } from '../src/util.js' + +describe('getWdioMajorVersion', () => { + let tmpDir: string + + const fakeProject = (name: string, cliVersion?: string) => { + const projectDir = path.join(tmpDir, name) + fs.mkdirSync(projectDir, { recursive: true }) + if (cliVersion) { + const cliDir = path.join(projectDir, 'node_modules', '@wdio', 'cli') + fs.mkdirSync(path.join(cliDir, 'build'), { recursive: true }) + fs.writeFileSync(path.join(cliDir, 'package.json'), JSON.stringify({ + name: '@wdio/cli', + version: cliVersion, + exports: { '.': { require: './build/index.cjs' } } + })) + fs.writeFileSync(path.join(cliDir, 'build', 'index.cjs'), 'module.exports = {}') + } + return pathToFileURL(path.join(projectDir, 'wdio.conf.js')).href + } + + beforeAll(() => { + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'wdio-major-')) + }) + + afterAll(() => { + fs.rmSync(tmpDir, { recursive: true, force: true }) + }) + + it('reads the major version of the @wdio/cli that runs the tests', () => { + // CI sets WDIO_MAJOR for each job of the matrix; a local run uses the WebdriverIO 10 dev dependencies + expect(getWdioMajorVersion()).toBe(Number(process.env.WDIO_MAJOR ?? 10)) + }) + + it('reads a WebdriverIO 9 install', () => { + expect(getWdioMajorVersion(fakeProject('v9', '9.32.0'))).toBe(9) + }) + + it('returns undefined when @wdio/cli is not installed', () => { + expect(getWdioMajorVersion(fakeProject('none'))).toBeUndefined() + }) +}) + +describe('setDefaultAppiumVersion', () => { + it('adds the default Appium 3 version to bstack:options', () => { + const capability: WebdriverIO.Capabilities = { 'bstack:options': { deviceName: 'Pixel 8' } } + + expect(setDefaultAppiumVersion(capability)).toBe(true) + expect(capability['bstack:options']).toEqual({ deviceName: 'Pixel 8', appiumVersion: DEFAULT_APPIUM_3_VERSION }) + }) + + it('creates bstack:options when it is missing', () => { + const capability: WebdriverIO.Capabilities = { 'appium:app': 'bs://app' } + + expect(setDefaultAppiumVersion(capability)).toBe(true) + expect(capability['bstack:options']).toEqual({ appiumVersion: DEFAULT_APPIUM_3_VERSION }) + }) + + it('uses the legacy key for legacy capabilities, which WebdriverIO rejects next to bstack:options', () => { + const capability = { app: 'bs://app', device: 'Google Pixel 8', 'browserstack.local': true } as WebdriverIO.Capabilities + + expect(setDefaultAppiumVersion(capability)).toBe(true) + expect(capability['bstack:options']).toBeUndefined() + expect((capability as Record)['browserstack.appium_version']).toBe(DEFAULT_APPIUM_3_VERSION) + }) + + it('keeps an appiumVersion that the user set', () => { + const capability: WebdriverIO.Capabilities = { 'bstack:options': { appiumVersion: '3.5.2' } } + + expect(setDefaultAppiumVersion(capability)).toBe(false) + expect(capability['bstack:options']?.appiumVersion).toBe('3.5.2') + }) + + it('keeps a legacy browserstack.appium_version that the user set', () => { + const capability = { 'browserstack.appium_version': '2.0.1' } as WebdriverIO.Capabilities + + expect(setDefaultAppiumVersion(capability)).toBe(false) + expect(capability['bstack:options']).toBeUndefined() + }) +}) diff --git a/packages/browserstack-service/tests/util.browsingContext.test.ts b/packages/browserstack-service/tests/util.browsingContext.test.ts new file mode 100644 index 00000000..a55e368f --- /dev/null +++ b/packages/browserstack-service/tests/util.browsingContext.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it, vi } from 'vitest' + +import { overwriteBrowsingContextCommand } from '../src/util.js' + +describe('overwriteBrowsingContextCommand', () => { + const fn = async () => {} + + it('overwrites the command of every browsing context on WebdriverIO 10', () => { + const browser = { browsingContexts: vi.fn(), overwriteCommand: vi.fn() } as unknown as WebdriverIO.Browser + + expect(overwriteBrowsingContextCommand(browser, 'execute', fn)).toBe(true) + expect(browser.overwriteCommand).toHaveBeenCalledWith('execute', fn, { attachToBrowsingContext: true }) + }) + + it('does nothing on WebdriverIO 9, which reads any third argument as "attach to elements"', () => { + const browser = { overwriteCommand: vi.fn() } as unknown as WebdriverIO.Browser + + expect(overwriteBrowsingContextCommand(browser, 'execute', fn)).toBe(false) + expect(browser.overwriteCommand).not.toHaveBeenCalled() + }) + + it('returns false when a browsing context has no command with this name', () => { + const browser = { + browsingContexts: vi.fn(), + overwriteCommand: vi.fn().mockImplementation(() => { + throw new Error('overwriteCommand: no browsing context command to be overwritten: url') + }) + } as unknown as WebdriverIO.Browser + + expect(overwriteBrowsingContextCommand(browser, 'url', fn)).toBe(false) + }) +}) diff --git a/packages/browserstack-service/tests/util.hookAffectedTests.test.ts b/packages/browserstack-service/tests/util.hookAffectedTests.test.ts new file mode 100644 index 00000000..4998fb41 --- /dev/null +++ b/packages/browserstack-service/tests/util.hookAffectedTests.test.ts @@ -0,0 +1,51 @@ +import type { Options } from '@wdio/types' +import { describe, expect, it } from 'vitest' + +import { createHookAffectedTestError, mochaFailsHookAffectedTests } from '../src/util.js' + +const mochaConfig = (mochaOpts?: Record) => + ({ framework: 'mocha', mochaOpts }) as unknown as Options.Testrunner + +describe('mochaFailsHookAffectedTests', () => { + it('is true on WebdriverIO 10 when the option is not set', () => { + expect(mochaFailsHookAffectedTests(mochaConfig(), 10)).toBe(true) + expect(mochaFailsHookAffectedTests(mochaConfig({ timeout: 1000 }), 10)).toBe(true) + }) + + it('is true on WebdriverIO 10 when the option is true', () => { + expect(mochaFailsHookAffectedTests(mochaConfig({ failHookAffectedTests: true }), 10)).toBe(true) + }) + + it('is false on WebdriverIO 10 when the user turned the option off', () => { + expect(mochaFailsHookAffectedTests(mochaConfig({ failHookAffectedTests: false }), 10)).toBe(false) + }) + + it('is false on WebdriverIO 9, which uses a Mocha without the option', () => { + expect(mochaFailsHookAffectedTests(mochaConfig({ failHookAffectedTests: true }), 9)).toBe(false) + }) + + it('is false when the WebdriverIO version is unknown', () => { + expect(mochaFailsHookAffectedTests(mochaConfig(), undefined)).toBe(false) + }) + + it('is false for other frameworks', () => { + expect(mochaFailsHookAffectedTests({ framework: 'jasmine' } as Options.Testrunner, 10)).toBe(false) + expect(mochaFailsHookAffectedTests(undefined, 10)).toBe(false) + }) +}) + +describe('createHookAffectedTestError', () => { + it('gives the message and stack that Mocha 12 gives the affected tests', () => { + const hookError = new Error('login failed') + const error = createHookAffectedTestError('"before all" hook for "logs in"', hookError) + + expect(error.message).toBe('Test skipped due to failure in hook ""before all" hook for "logs in"": login failed') + expect(error.stack).toBe(hookError.stack) + }) + + it('handles a hook that failed without an error', () => { + const error = createHookAffectedTestError('"before each" hook', undefined) + + expect(error.message).toBe('Test skipped due to failure in hook ""before each" hook": Hook failed') + }) +}) diff --git a/packages/browserstack-service/tests/util.test.ts b/packages/browserstack-service/tests/util.test.ts index a6e2e6bc..c80230f7 100644 --- a/packages/browserstack-service/tests/util.test.ts +++ b/packages/browserstack-service/tests/util.test.ts @@ -165,6 +165,16 @@ describe('getBrowserCapabilities', () => { .toEqual(browser.browserA.capabilities as any) }) + it('should get multiremote browser capabilities with the WebdriverIO v10 flag', () => { + const instances: Record = { browserA: { capabilities: { browser: 'browser' } } } + const browser = { + isMultiRemote: true, + getInstance: vi.fn().mockImplementation((browserName: string) => instances[browserName]), + } as unknown as WebdriverIO.MultiRemoteBrowser + expect(getBrowserCapabilities(browser, {}, 'browserA')) + .toEqual({ browser: 'browser' } as any) + }) + it('should handle null multiremote browser capabilities', () => { const browser = { isMultiremote: true, @@ -222,6 +232,35 @@ describe('getBrowserCapabilities', () => { }) }) +describe('isMultiRemoteBrowser', () => { + it('detects the WebdriverIO v10 isMultiRemote flag', () => { + expect(utils.isMultiRemoteBrowser({ isMultiRemote: true })).toBe(true) + }) + + it('detects the WebdriverIO v9 isMultiremote flag', () => { + expect(utils.isMultiRemoteBrowser({ isMultiremote: true })).toBe(true) + }) + + it('returns false for a single browser', () => { + expect(utils.isMultiRemoteBrowser({ isMultiRemote: false })).toBe(false) + expect(utils.isMultiRemoteBrowser({ isMultiremote: false })).toBe(false) + expect(utils.isMultiRemoteBrowser({})).toBe(false) + expect(utils.isMultiRemoteBrowser(undefined)).toBe(false) + }) +}) + +describe('commandScopeOptions', () => { + // WebdriverIO v10 throws on a boolean third argument to overwriteCommand, + // and v9 treats any truthy third argument as "attach to element". + it('attaches element commands with an options object', () => { + expect(utils.commandScopeOptions(true)).toEqual({ attachToElement: true }) + }) + + it('passes no options for browser commands', () => { + expect(utils.commandScopeOptions(false)).toBeUndefined() + }) +}) + describe('getBrowserDescription', () => { const defaultCap = { 'device': 'device', @@ -554,16 +593,25 @@ describe('getCloudProvider', () => { it('return Browserstack if test being run on browserstack', () => { expect(getCloudProvider({ options: { hostname: 'hub.browserstack.com' } })).toEqual('browserstack') }) + // WebdriverIO v10 multiremote: no instance properties, only getInstance() + const multiRemote = (children: Record) => ({ + isMultiRemote: true, + instances: Object.keys(children), + getInstance: (name: string) => children[name] + }) as unknown as WebdriverIO.MultiRemoteBrowser + it('return Browserstack if test being run on browserstack with multiremote', () => { - const browser = { - isMultiremote: true, - instances: ['browserA'], - browserA: { - options: { hostname: 'hub.browserstack.com' } - } - } as unknown as WebdriverIO.MultiRemoteBrowser + const browser = multiRemote({ + browserA: { options: { hostname: 'localhost' } }, + browserB: { options: { hostname: 'hub.browserstack.com' } } + }) expect(getCloudProvider(browser)).toEqual('browserstack') }) + + it('return unknown_grid if no multiremote instance is on browserstack', () => { + const browser = multiRemote({ browserA: { options: { hostname: 'localhost' } } }) + expect(getCloudProvider(browser)).toEqual('unknown_grid') + }) }) describe('isBrowserstackSession', () => {