From 564866a927200c47f495cb1b5805f108674ea914 Mon Sep 17 00:00:00 2001 From: dprevost-perso Date: Sat, 3 Oct 2026 09:37:21 -0400 Subject: [PATCH 01/10] Support webdriverio v10 --- package-lock.json | 22 +++++++++++----------- packages/browserstack-service/package.json | 20 ++++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index f9f67c78..a38cd71c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12905,7 +12905,7 @@ }, "packages/browserstack-service": { "name": "@wdio/browserstack-service", - "version": "9.37.0", + "version": "9.39.3", "license": "MIT", "dependencies": { "@browserstack/ai-sdk-node": "1.5.17", @@ -12915,9 +12915,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-0 || ^10.0.0", + "@wdio/reporter": "^9.0.0 || ^10.0.0-0 || ^10.0.0", + "@wdio/types": "^9.0.0 || ^10.0.0-0 || ^10.0.0", "browserstack-local": "^1.5.1", "chalk": "^5.6.2", "csv-writer": "^1.6.0", @@ -12927,7 +12927,7 @@ "tar": "^7.5.11", "undici": "^6.24.0", "uuid": "^11.1.0", - "webdriverio": "^9.0.0", + "webdriverio": "^9.0.0 || ^10.0.0-0 || ^10.0.0", "winston-transport": "^4.5.0", "yauzl": "^3.4.0" }, @@ -12937,22 +12937,22 @@ "@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/globals": "^9.0.0 || ^10.0.0-0 || ^10.0.0", + "@wdio/logger": "^9.0.0 || ^10.0.0-0 || ^10.0.0", + "@wdio/reporter": "^9.0.0 || ^10.0.0-0 || ^10.0.0", + "@wdio/types": "^9.0.0 || ^10.0.0-0 || ^10.0.0", "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": "^9.0.0 || ^10.0.0-0 || ^10.0.0" }, "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": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0-0 || ^10.0.0" } }, "packages/browserstack-service/node_modules/@esbuild/aix-ppc64": { diff --git a/packages/browserstack-service/package.json b/packages/browserstack-service/package.json index a0cbb17d..b37c754b 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-0 || ^10.0.0", + "@wdio/reporter": "^9.0.0 || ^10.0.0-0 || ^10.0.0", + "@wdio/types": "^9.0.0 || ^10.0.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-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": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0-0 || ^10.0.0" }, "devDependencies": { "@bufbuild/buf": "^1.55.1", @@ -85,16 +85,16 @@ "@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/globals": "^9.0.0 || ^10.0.0-0 || ^10.0.0", + "@wdio/logger": "^9.0.0 || ^10.0.0-0 || ^10.0.0", + "@wdio/reporter": "^9.0.0 || ^10.0.0-0 || ^10.0.0", + "@wdio/types": "^9.0.0 || ^10.0.0-0 || ^10.0.0", "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": "^9.0.0 || ^10.0.0-0 || ^10.0.0" }, "publishConfig": { "access": "public" From a7035bdbf5f21f9fa54e9d64e1c960683adcbd32 Mon Sep 17 00:00:00 2001 From: dprevost-perso Date: Mon, 5 Oct 2026 15:40:11 -0400 Subject: [PATCH 02/10] Support WebdriverIO 10 and keep WebdriverIO 9 support - Detect multiremote with isMultiRemote (v10) or isMultiremote (v9), and get each instance with getInstance(). - Pass { attachToElement: true } to overwriteCommand, which works in v9 and v10. - Run the CLI accessibility scripts with execute(), because v10 removed executeAsync(). Patch executeAsync for BiDi executor routing only on v9. - On WebdriverIO 10, set bstack:options.appiumVersion to 3.3.0 for App Automate capabilities that do not set a version. App Automate uses Appium 1.22.0 by default, and WebdriverIO 10 supports Appium 3 only. Read the major version from @wdio/cli, because webdriverio can resolve to the v9 copy that @percy/webdriverio installs. - Use WebdriverIO 10 dev dependencies. Override the @percy/webdriverio peer range in the private root package until Percy supports v10. - Test WebdriverIO 10 on Node.js 24 and WebdriverIO 9 on Node.js 22 in CI, with a check of the installed major version. - Add a changeset and a README compatibility table. Co-Authored-By: Claude Opus 5.5 --- .changeset/wdio-v10-support.md | 11 + .github/scripts/check-wdio-major.mjs | 16 + .github/workflows/ci.yml | 21 +- package-lock.json | 2685 +++++++++-------- package.json | 5 +- packages/browserstack-service/README.md | 9 + packages/browserstack-service/package.json | 11 +- .../src/Percy/PercyHelper.ts | 2 +- .../src/accessibility-handler.ts | 6 +- .../browserstack-service/src/ai-handler.ts | 18 +- .../src/cli/modules/accessibilityModule.ts | 15 +- .../browserstack-service/src/constants.ts | 4 + .../src/custom-tags-handler.ts | 4 +- packages/browserstack-service/src/launcher.ts | 48 +- packages/browserstack-service/src/service.ts | 19 +- packages/browserstack-service/src/util.ts | 69 +- .../tests/accessibility-handler.test.ts | 27 + .../tests/ai-handler.test.ts | 28 + .../cli/modules/accessibilityModule.test.ts | 93 +- .../tests/custom-tags-handler.test.ts | 41 + .../tests/launcher.test.ts | 103 + .../tests/service.test.ts | 63 + .../tests/util.appiumVersion.test.ts | 79 + .../browserstack-service/tests/util.test.ts | 39 + 24 files changed, 2102 insertions(+), 1314 deletions(-) create mode 100644 .changeset/wdio-v10-support.md create mode 100644 .github/scripts/check-wdio-major.mjs create mode 100644 packages/browserstack-service/tests/custom-tags-handler.test.ts create mode 100644 packages/browserstack-service/tests/util.appiumVersion.test.ts diff --git a/.changeset/wdio-v10-support.md b/.changeset/wdio-v10-support.md new file mode 100644 index 00000000..4ad6d2ef --- /dev/null +++ b/.changeset/wdio-v10-support.md @@ -0,0 +1,11 @@ +--- +"@wdio/browserstack-service": minor +--- + +Support WebdriverIO 10. WebdriverIO 9 stays supported. + +- 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, App Automate sessions that do not set an Appium version get `bstack:options.appiumVersion: '3.3.0'`. 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..a56ad1e7 --- /dev/null +++ b/.github/scripts/check-wdio-major.mjs @@ -0,0 +1,16 @@ +// Fails when the WebdriverIO packages of the service are not on the expected major. +// 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..41f7812a 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,7 +53,21 @@ jobs: - name: Install run: npm ci + - 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 + + # The published build uses the WebdriverIO 10 types - name: Build + if: matrix.wdio == 10 run: npm run build - name: Test diff --git a/package-lock.json b/package-lock.json index a38cd71c..0ff63cf3 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.0", + "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-10.0.0.tgz", + "integrity": "sha512-m8ILazuQQLfdE14IEFrrJLTXkz25Nomhnypw7p5royHuxacB643+d9fYjX4VrI0h/Nufomg2ua/vxRMTm6wVBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/snapshot": "^5.0.2", + "@wdio/config": "10.0.0", + "@wdio/globals": "10.0.0", + "@wdio/logger": "10.0.0", + "@wdio/protocols": "10.0.0", + "@wdio/session": "10.0.0", + "@wdio/types": "10.0.0", + "@wdio/utils": "10.0.0", "async-exit-hook": "^2.0.1", "chalk": "^5.4.1", "chokidar": "^4.0.0", - "create-wdio": "9.28.0", + "create-wdio": "10.0.0", "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.0", + "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.0", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-10.0.0.tgz", + "integrity": "sha512-g0xaQxKJM5hIhZBW05Ivs7vZi/txB+UcWeoiuzNl0/fS5rvQYvofnYlROvCBKeEdf72ooIRtfkRnW8nKQ97P+A==", + "license": "MIT", + "dependencies": { + "@wdio/logger": "10.0.0", + "@wdio/types": "10.0.0", + "@wdio/utils": "10.0.0", + "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.0", + "resolved": "https://registry.npmjs.org/@wdio/display-server/-/display-server-10.0.0.tgz", + "integrity": "sha512-aBLa9Qd+7Cm6LnazcxNGdet141ZY80rx5pAiO6B+f9b/Dz2h3tAP8ryPyKclxKaMoGNh8QDcyNluPGfPW3k0hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@wdio/logger": "10.0.0", + "@wdio/types": "10.0.0" + }, + "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.0", + "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-10.0.0.tgz", + "integrity": "sha512-QlC6dkB2hu//cnWIcqine7MUdiJknmB8v7A3jLqEEIOe4J9QXU6HLk35oKKbVQfDnFiEr0JViLQu9XggqH6K0w==", + "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.0", + "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-10.0.0.tgz", + "integrity": "sha512-qPxe2dq0hFDd6AZj6Z3SunpMvCOv9UdXS3CINtSrJweGNGHj1VltSNVPVqWYWwzs3ywuEjGg2t91JIQVxgfgWA==", "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.0", + "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.0", + "resolved": "https://registry.npmjs.org/@wdio/session/-/session-10.0.0.tgz", + "integrity": "sha512-at9DQLmPfAinFUs6xmbhjHlj0ApaiAbzJ4JPvTW//csAGgeV+C8KAZmkN9jB4ttHHk//GFWANU6tNzEOPFdG5Q==", + "dev": true, "license": "MIT", "dependencies": { - "@types/node": "^20.1.0" + "@types/node": "^22.20.4", + "@wdio/config": "10.0.0", + "@wdio/display-server": "10.0.0", + "@wdio/logger": "10.0.0", + "@wdio/types": "10.0.0", + "@wdio/utils": "10.0.0", + "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.0", + "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.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-10.0.0.tgz", + "integrity": "sha512-qUJbKEHjsN0PuC3S2r7Gy9wYtbChmX+PYghKGHYQhjyH9P9t+sWhSPQWUBk3S+IUTYT1f+Z4VbF+5FmvlHL6Lg==", "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.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-10.0.0.tgz", + "integrity": "sha512-Ta3wq+u83DT0Vnz1QcuqkagBSwWBnTg8LCIQmDO6DGkMIiHXJrhUMfz6ibrlMJPslt4g0P/Ri5wm7WkJXo9HXw==", "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.0", + "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.0", + "resolved": "https://registry.npmjs.org/create-wdio/-/create-wdio-10.0.0.tgz", + "integrity": "sha512-eMvUZd2SVATrIPnYdWZur098ZVShrkpCk/g9QSrgelFcX5oijwUsxn7T0PSDfRLJcIQxPyl7Dyk3ObcAmdMFtg==", + "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.0", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-10.0.0.tgz", + "integrity": "sha512-EyMSQjY+Nq9mAUTCzzW1hjarwHNJWtCBRtGSZkiTcdJdFZ6siKF0Z01hHJFVkF1Xm+aCkFxD1atm1S2cNxCQxA==", "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.0", + "@wdio/logger": "10.0.0", + "@wdio/protocols": "10.0.0", + "@wdio/types": "10.0.0", + "@wdio/utils": "10.0.0", + "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.0", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-10.0.0.tgz", + "integrity": "sha512-DiDflg731z/b+qg4EmVAGihMJSWhHJYhlwrS6Q1uSFqL4c48+xHDj9VjhQr7sFzZ/MBJySjGZfMBGyjP7p547Q==", "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.0", + "@wdio/logger": "10.0.0", + "@wdio/protocols": "10.0.0", + "@wdio/repl": "10.0.0", + "@wdio/types": "10.0.0", + "@wdio/utils": "10.0.0", + "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.0" }, "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,17 +13109,17 @@ } }, "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": { @@ -12937,16 +13156,17 @@ "@types/node": "^20.1.0", "@types/tar": "^7.0.87", "@types/yauzl": "^2.10.3", - "@wdio/globals": "^9.0.0 || ^10.0.0-0 || ^10.0.0", - "@wdio/logger": "^9.0.0 || ^10.0.0-0 || ^10.0.0", - "@wdio/reporter": "^9.0.0 || ^10.0.0-0 || ^10.0.0", - "@wdio/types": "^9.0.0 || ^10.0.0-0 || ^10.0.0", + "@wdio/cli": "^10.0.0-0 || ^10.0.0", + "@wdio/globals": "^10.0.0-0 || ^10.0.0", + "@wdio/logger": "^10.0.0-0 || ^10.0.0", + "@wdio/reporter": "^10.0.0-0 || ^10.0.0", + "@wdio/types": "^10.0.0-0 || ^10.0.0", "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 || ^10.0.0-0 || ^10.0.0" + "webdriverio": "^10.0.0-0 || ^10.0.0" }, "engines": { "node": ">=18.20.0" @@ -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..cf929a75 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. 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 b37c754b..fcf53367 100644 --- a/packages/browserstack-service/package.json +++ b/packages/browserstack-service/package.json @@ -85,16 +85,17 @@ "@types/node": "^20.1.0", "@types/tar": "^7.0.87", "@types/yauzl": "^2.10.3", - "@wdio/globals": "^9.0.0 || ^10.0.0-0 || ^10.0.0", - "@wdio/logger": "^9.0.0 || ^10.0.0-0 || ^10.0.0", - "@wdio/reporter": "^9.0.0 || ^10.0.0-0 || ^10.0.0", - "@wdio/types": "^9.0.0 || ^10.0.0-0 || ^10.0.0", + "@wdio/cli": "^10.0.0-0 || ^10.0.0", + "@wdio/globals": "^10.0.0-0 || ^10.0.0", + "@wdio/logger": "^10.0.0-0 || ^10.0.0", + "@wdio/reporter": "^10.0.0-0 || ^10.0.0", + "@wdio/types": "^10.0.0-0 || ^10.0.0", "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 || ^10.0.0-0 || ^10.0.0" + "webdriverio": "^10.0.0-0 || ^10.0.0" }, "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..a91740d9 100644 --- a/packages/browserstack-service/src/accessibility-handler.ts +++ b/packages/browserstack-service/src/accessibility-handler.ts @@ -58,6 +58,8 @@ import { isAccessibilityAutomationSession, isAppAccessibilityAutomationSession, isBrowserstackSession, + isMultiRemoteBrowser, + commandScopeOptions, o11yClassErrorHandler, shouldScanTestForAccessibility, validateCapsWithA11y, @@ -319,7 +321,7 @@ 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')) } catch (error) { BStackLogger.debug(`Exception in overwrite command ${command.name} - ${error}`) } @@ -336,7 +338,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) { 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..e3c3d68b 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, 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,7 +262,7 @@ 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') ) } catch (wrapError) { this.logger.debug(`Skipping command wrap for ${command.name}: ${wrapError}`) @@ -565,11 +565,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 +599,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 +618,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 +640,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/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/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..9deb1553 100644 --- a/packages/browserstack-service/src/service.ts +++ b/packages/browserstack-service/src/service.ts @@ -6,6 +6,7 @@ import { isBrowserstackCapability, getParentSuiteName, isBrowserstackSession, + isMultiRemoteBrowser, patchConsoleLogs, isTrue, isFalse, @@ -144,7 +145,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 +269,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) @@ -1185,7 +1186,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 +1255,13 @@ export default class BrowserstackService implements Services.ServiceInstance { return originalExecute(script, ...args) }) - browser.overwriteCommand('executeAsync', async (originalExecuteAsync, 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 +1274,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 +1407,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..2ae41d91 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,67 @@ 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 +} + +/** + * 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` 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 + } + capability['bstack:options'] = { ...capability['bstack:options'], appiumVersion: DEFAULT_APPIUM_3_VERSION } + return true +} + /** * get correct browser capabilities object in both multiremote and normal setups * @param browser browser object @@ -126,11 +189,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 diff --git a/packages/browserstack-service/tests/accessibility-handler.test.ts b/packages/browserstack-service/tests/accessibility-handler.test.ts index 8f2f6925..a431cc53 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' @@ -483,6 +484,32 @@ 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) + }) + // 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..299c6737 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,23 @@ 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, + 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 +86,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() } @@ -427,7 +434,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 +445,7 @@ describe('AccessibilityModule', () => { await accessibilityModule.onAfterTest() - expect(mockBrowser.executeAsync).not.toHaveBeenCalled() + expect(mockBrowser.execute).not.toHaveBeenCalled() }) }) @@ -450,7 +457,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 +471,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 +513,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 +522,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 +548,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 +559,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 +622,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 +632,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/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/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.test.ts b/packages/browserstack-service/tests/service.test.ts index aa32efbc..b14f0692 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,15 @@ 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 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 +874,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..d330b659 --- /dev/null +++ b/packages/browserstack-service/tests/util.appiumVersion.test.ts @@ -0,0 +1,79 @@ +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', () => { + expect(getWdioMajorVersion()).toBe(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('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.test.ts b/packages/browserstack-service/tests/util.test.ts index a6e2e6bc..9721c149 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', From 7929befd31a4ab9ebf2683087a48cd8d131a9b42 Mon Sep 17 00:00:00 2001 From: dprevost-perso Date: Mon, 5 Oct 2026 21:42:47 -0400 Subject: [PATCH 03/10] Get multiremote instances with getInstance in v10 WebdriverIO 10 does not store multiremote instances as properties of the browser object. getCloudProvider() read browser[instanceName], so it threw a TypeError for every multiremote session, first in the service before hook. AccessibilityHandler.isBidiSession() read the same properties and always returned false for multiremote. Both now use getInstance(), which works in WebdriverIO 9 and 10. The test fixtures use the v10 object shape, without instance properties. Co-Authored-By: Claude Opus 5.5 --- .../src/accessibility-handler.ts | 5 ++-- packages/browserstack-service/src/util.ts | 7 +++--- .../tests/accessibility-handler.test.ts | 10 ++++++-- .../browserstack-service/tests/util.test.ts | 23 +++++++++++++------ 4 files changed, 31 insertions(+), 14 deletions(-) diff --git a/packages/browserstack-service/src/accessibility-handler.ts b/packages/browserstack-service/src/accessibility-handler.ts index a91740d9..5d4e8dca 100644 --- a/packages/browserstack-service/src/accessibility-handler.ts +++ b/packages/browserstack-service/src/accessibility-handler.ts @@ -660,8 +660,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/util.ts b/packages/browserstack-service/src/util.ts index 2ae41d91..6bdabdbe 100644 --- a/packages/browserstack-service/src/util.ts +++ b/packages/browserstack-service/src/util.ts @@ -1217,10 +1217,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 a431cc53..ec8bffdf 100644 --- a/packages/browserstack-service/tests/accessibility-handler.test.ts +++ b/packages/browserstack-service/tests/accessibility-handler.test.ts @@ -54,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) }) }) diff --git a/packages/browserstack-service/tests/util.test.ts b/packages/browserstack-service/tests/util.test.ts index 9721c149..c80230f7 100644 --- a/packages/browserstack-service/tests/util.test.ts +++ b/packages/browserstack-service/tests/util.test.ts @@ -593,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', () => { From 4b1e93fbd9862f80fd6ed1e3c84dbc9757ff7220 Mon Sep 17 00:00:00 2001 From: dprevost-perso Date: Mon, 5 Oct 2026 22:17:48 -0400 Subject: [PATCH 04/10] Report tests after a failed Mocha hook as failed on WebdriverIO 10 Mocha 12 (WebdriverIO 10) fails the tests that a failed before or beforeEach hook skipped (mochaOpts.failHookAffectedTests, true by default). The service reported them as skipped, so the dashboard and WebdriverIO did not agree. When the option is not false on WebdriverIO 10, the service now reports these tests as failed, with the Mocha 12 message "Test skipped due to failure in hook ...", on the legacy path (InsightsHandler.afterHook) and on the CLI path (reportSuiteFailed). WebdriverIO 9, afterEach failures and failHookAffectedTests: false keep the skipped status. Co-Authored-By: Claude Opus 5.5 --- .changeset/wdio-v10-support.md | 1 + .../src/cli/skipReporter.ts | 21 ++- .../src/insights-handler.ts | 16 ++- packages/browserstack-service/src/service.ts | 24 +++- packages/browserstack-service/src/util.ts | 27 ++++ .../tests/cli/skipReporter.test.ts | 30 ++++- .../tests/insights-handler.test.ts | 45 +++++++ .../tests/service.hookAffectedTests.test.ts | 121 ++++++++++++++++++ .../tests/util.hookAffectedTests.test.ts | 51 ++++++++ 9 files changed, 324 insertions(+), 12 deletions(-) create mode 100644 packages/browserstack-service/tests/service.hookAffectedTests.test.ts create mode 100644 packages/browserstack-service/tests/util.hookAffectedTests.test.ts diff --git a/.changeset/wdio-v10-support.md b/.changeset/wdio-v10-support.md index 4ad6d2ef..12385f9a 100644 --- a/.changeset/wdio-v10-support.md +++ b/.changeset/wdio-v10-support.md @@ -8,4 +8,5 @@ Support WebdriverIO 10. WebdriverIO 9 stays supported. - 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, App Automate sessions that do not set an Appium version get `bstack:options.appiumVersion: '3.3.0'`. App Automate uses Appium 1.22.0 by default, and WebdriverIO 10 supports Appium 3 only. 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/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/service.ts b/packages/browserstack-service/src/service.ts index 9deb1553..decc2b02 100644 --- a/packages/browserstack-service/src/service.ts +++ b/packages/browserstack-service/src/service.ts @@ -12,7 +12,10 @@ import { isFalse, getUniqueIdentifier, getHookType, - isBrowserstackExecutorScript + isBrowserstackExecutorScript, + getWdioMajorVersion, + mochaFailsHookAffectedTests, + createHookAffectedTestError } from './util.js' import type { BrowserstackConfig, BrowserstackOptions, MultiRemoteAction } from './types.js' import type { Pickle, Feature, ITestCaseHookParameter, CucumberHook } from './cucumber-types.js' @@ -36,7 +39,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' @@ -57,6 +60,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 @@ -499,6 +503,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). @@ -549,13 +562,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() } diff --git a/packages/browserstack-service/src/util.ts b/packages/browserstack-service/src/util.ts index 6bdabdbe..9c378988 100644 --- a/packages/browserstack-service/src/util.ts +++ b/packages/browserstack-service/src/util.ts @@ -182,6 +182,33 @@ export function setDefaultAppiumVersion(capability: WebdriverIO.Capabilities): b 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 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/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/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/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') + }) +}) From 3dcd9fa896b414744c5cf14abefe4f51d4fde55e Mon Sep 17 00:00:00 2001 From: dprevost-perso Date: Tue, 6 Oct 2026 06:52:14 -0400 Subject: [PATCH 05/10] Fix v10 Appium default, a11y wrapper retry and browsing contexts - setDefaultAppiumVersion() added bstack:options to capabilities in the legacy format (no x:y key). WebdriverIO rejects a capability that mixes extension keys with legacy keys, so the session did not start. These capabilities now get browserstack.appium_version. - The CLI accessibility commandWrapper caught the error of the original command, logged it as its own error, and ran the command again. In WebdriverIO 10, every StrictSelectorError went through this path. The command now runs once, outside the try, and its error goes to the caller. A failed scan setup still logs and lets the command run. - WebdriverIO 10 browser.url() and browser.newWindow() return browsing contexts with their own commands, which a browser-level overwriteCommand does not reach. overwriteBrowsingContextCommand() also overwrites them with { attachToBrowsingContext: true }, only when browser.browsingContexts exists (WebdriverIO 9 reads any third argument as "attach to elements"). It routes browserstack_executor scripts from context.execute(), and runs the accessibility scan before the Browser commands of a context, on that context (legacy and CLI paths). Co-Authored-By: Claude Opus 5.5 --- .changeset/wdio-v10-support.md | 4 +- packages/browserstack-service/README.md | 2 +- .../src/accessibility-handler.ts | 19 +++++- .../src/cli/modules/accessibilityModule.ts | 40 +++++++----- packages/browserstack-service/src/service.ts | 12 +++- packages/browserstack-service/src/util.ts | 33 +++++++++- .../tests/accessibility-handler.test.ts | 51 +++++++++++++++ .../cli/modules/accessibilityModule.test.ts | 64 +++++++++++++++++++ .../tests/service.test.ts | 24 +++++++ .../tests/util.appiumVersion.test.ts | 8 +++ .../tests/util.browsingContext.test.ts | 32 ++++++++++ 11 files changed, 268 insertions(+), 21 deletions(-) create mode 100644 packages/browserstack-service/tests/util.browsingContext.test.ts diff --git a/.changeset/wdio-v10-support.md b/.changeset/wdio-v10-support.md index 12385f9a..0c886fc1 100644 --- a/.changeset/wdio-v10-support.md +++ b/.changeset/wdio-v10-support.md @@ -9,4 +9,6 @@ Support WebdriverIO 10. WebdriverIO 9 stays supported. - 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, App Automate sessions that do not set an Appium version get `bstack:options.appiumVersion: '3.3.0'`. App Automate uses Appium 1.22.0 by default, and WebdriverIO 10 supports Appium 3 only. +- 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/packages/browserstack-service/README.md b/packages/browserstack-service/README.md index cf929a75..78634c93 100644 --- a/packages/browserstack-service/README.md +++ b/packages/browserstack-service/README.md @@ -21,7 +21,7 @@ Instructions on how to install `WebdriverIO` can be found [here.](https://webdri | 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. 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. +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/src/accessibility-handler.ts b/packages/browserstack-service/src/accessibility-handler.ts index 5d4e8dca..4f88b37e 100644 --- a/packages/browserstack-service/src/accessibility-handler.ts +++ b/packages/browserstack-service/src/accessibility-handler.ts @@ -60,6 +60,7 @@ import { isBrowserstackSession, isMultiRemoteBrowser, commandScopeOptions, + overwriteBrowsingContextCommand, o11yClassErrorHandler, shouldScanTestForAccessibility, validateCapsWithA11y, @@ -322,6 +323,14 @@ class _AccessibilityHandler { const prevImpl = orig ? orig.bind(browser) : undefined // @ts-expect-error fix type 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}`) } @@ -543,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] && @@ -561,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) } diff --git a/packages/browserstack-service/src/cli/modules/accessibilityModule.ts b/packages/browserstack-service/src/cli/modules/accessibilityModule.ts index e3c3d68b..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, commandScopeOptions, executeAccessibilityScript, 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' @@ -264,6 +264,14 @@ export default class AccessibilityModule extends BaseModule { this.commandWrapper.bind(this, command), 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) { diff --git a/packages/browserstack-service/src/service.ts b/packages/browserstack-service/src/service.ts index decc2b02..7761053a 100644 --- a/packages/browserstack-service/src/service.ts +++ b/packages/browserstack-service/src/service.ts @@ -15,7 +15,8 @@ import { isBrowserstackExecutorScript, getWdioMajorVersion, mochaFailsHookAffectedTests, - createHookAffectedTestError + createHookAffectedTestError, + overwriteBrowsingContextCommand } from './util.js' import type { BrowserstackConfig, BrowserstackOptions, MultiRemoteAction } from './types.js' import type { Pickle, Feature, ITestCaseHookParameter, CucumberHook } from './cucumber-types.js' @@ -1271,6 +1272,15 @@ export default class BrowserstackService implements Services.ServiceInstance { return originalExecute(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 diff --git a/packages/browserstack-service/src/util.ts b/packages/browserstack-service/src/util.ts index 9c378988..c502492d 100644 --- a/packages/browserstack-service/src/util.ts +++ b/packages/browserstack-service/src/util.ts @@ -140,6 +140,29 @@ export function commandScopeOptions(attachToElement: boolean): { attachToElement 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, @@ -169,7 +192,8 @@ export function getWdioMajorVersion(from: string = import.meta.url): number | un } /** - * set `bstack:options.appiumVersion` to the default Appium 3 version, unless the user set a version + * 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 */ @@ -178,6 +202,13 @@ export function setDefaultAppiumVersion(capability: WebdriverIO.Capabilities): b 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 } diff --git a/packages/browserstack-service/tests/accessibility-handler.test.ts b/packages/browserstack-service/tests/accessibility-handler.test.ts index ec8bffdf..2affba81 100644 --- a/packages/browserstack-service/tests/accessibility-handler.test.ts +++ b/packages/browserstack-service/tests/accessibility-handler.test.ts @@ -516,6 +516,57 @@ describe('scans ahead of the first test (config-level hooks)', () => { 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/cli/modules/accessibilityModule.test.ts b/packages/browserstack-service/tests/cli/modules/accessibilityModule.test.ts index 299c6737..4756df7c 100644 --- a/packages/browserstack-service/tests/cli/modules/accessibilityModule.test.ts +++ b/packages/browserstack-service/tests/cli/modules/accessibilityModule.test.ts @@ -35,6 +35,7 @@ vi.mock('../../../src/util.js', async (importOriginal) => { // 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), @@ -249,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) diff --git a/packages/browserstack-service/tests/service.test.ts b/packages/browserstack-service/tests/service.test.ts index b14f0692..3aca325d 100644 --- a/packages/browserstack-service/tests/service.test.ts +++ b/packages/browserstack-service/tests/service.test.ts @@ -835,6 +835,30 @@ describe('before', () => { 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: {} }) diff --git a/packages/browserstack-service/tests/util.appiumVersion.test.ts b/packages/browserstack-service/tests/util.appiumVersion.test.ts index d330b659..37b431ca 100644 --- a/packages/browserstack-service/tests/util.appiumVersion.test.ts +++ b/packages/browserstack-service/tests/util.appiumVersion.test.ts @@ -63,6 +63,14 @@ describe('setDefaultAppiumVersion', () => { 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' } } 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) + }) +}) From 2cd4b1f7afa19f895fa0b73db610b734f328c82d Mon Sep 17 00:00:00 2001 From: dprevost-perso Date: Tue, 6 Oct 2026 07:11:09 -0400 Subject: [PATCH 06/10] Explain why CI checks the installed WebdriverIO major Co-Authored-By: Claude Opus 5.5 --- .github/scripts/check-wdio-major.mjs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.github/scripts/check-wdio-major.mjs b/.github/scripts/check-wdio-major.mjs index a56ad1e7..fc7a4ade 100644 --- a/.github/scripts/check-wdio-major.mjs +++ b/.github/scripts/check-wdio-major.mjs @@ -1,4 +1,15 @@ -// Fails when the WebdriverIO packages of the service are not on the expected major. +// 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' From ce69fc41bed31a49eb8ed46c74cd4f6a933b9e01 Mon Sep 17 00:00:00 2001 From: dprevost-perso Date: Tue, 6 Oct 2026 07:11:09 -0400 Subject: [PATCH 07/10] Drop the WebdriverIO 10 prerelease ranges WebdriverIO 10.0.0 is released, so ^10.0.0-0 is no longer needed. Co-Authored-By: Claude Opus 5.5 --- package-lock.json | 24 +++++++++++----------- packages/browserstack-service/package.json | 22 ++++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0ff63cf3..ec607372 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13124,7 +13124,7 @@ }, "packages/browserstack-service": { "name": "@wdio/browserstack-service", - "version": "9.39.3", + "version": "9.39.4", "license": "MIT", "dependencies": { "@browserstack/ai-sdk-node": "1.5.17", @@ -13134,9 +13134,9 @@ "@percy/selenium-webdriver": "^2.2.2", "@percy/webdriverio": "^3.3.4", "@types/gitconfiglocal": "^2.0.1", - "@wdio/logger": "^9.0.0 || ^10.0.0-0 || ^10.0.0", - "@wdio/reporter": "^9.0.0 || ^10.0.0-0 || ^10.0.0", - "@wdio/types": "^9.0.0 || ^10.0.0-0 || ^10.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", @@ -13146,7 +13146,7 @@ "tar": "^7.5.11", "undici": "^6.24.0", "uuid": "^11.1.0", - "webdriverio": "^9.0.0 || ^10.0.0-0 || ^10.0.0", + "webdriverio": "^9.0.0 || ^10.0.0", "winston-transport": "^4.5.0", "yauzl": "^3.4.0" }, @@ -13156,23 +13156,23 @@ "@types/node": "^20.1.0", "@types/tar": "^7.0.87", "@types/yauzl": "^2.10.3", - "@wdio/cli": "^10.0.0-0 || ^10.0.0", - "@wdio/globals": "^10.0.0-0 || ^10.0.0", - "@wdio/logger": "^10.0.0-0 || ^10.0.0", - "@wdio/reporter": "^10.0.0-0 || ^10.0.0", - "@wdio/types": "^10.0.0-0 || ^10.0.0", + "@wdio/cli": "^10.0.0", + "@wdio/globals": "^10.0.0", + "@wdio/logger": "^10.0.0", + "@wdio/reporter": "^10.0.0", + "@wdio/types": "^10.0.0", "esbuild": "^0.27.2", "rimraf": "^6.0.1", "ts-proto": "^2.7.5", "typescript": "^5.8.3", "vitest": "^3.2.4", - "webdriverio": "^10.0.0-0 || ^10.0.0" + "webdriverio": "^10.0.0" }, "engines": { "node": ">=18.20.0" }, "peerDependencies": { - "@wdio/cli": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0-0 || ^10.0.0" + "@wdio/cli": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" } }, "packages/browserstack-service/node_modules/@esbuild/aix-ppc64": { diff --git a/packages/browserstack-service/package.json b/packages/browserstack-service/package.json index fcf53367..4553e0dc 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 || ^10.0.0-0 || ^10.0.0", - "@wdio/reporter": "^9.0.0 || ^10.0.0-0 || ^10.0.0", - "@wdio/types": "^9.0.0 || ^10.0.0-0 || ^10.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 || ^10.0.0-0 || ^10.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 || ^10.0.0-0 || ^10.0.0" + "@wdio/cli": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" }, "devDependencies": { "@bufbuild/buf": "^1.55.1", @@ -85,17 +85,17 @@ "@types/node": "^20.1.0", "@types/tar": "^7.0.87", "@types/yauzl": "^2.10.3", - "@wdio/cli": "^10.0.0-0 || ^10.0.0", - "@wdio/globals": "^10.0.0-0 || ^10.0.0", - "@wdio/logger": "^10.0.0-0 || ^10.0.0", - "@wdio/reporter": "^10.0.0-0 || ^10.0.0", - "@wdio/types": "^10.0.0-0 || ^10.0.0", + "@wdio/cli": "^10.0.0", + "@wdio/globals": "^10.0.0", + "@wdio/logger": "^10.0.0", + "@wdio/reporter": "^10.0.0", + "@wdio/types": "^10.0.0", "esbuild": "^0.27.2", "rimraf": "^6.0.1", "ts-proto": "^2.7.5", "typescript": "^5.8.3", "vitest": "^3.2.4", - "webdriverio": "^10.0.0-0 || ^10.0.0" + "webdriverio": "^10.0.0" }, "publishConfig": { "access": "public" From 2b1d2ea1f6cd4bf78ca09082233c9d0509e706fc Mon Sep 17 00:00:00 2001 From: dprevost-perso Date: Tue, 6 Oct 2026 07:18:02 -0400 Subject: [PATCH 08/10] Narrow the @wdio/cli peer range to WebdriverIO 9 and 10 The service needs WebdriverIO 9 or 10: webdriverio, @wdio/types, @wdio/reporter and @wdio/logger are ^9 || ^10. The peer range still allowed @wdio/cli 5 to 8, so npm installed the service on those versions without an error, and it did not work at runtime. WebdriverIO 7 and 8 users have the v7 and v8 release lines. Co-Authored-By: Claude Opus 5.5 --- .changeset/wdio-v10-support.md | 1 + package-lock.json | 2 +- packages/browserstack-service/package.json | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.changeset/wdio-v10-support.md b/.changeset/wdio-v10-support.md index 0c886fc1..8bbe89a2 100644 --- a/.changeset/wdio-v10-support.md +++ b/.changeset/wdio-v10-support.md @@ -4,6 +4,7 @@ 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()`. diff --git a/package-lock.json b/package-lock.json index ec607372..05e6186a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13172,7 +13172,7 @@ "node": ">=18.20.0" }, "peerDependencies": { - "@wdio/cli": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" + "@wdio/cli": "^9.0.0 || ^10.0.0" } }, "packages/browserstack-service/node_modules/@esbuild/aix-ppc64": { diff --git a/packages/browserstack-service/package.json b/packages/browserstack-service/package.json index 4553e0dc..67671768 100644 --- a/packages/browserstack-service/package.json +++ b/packages/browserstack-service/package.json @@ -77,7 +77,7 @@ "yauzl": "^3.4.0" }, "peerDependencies": { - "@wdio/cli": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0" + "@wdio/cli": "^9.0.0 || ^10.0.0" }, "devDependencies": { "@bufbuild/buf": "^1.55.1", From 2035dc9851590b712b6e91a3f38c6fed603a039d Mon Sep 17 00:00:00 2001 From: dprevost-perso Date: Wed, 7 Oct 2026 13:03:28 -0400 Subject: [PATCH 09/10] Build in every CI job and check the tested WebdriverIO major The WebdriverIO 9 job did not build, so src/grpc/generated did not exist and 12 test files failed to load. Every job now builds before it installs another WebdriverIO major; the build still uses the WebdriverIO 10 types. The getWdioMajorVersion test expected 10. CI now sets WDIO_MAJOR for each job, and the test expects that major (10 for a local run). Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 12 +++++++----- .../tests/util.appiumVersion.test.ts | 3 ++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41f7812a..2d41ad0d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,6 +53,11 @@ 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: >- @@ -65,10 +70,7 @@ jobs: 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 - # The published build uses the WebdriverIO 10 types - - name: Build - if: matrix.wdio == 10 - run: npm run build - - name: Test + env: + WDIO_MAJOR: ${{ matrix.wdio }} run: npm test diff --git a/packages/browserstack-service/tests/util.appiumVersion.test.ts b/packages/browserstack-service/tests/util.appiumVersion.test.ts index 37b431ca..5b7e2b33 100644 --- a/packages/browserstack-service/tests/util.appiumVersion.test.ts +++ b/packages/browserstack-service/tests/util.appiumVersion.test.ts @@ -36,7 +36,8 @@ describe('getWdioMajorVersion', () => { }) it('reads the major version of the @wdio/cli that runs the tests', () => { - expect(getWdioMajorVersion()).toBe(10) + // 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', () => { From e07b35dedca1e4a8d64ac42128e4da0e51ce50ae Mon Sep 17 00:00:00 2001 From: dprevost-perso Date: Wed, 7 Oct 2026 13:03:28 -0400 Subject: [PATCH 10/10] Update the WebdriverIO dev dependencies to 10.0.1 Co-Authored-By: Claude Opus 5.5 --- package-lock.json | 124 ++++++++++----------- packages/browserstack-service/package.json | 10 +- 2 files changed, 67 insertions(+), 67 deletions(-) diff --git a/package-lock.json b/package-lock.json index 05e6186a..b58b1956 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3024,24 +3024,24 @@ "link": true }, "node_modules/@wdio/cli": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@wdio/cli/-/cli-10.0.0.tgz", - "integrity": "sha512-m8ILazuQQLfdE14IEFrrJLTXkz25Nomhnypw7p5royHuxacB643+d9fYjX4VrI0h/Nufomg2ua/vxRMTm6wVBA==", + "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.0", - "@wdio/globals": "10.0.0", + "@wdio/config": "10.0.1", + "@wdio/globals": "10.0.1", "@wdio/logger": "10.0.0", "@wdio/protocols": "10.0.0", - "@wdio/session": "10.0.0", - "@wdio/types": "10.0.0", - "@wdio/utils": "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": "10.0.0", + "create-wdio": "10.0.1", "dotenv": "^17.2.0", "import-meta-resolve": "^4.0.0", "lodash.flattendeep": "^4.4.0", @@ -3049,7 +3049,7 @@ "lodash.union": "^4.6.0", "read-pkg-up": "^10.0.0", "tsx": "^4.7.2", - "webdriverio": "10.0.0", + "webdriverio": "10.0.1", "yargs": "^18.2.0" }, "bin": { @@ -3223,14 +3223,14 @@ } }, "node_modules/@wdio/config": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-10.0.0.tgz", - "integrity": "sha512-g0xaQxKJM5hIhZBW05Ivs7vZi/txB+UcWeoiuzNl0/fS5rvQYvofnYlROvCBKeEdf72ooIRtfkRnW8nKQ97P+A==", + "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.0", - "@wdio/utils": "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", @@ -3419,23 +3419,23 @@ } }, "node_modules/@wdio/display-server": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@wdio/display-server/-/display-server-10.0.0.tgz", - "integrity": "sha512-aBLa9Qd+7Cm6LnazcxNGdet141ZY80rx5pAiO6B+f9b/Dz2h3tAP8ryPyKclxKaMoGNh8QDcyNluPGfPW3k0hA==", + "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.0" + "@wdio/types": "10.0.1" }, "engines": { "node": ">=22.19.0" } }, "node_modules/@wdio/globals": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@wdio/globals/-/globals-10.0.0.tgz", - "integrity": "sha512-QlC6dkB2hu//cnWIcqine7MUdiJknmB8v7A3jLqEEIOe4J9QXU6HLk35oKKbVQfDnFiEr0JViLQu9XggqH6K0w==", + "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": { @@ -3519,14 +3519,14 @@ } }, "node_modules/@wdio/reporter": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@wdio/reporter/-/reporter-10.0.0.tgz", - "integrity": "sha512-qPxe2dq0hFDd6AZj6Z3SunpMvCOv9UdXS3CINtSrJweGNGHj1VltSNVPVqWYWwzs3ywuEjGg2t91JIQVxgfgWA==", + "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": "^22.20.4", - "@wdio/types": "10.0.0", + "@wdio/types": "10.0.1", "diff": "^8.0.4", "object-inspect": "^1.12.0" }, @@ -3535,18 +3535,18 @@ } }, "node_modules/@wdio/session": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@wdio/session/-/session-10.0.0.tgz", - "integrity": "sha512-at9DQLmPfAinFUs6xmbhjHlj0ApaiAbzJ4JPvTW//csAGgeV+C8KAZmkN9jB4ttHHk//GFWANU6tNzEOPFdG5Q==", + "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": "^22.20.4", - "@wdio/config": "10.0.0", - "@wdio/display-server": "10.0.0", + "@wdio/config": "10.0.1", + "@wdio/display-server": "10.0.1", "@wdio/logger": "10.0.0", - "@wdio/types": "10.0.0", - "@wdio/utils": "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", @@ -3554,7 +3554,7 @@ "picomatch": "^4.0.7", "semver": "^7.7.4", "tsx": "^4.23.15", - "webdriverio": "10.0.0", + "webdriverio": "10.0.1", "yargs": "^18.2.0" }, "bin": { @@ -3757,9 +3757,9 @@ } }, "node_modules/@wdio/types": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-10.0.0.tgz", - "integrity": "sha512-qUJbKEHjsN0PuC3S2r7Gy9wYtbChmX+PYghKGHYQhjyH9P9t+sWhSPQWUBk3S+IUTYT1f+Z4VbF+5FmvlHL6Lg==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-10.0.1.tgz", + "integrity": "sha512-HGP9lAYXCsLAXzugfT4khCh7tYByWOMIxZHCG+tUvgmpwNzi6lfIsxyIlWownXK1wYJUpk6tkDBX/iXwN+q0lg==", "license": "MIT", "dependencies": { "@types/node": "^22.20.4" @@ -3769,15 +3769,15 @@ } }, "node_modules/@wdio/utils": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-10.0.0.tgz", - "integrity": "sha512-Ta3wq+u83DT0Vnz1QcuqkagBSwWBnTg8LCIQmDO6DGkMIiHXJrhUMfz6ibrlMJPslt4g0P/Ri5wm7WkJXo9HXw==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-10.0.1.tgz", + "integrity": "sha512-aYvTtF+7IYsvkFdXdu32Q2vexEaXYK79R1wLTnAXrT2Q7fp0oCDL0UgBx8Jgo5Tn98ZLWx4JlUeZsDRAIkV4Zw==", "license": "MIT", "dependencies": { "@puppeteer/browsers": "^3.2.1", "@types/split2": "^4.2.3", "@wdio/logger": "10.0.0", - "@wdio/types": "10.0.0", + "@wdio/types": "10.0.1", "aria-query": "^5.3.2", "decamelize": "^6.0.0", "deepmerge-ts": "^8.0.0", @@ -4877,9 +4877,9 @@ } }, "node_modules/create-wdio": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/create-wdio/-/create-wdio-10.0.0.tgz", - "integrity": "sha512-eMvUZd2SVATrIPnYdWZur098ZVShrkpCk/g9QSrgelFcX5oijwUsxn7T0PSDfRLJcIQxPyl7Dyk3ObcAmdMFtg==", + "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", "dependencies": { @@ -12626,18 +12626,18 @@ } }, "node_modules/webdriver": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-10.0.0.tgz", - "integrity": "sha512-EyMSQjY+Nq9mAUTCzzW1hjarwHNJWtCBRtGSZkiTcdJdFZ6siKF0Z01hHJFVkF1Xm+aCkFxD1atm1S2cNxCQxA==", + "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": "^22.20.4", "@types/ws": "^8.5.3", - "@wdio/config": "10.0.0", + "@wdio/config": "10.0.1", "@wdio/logger": "10.0.0", "@wdio/protocols": "10.0.0", - "@wdio/types": "10.0.0", - "@wdio/utils": "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": "^8.11.2", @@ -12679,19 +12679,19 @@ } }, "node_modules/webdriverio": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-10.0.0.tgz", - "integrity": "sha512-DiDflg731z/b+qg4EmVAGihMJSWhHJYhlwrS6Q1uSFqL4c48+xHDj9VjhQr7sFzZ/MBJySjGZfMBGyjP7p547Q==", + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-10.0.1.tgz", + "integrity": "sha512-nITIsmcooGy50RLrol4BDhnzEn04uZ5WJWVlebIQOnErhw2A0ZwR6XClX7v1z+qHtvDZVVEQs5i3Yfz9LR5XPA==", "license": "MIT", "dependencies": { "@types/node": "^22.20.4", "@types/sinonjs__fake-timers": "^8.1.5", - "@wdio/config": "10.0.0", + "@wdio/config": "10.0.1", "@wdio/logger": "10.0.0", "@wdio/protocols": "10.0.0", "@wdio/repl": "10.0.0", - "@wdio/types": "10.0.0", - "@wdio/utils": "10.0.0", + "@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", @@ -12706,7 +12706,7 @@ "rgb2hex": "0.2.5", "serialize-error": "^12.0.0", "urlpattern-polyfill": "^10.0.0", - "webdriver": "10.0.0" + "webdriver": "10.0.1" }, "engines": { "node": ">=22.19.0" @@ -13156,17 +13156,17 @@ "@types/node": "^20.1.0", "@types/tar": "^7.0.87", "@types/yauzl": "^2.10.3", - "@wdio/cli": "^10.0.0", - "@wdio/globals": "^10.0.0", + "@wdio/cli": "^10.0.1", + "@wdio/globals": "^10.0.1", "@wdio/logger": "^10.0.0", - "@wdio/reporter": "^10.0.0", - "@wdio/types": "^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": "^10.0.0" + "webdriverio": "^10.0.1" }, "engines": { "node": ">=18.20.0" diff --git a/packages/browserstack-service/package.json b/packages/browserstack-service/package.json index 67671768..b3c9d678 100644 --- a/packages/browserstack-service/package.json +++ b/packages/browserstack-service/package.json @@ -85,17 +85,17 @@ "@types/node": "^20.1.0", "@types/tar": "^7.0.87", "@types/yauzl": "^2.10.3", - "@wdio/cli": "^10.0.0", - "@wdio/globals": "^10.0.0", + "@wdio/cli": "^10.0.1", + "@wdio/globals": "^10.0.1", "@wdio/logger": "^10.0.0", - "@wdio/reporter": "^10.0.0", - "@wdio/types": "^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": "^10.0.0" + "webdriverio": "^10.0.1" }, "publishConfig": { "access": "public"