diff --git a/package-lock.json b/package-lock.json index 53c0f780..ef84a33f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5979,18 +5979,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@sindresorhus/is": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz", - "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, "node_modules/@sindresorhus/merge-streams": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", @@ -6044,18 +6032,6 @@ "node": ">=4" } }, - "node_modules/@szmarczak/http-timer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz", - "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==", - "dev": true, - "dependencies": { - "defer-to-connect": "^2.0.1" - }, - "engines": { - "node": ">=14.16" - } - }, "node_modules/@testim/chrome-version": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@testim/chrome-version/-/chrome-version-1.1.4.tgz", @@ -9145,7 +9121,8 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" }, "node_modules/available-typed-arrays": { "version": "1.0.7", @@ -9183,14 +9160,23 @@ "license": "MPL-2.0" }, "node_modules/axios": { - "version": "1.12.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.14.0.tgz", + "integrity": "sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.15.11", + "form-data": "^4.0.5", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/axios/node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" } }, "node_modules/b4a": { @@ -9969,45 +9955,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/cacheable-lookup": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", - "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", - "dev": true, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/cacheable-request": { - "version": "10.2.13", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.13.tgz", - "integrity": "sha512-3SD4rrMu1msNGEtNSt8Od6enwdo//U9s4ykmXfA2TD58kcLkCobtCDiby7kNyj7a/Q7lz/mAesAFI54rTdnvBA==", - "dev": true, - "dependencies": { - "@types/http-cache-semantics": "^4.0.1", - "get-stream": "^6.0.1", - "http-cache-semantics": "^4.1.1", - "keyv": "^4.5.3", - "mimic-response": "^4.0.0", - "normalize-url": "^8.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - } - }, - "node_modules/cacheable-request/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/caching-transform": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/caching-transform/-/caching-transform-4.0.0.tgz", @@ -10406,52 +10353,70 @@ } }, "node_modules/chromedriver": { - "version": "127.0.2", - "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-127.0.2.tgz", - "integrity": "sha512-mYfJ/8FqzsdFOs2rPiAI4y0suFnv78cRnzZK0MHdSfSIDeRPbqZz0rNX4lrXt14hXc9vqXa+a8cMxlrhWtXKSQ==", + "version": "146.0.6", + "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-146.0.6.tgz", + "integrity": "sha512-FIRi3hy0nRiyirK03etVXEpYTIodevFcvTBAM5ZCq+pX3w31jLm6JE8BVW1ypAVLvSp6HJDvboCcdgUroS3miw==", "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { "@testim/chrome-version": "^1.1.4", - "axios": "^1.6.7", + "axios": "^1.13.5", "compare-versions": "^6.1.0", "extract-zip": "^2.0.1", - "proxy-agent": "^6.4.0", - "proxy-from-env": "^1.1.0", + "proxy-agent": "^6.5.0", + "proxy-from-env": "^2.0.0", "tcp-port-used": "^1.0.2" }, "bin": { "chromedriver": "bin/chromedriver" }, "engines": { - "node": ">=18" + "node": ">=20" } }, "node_modules/chromedriver/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/chromedriver/node_modules/proxy-agent": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.4.0.tgz", - "integrity": "sha512-u0piLU+nCOHMgGjRbimiXmA9kM/L9EHh3zL81xCdp7m+Y2pHIsnmbdDoEDoAz5geaonNR6q6+yOPQs6n4T6sBQ==", + "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.0.2", + "agent-base": "^7.1.2", "debug": "^4.3.4", "http-proxy-agent": "^7.0.1", - "https-proxy-agent": "^7.0.3", + "https-proxy-agent": "^7.0.6", "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.1", + "pac-proxy-agent": "^7.1.0", "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.2" + "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" } }, + "node_modules/chromedriver/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/chromedriver/node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/chromium-bidi": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz", @@ -10787,6 +10752,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -12271,6 +12237,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -14457,15 +14424,16 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", + "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], + "license": "MIT", "engines": { "node": ">=4.0" }, @@ -14501,9 +14469,9 @@ } }, "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -14516,15 +14484,6 @@ "node": ">= 6" } }, - "node_modules/form-data-encoder": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz", - "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==", - "dev": true, - "engines": { - "node": ">= 14.17" - } - }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -15340,43 +15299,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/got": { - "version": "12.6.1", - "resolved": "https://registry.npmjs.org/got/-/got-12.6.1.tgz", - "integrity": "sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==", - "dev": true, - "dependencies": { - "@sindresorhus/is": "^5.2.0", - "@szmarczak/http-timer": "^5.0.1", - "cacheable-lookup": "^7.0.0", - "cacheable-request": "^10.2.8", - "decompress-response": "^6.0.0", - "form-data-encoder": "^2.1.2", - "get-stream": "^6.0.1", - "http2-wrapper": "^2.1.10", - "lowercase-keys": "^3.0.0", - "p-cancelable": "^3.0.0", - "responselike": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/got?sponsor=1" - } - }, - "node_modules/got/node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -15707,31 +15629,6 @@ "node": ">=12" } }, - "node_modules/http2-wrapper": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.0.tgz", - "integrity": "sha512-kZB0wxMo0sh1PehyjJUWRFEd99KC5TLjZ2cULC4f9iqJBAmKQQXEICjxl5iPJRwP40dpeHFqqhm7tYCvODpqpQ==", - "dev": true, - "dependencies": { - "quick-lru": "^5.1.1", - "resolve-alpn": "^1.2.0" - }, - "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/http2-wrapper/node_modules/quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", @@ -17937,18 +17834,6 @@ "node": ">=0.10.0" } }, - "node_modules/ky": { - "version": "0.33.3", - "resolved": "https://registry.npmjs.org/ky/-/ky-0.33.3.tgz", - "integrity": "sha512-CasD9OCEQSFIam2U8efFK81Yeg8vNMTBUqtMOHlrcWQHqUX3HeCl9Dr31u4toV7emlH8Mymk5+9p0lL6mKb/Xw==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sindresorhus/ky?sponsor=1" - } - }, "node_modules/lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", @@ -18987,18 +18872,6 @@ "get-func-name": "^2.0.0" } }, - "node_modules/lowercase-keys": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", - "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -19402,6 +19275,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -19410,6 +19284,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -19438,18 +19313,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/mimic-response": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", - "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -20194,18 +20057,6 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-url": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.0.tgz", - "integrity": "sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/npm-bundled": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-4.0.0.tgz", @@ -21092,15 +20943,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-cancelable": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz", - "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==", - "dev": true, - "engines": { - "node": ">=12.20" - } - }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", @@ -23141,21 +22983,6 @@ "node": ">=10" } }, - "node_modules/responselike": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz", - "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==", - "dev": true, - "dependencies": { - "lowercase-keys": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/resq": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/resq/-/resq-1.11.0.tgz", @@ -27518,7 +27345,7 @@ "dependencies": { "@axe-core/webdriverjs": "^4.11.1", "axe-core": "~4.11.1", - "chromedriver": "*", + "chromedriver": "latest", "colors": "^1.4.0", "commander": "^9.4.1", "dotenv": "^17.2.2", @@ -27666,6 +27493,7 @@ "async-listen": "^3.0.1", "axe-test-fixtures": "github:dequelabs/axe-test-fixtures#v1", "chai": "^4.3.6", + "chromedriver": "^146", "cross-dirname": "^0.1.0", "delay": "^5.0.0", "devtools": "^8.27.2", @@ -27675,193 +27503,141 @@ "rimraf": "^6.0.1", "source-map-support": "^0.5.21", "tsup": "^8.0.1", - "webdriverio": "^8.8.2" + "webdriverio": "^9.27.0" }, "peerDependencies": { "webdriverio": "^5 || ^6 || ^7 || ^8 || ^9" } }, - "packages/webdriverio/node_modules/@wdio/config": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@wdio/config/-/config-8.46.0.tgz", - "integrity": "sha512-WrNPCqm22vuNimGJc8UCc6duEcvOy2foY5I8mv2AUaoTtvCZOfVGRrFnPreypOKVdZChubFCaWrKVNqjgMK5RA==", + "packages/webdriverio/node_modules/@puppeteer/browsers": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.0.tgz", + "integrity": "sha512-46BZJYJjc/WwmKjsvDFykHtXrtomsCIrwYQPOP7VfMJoZY2bsDF9oROBABR3paDjDcmkUye1Pb1BqdcdiipaWA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@wdio/logger": "8.38.0", - "@wdio/types": "8.41.0", - "@wdio/utils": "8.46.0", - "decamelize": "^6.0.0", - "deepmerge-ts": "^5.0.0", - "glob": "^10.2.2", - "import-meta-resolve": "^4.0.0" + "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" + }, + "bin": { + "browsers": "lib/cjs/main-cli.js" }, "engines": { - "node": "^16.13 || >=18" + "node": ">=18" } }, - "packages/webdriverio/node_modules/@wdio/logger": { - "version": "8.38.0", - "resolved": "https://registry.npmjs.org/@wdio/logger/-/logger-8.38.0.tgz", - "integrity": "sha512-kcHL86RmNbcQP+Gq/vQUGlArfU6IIcbbnNp32rRIraitomZow+iEoc519rdQmSVusDozMS5DZthkgDdxK+vz6Q==", + "packages/webdriverio/node_modules/@types/node": { + "version": "20.19.37", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", + "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", "dev": true, "license": "MIT", "dependencies": { - "chalk": "^5.1.2", - "loglevel": "^1.6.0", - "loglevel-plugin-prefix": "^0.8.4", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": "^16.13 || >=18" + "undici-types": "~6.21.0" } }, - "packages/webdriverio/node_modules/@wdio/protocols": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-8.44.0.tgz", - "integrity": "sha512-Do+AW3xuDUHWkrX++LeMBSrX2yRILlDqunRHPMv4adGFEA45m7r4WP8wGCDb+chrHGhXq5TwB9Ne4J7x1dHGng==", - "dev": true, - "license": "MIT" - }, - "packages/webdriverio/node_modules/@wdio/repl": { - "version": "8.40.3", - "resolved": "https://registry.npmjs.org/@wdio/repl/-/repl-8.40.3.tgz", - "integrity": "sha512-mWEiBbaC7CgxvSd2/ozpbZWebnRIc8KRu/J81Hlw/txUWio27S7IpXBlZGVvhEsNzq0+cuxB/8gDkkXvMPbesw==", + "packages/webdriverio/node_modules/@wdio/config": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@wdio/config/-/config-9.27.0.tgz", + "integrity": "sha512-9y8z7ugIbU6ycKrA2SqCpKh1/hobut2rDq9CLt/BNVzSlebBBVOTMiAt1XroZzcPnA7/ZqpbkpOsbpPUaAQuNQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "^22.2.0" + "@wdio/logger": "9.18.0", + "@wdio/types": "9.27.0", + "@wdio/utils": "9.27.0", + "deepmerge-ts": "^7.0.3", + "glob": "^10.2.2", + "import-meta-resolve": "^4.0.0", + "jiti": "^2.6.1" }, "engines": { - "node": "^16.13 || >=18" - } - }, - "packages/webdriverio/node_modules/@wdio/repl/node_modules/@types/node": { - "version": "22.19.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", - "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" + "node": ">=18.20.0" } }, - "packages/webdriverio/node_modules/@wdio/repl/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "packages/webdriverio/node_modules/@wdio/protocols": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@wdio/protocols/-/protocols-9.27.0.tgz", + "integrity": "sha512-rIk69BsY1+6uU2PEN5FiRpI6K7HJ86YHzZRFBe4iRzKXQgGNk1zWzbdVJIuNFoOWsnmYUkK42KSSOT4Le6EmiQ==", "dev": true, "license": "MIT" }, "packages/webdriverio/node_modules/@wdio/types": { - "version": "8.41.0", - "resolved": "https://registry.npmjs.org/@wdio/types/-/types-8.41.0.tgz", - "integrity": "sha512-t4NaNTvJZci3Xv/yUZPH4eTL0hxrVTf5wdwNnYIBrzMnlRDbNefjQ0P7FM7ZjQCLaH92AEH6t/XanUId7Webug==", + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@wdio/types/-/types-9.27.0.tgz", + "integrity": "sha512-DQJ+OdRBqUBcQ30DN2Z651hEVh3OoxnlDUSRqlWy9An2AY6v9rYWTj825B6zsj5pLLEToYO1tfwWq0ab183pXg==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "^22.2.0" + "@types/node": "^20.1.0" }, "engines": { - "node": "^16.13 || >=18" + "node": ">=18.20.0" } }, - "packages/webdriverio/node_modules/@wdio/types/node_modules/@types/node": { - "version": "22.19.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", - "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", + "packages/webdriverio/node_modules/@wdio/utils": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-9.27.0.tgz", + "integrity": "sha512-fUasd5OKJTy2seJfWnYZ9xlxTtY0p/Kyeuh7Tbb8kcofBqmBi2fTvM3sfZlo1tGQX9yCh+IS2N7hlfyFMmuZ+w==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.21.0" - } - }, - "packages/webdriverio/node_modules/@wdio/types/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "packages/webdriverio/node_modules/@wdio/utils": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/@wdio/utils/-/utils-8.46.0.tgz", - "integrity": "sha512-C94kJjZhEfPUNbOA69BQr1SgziQYgjNXK8S1GJXQKuwxN/24PQkYCzeBqXstfxyTXyOwoQCcEZAQ/qJccboufQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@puppeteer/browsers": "^1.6.0", - "@wdio/logger": "8.38.0", - "@wdio/types": "8.41.0", + "@puppeteer/browsers": "^2.2.0", + "@wdio/logger": "9.18.0", + "@wdio/types": "9.27.0", "decamelize": "^6.0.0", - "deepmerge-ts": "^5.1.0", - "edgedriver": "^5.5.0", - "geckodriver": "~4.2.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.1.0", - "safaridriver": "^0.1.0", + "locate-app": "^2.2.24", + "mitt": "^3.0.1", + "safaridriver": "^1.0.0", "split2": "^4.2.0", - "wait-port": "^1.0.4" + "wait-port": "^1.1.0" }, "engines": { - "node": "^16.13 || >=18" - } - }, - "packages/webdriverio/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": ">=18.20.0" } }, "packages/webdriverio/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", + "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, - "packages/webdriverio/node_modules/chalk": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz", - "integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==", + "packages/webdriverio/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "packages/webdriverio/node_modules/chromium-bidi": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.5.8.tgz", - "integrity": "sha512-blqh+1cEQbHBKmok3rVJkBlBxt9beKBgOsxbFgs7UJcoVbbeZ+K7+6liAsjgpc8l1Xd55cQUy14fXZdGSb4zIw==", - "dev": true, - "license": "Apache-2.0", "dependencies": { - "mitt": "3.0.1", - "urlpattern-polyfill": "10.0.0" + "ms": "^2.1.3" }, - "peerDependencies": { - "devtools-protocol": "*" + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "packages/webdriverio/node_modules/decamelize": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.0.tgz", - "integrity": "sha512-Fv96DCsdOgB6mdGl67MT5JaTNKRzrzill5OH5s8bjYJXVlcXyPYGyPsUkWyGV5p1TXI5esYIYMMeDJL0hEIwaA==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-6.0.1.tgz", + "integrity": "sha512-G7Cqgaelq68XHJNGlZ7lrNQyhZGsFqpwtGFexqUv4IQdjKoSYF7ipZ9UuTJZUSQXFj/XaoBLuEVIVqr8EJngEQ==", "dev": true, "license": "MIT", "engines": { @@ -27871,41 +27647,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "packages/webdriverio/node_modules/devtools-protocol": { - "version": "0.0.1400418", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1400418.tgz", - "integrity": "sha512-U8j75zDOXF8IP3o0Cgb7K4tFA9uUHEOru2Wx64+EUqL4LNOh9dRe1i8WKR1k3mSpjcCe3aIkTDvEwq0YkI4hfw==", + "packages/webdriverio/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==", "dev": true, - "license": "BSD-3-Clause" + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } }, - "packages/webdriverio/node_modules/geckodriver": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.2.1.tgz", - "integrity": "sha512-4m/CRk0OI8MaANRuFIahvOxYTSjlNAO2p9JmE14zxueknq6cdtB5M9UGRQ8R9aMV0bLGNVHHDnDXmoXdOwJfWg==", + "packages/webdriverio/node_modules/edgedriver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/edgedriver/-/edgedriver-6.3.0.tgz", + "integrity": "sha512-ggEQL+oEyIcM4nP2QC3AtCQ04o4kDNefRM3hja0odvlPSnsaxiruMxEZ93v3gDCKWYW6BXUr51PPradb+3nffw==", "dev": true, "hasInstallScript": true, - "license": "MPL-2.0", + "license": "MIT", "dependencies": { - "@wdio/logger": "^8.11.0", - "decamelize": "^6.0.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.1", - "node-fetch": "^3.3.1", - "tar-fs": "^3.0.4", - "unzipper": "^0.10.14", - "which": "^4.0.0" + "@wdio/logger": "^9.18.0", + "@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" }, "bin": { - "geckodriver": "bin/geckodriver.js" + "edgedriver": "bin/edgedriver.js" }, "engines": { - "node": "^16.13 || >=18 || >=20" + "node": ">=20.0.0" } }, "packages/webdriverio/node_modules/get-port": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz", - "integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.2.0.tgz", + "integrity": "sha512-afP4W205ONCuMoPBqcR6PSXnzX35KTcJygfJfcp+QY+uwm3p20p1YczWXhlICIzGMCxYBQcySEcOgsJcrkyobg==", "dev": true, "license": "MIT", "engines": { @@ -27916,9 +27695,10 @@ } }, "packages/webdriverio/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "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", "dev": true, "license": "ISC", "dependencies": { @@ -27950,13 +27730,13 @@ } }, "packages/webdriverio/node_modules/isexe": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=16" + "node": ">=20" } }, "packages/webdriverio/node_modules/jackspeak": { @@ -27975,14 +27755,24 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "packages/webdriverio/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==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "packages/webdriverio/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -27991,45 +27781,41 @@ "url": "https://github.com/sponsors/isaacs" } }, - "packages/webdriverio/node_modules/puppeteer-core": { - "version": "21.11.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-21.11.0.tgz", - "integrity": "sha512-ArbnyA3U5SGHokEvkfWjW+O8hOxV1RSJxOgriX/3A4xZRqixt9ZFHD0yPgZQF05Qj0oAqi8H/7stDorjoHY90Q==", + "packages/webdriverio/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "license": "Apache-2.0", + "license": "MIT" + }, + "packages/webdriverio/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==", + "dev": true, + "license": "MIT", "dependencies": { - "@puppeteer/browsers": "1.9.1", - "chromium-bidi": "0.5.8", - "cross-fetch": "4.0.0", - "debug": "4.3.4", - "devtools-protocol": "0.0.1232444", - "ws": "8.16.0" + "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": ">=16.13.2" + "node": ">= 14" } }, - "packages/webdriverio/node_modules/puppeteer-core/node_modules/devtools-protocol": { - "version": "0.0.1232444", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1232444.tgz", - "integrity": "sha512-pM27vqEfxSxRkTMnF+XCmxSEb6duO5R+t8A9DEEJgy4Wz2RVanje2mmj99B6A3zv2r/qGfYlOvYznUhuokizmg==", - "dev": true, - "license": "BSD-3-Clause" - }, - "packages/webdriverio/node_modules/serialize-error": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-11.0.3.tgz", - "integrity": "sha512-2G2y++21dhj2R7iHAdd0FIzjGwuKZld+7Pl/bTU6YIkrC2ZMbVUjm+luj6A6V34Rv9XfKJDKpTWu9W4Gse1D9g==", + "packages/webdriverio/node_modules/safaridriver": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safaridriver/-/safaridriver-1.0.1.tgz", + "integrity": "sha512-jkg4434cYgtrIF2AeY/X0Wmd2W73cK5qIEFE3hDrrQenJH/2SDJIXGvPAigfvQTcE9+H31zkiNHbUqcihEiMRA==", "dev": true, "license": "MIT", - "dependencies": { - "type-fest": "^2.12.2" - }, "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=18.0.0" } }, "packages/webdriverio/node_modules/split2": { @@ -28042,173 +27828,95 @@ "node": ">= 10.x" } }, - "packages/webdriverio/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "packages/webdriverio/node_modules/type-fest": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", - "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "packages/webdriverio/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, "packages/webdriverio/node_modules/webdriver": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-8.46.0.tgz", - "integrity": "sha512-ucb+ow6QHTBBDAdpV1AAKPY+un2cv23QU/rsSJBmuDZi8lZc5NluWz16qVVbdD1+Hn45PXfpxQcBaAkavStORA==", + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.27.0.tgz", + "integrity": "sha512-w07ThZND48SIr0b4S7eFougYUyclmoUwdmju8yXvEJiXYjDjeYUpl8wZrYPEYRBylxpSx+sBHfEUBrPQkcTTRQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "^22.2.0", + "@types/node": "^20.1.0", "@types/ws": "^8.5.3", - "@wdio/config": "8.46.0", - "@wdio/logger": "8.38.0", - "@wdio/protocols": "8.44.0", - "@wdio/types": "8.41.0", - "@wdio/utils": "8.46.0", - "deepmerge-ts": "^5.1.0", - "got": "^12.6.1", - "ky": "^0.33.0", + "@wdio/config": "9.27.0", + "@wdio/logger": "9.18.0", + "@wdio/protocols": "9.27.0", + "@wdio/types": "9.27.0", + "@wdio/utils": "9.27.0", + "deepmerge-ts": "^7.0.3", + "https-proxy-agent": "^7.0.6", + "undici": "^6.21.3", "ws": "^8.8.0" }, "engines": { - "node": "^16.13 || >=18" - } - }, - "packages/webdriverio/node_modules/webdriver/node_modules/@types/node": { - "version": "22.19.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", - "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" + "node": ">=18.20.0" } }, - "packages/webdriverio/node_modules/webdriver/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, "packages/webdriverio/node_modules/webdriverio": { - "version": "8.46.0", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-8.46.0.tgz", - "integrity": "sha512-SyrSVpygEdPzvgpapVZRQCy8XIOecadp56bPQewpfSfo9ypB6wdOUkx13NBu2ANDlUAtJX7KaLJpTtywVHNlVw==", + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.27.0.tgz", + "integrity": "sha512-Y4FbMf4bKBXpPB0lYpglzQ2GfDDe6uojmMZl85uPyrDx18NW7mqN84ZawGoIg/FRvcLaVhcOzc98WOPo725Rag==", "dev": true, "license": "MIT", "dependencies": { - "@types/node": "^22.2.0", - "@wdio/config": "8.46.0", - "@wdio/logger": "8.38.0", - "@wdio/protocols": "8.44.0", - "@wdio/repl": "8.40.3", - "@wdio/types": "8.41.0", - "@wdio/utils": "8.46.0", - "archiver": "^7.0.0", - "aria-query": "^5.0.0", + "@types/node": "^20.11.30", + "@types/sinonjs__fake-timers": "^8.1.5", + "@wdio/config": "9.27.0", + "@wdio/logger": "9.18.0", + "@wdio/protocols": "9.27.0", + "@wdio/repl": "9.16.2", + "@wdio/types": "9.27.0", + "@wdio/utils": "9.27.0", + "archiver": "^7.0.1", + "aria-query": "^5.3.0", + "cheerio": "^1.0.0-rc.12", "css-shorthand-properties": "^1.1.1", "css-value": "^0.0.1", - "devtools-protocol": "^0.0.1400418", - "grapheme-splitter": "^1.0.2", - "import-meta-resolve": "^4.0.0", + "grapheme-splitter": "^1.0.4", + "htmlfy": "^0.8.1", "is-plain-obj": "^4.1.0", "jszip": "^3.10.1", "lodash.clonedeep": "^4.5.0", "lodash.zip": "^4.2.0", - "minimatch": "^9.0.0", - "puppeteer-core": "^21.11.0", - "query-selector-shadow-dom": "^1.0.0", - "resq": "^1.9.1", + "query-selector-shadow-dom": "^1.0.1", + "resq": "^1.11.0", "rgb2hex": "0.2.5", - "serialize-error": "^11.0.1", - "webdriver": "8.46.0" + "serialize-error": "^12.0.0", + "urlpattern-polyfill": "^10.0.0", + "webdriver": "9.27.0" }, "engines": { - "node": "^16.13 || >=18" + "node": ">=18.20.0" }, "peerDependencies": { - "devtools": "^8.14.0" + "puppeteer-core": ">=22.x || <=24.x" }, "peerDependenciesMeta": { - "devtools": { + "puppeteer-core": { "optional": true } } }, - "packages/webdriverio/node_modules/webdriverio/node_modules/@types/node": { - "version": "22.19.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz", - "integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "packages/webdriverio/node_modules/webdriverio/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, "packages/webdriverio/node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", "dev": true, "license": "ISC", "dependencies": { - "isexe": "^3.1.1" + "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" }, "engines": { - "node": "^16.13.0 || >=18.0.0" - } - }, - "packages/webdriverio/node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } + "node": "^20.17.0 || >=22.9.0" } }, "packages/webdriverjs": { @@ -28251,11 +27959,11 @@ "version": "4.11.1", "license": "ISC", "dependencies": { - "@axe-core/cli": "*", - "@axe-core/playwright": "*", - "@axe-core/puppeteer": "*", - "@axe-core/webdriverio": "*", - "@axe-core/webdriverjs": "*", + "@axe-core/cli": "latest", + "@axe-core/playwright": "latest", + "@axe-core/puppeteer": "latest", + "@axe-core/webdriverio": "latest", + "@axe-core/webdriverjs": "latest", "chai": "^4.3.4", "execa": "^5.1.1", "mocha": "^11.7.5", @@ -29282,7 +28990,7 @@ "@wdio/globals": "^9.17.0", "@wdio/local-runner": "^9.19.2", "@wdio/mocha-framework": "^9.21.0", - "@wdio/spec-reporter": "^9.24.0", + "@wdio/spec-reporter": "^9.19.1", "async-listen": "^3.0.1", "axe-test-fixtures": "github:dequelabs/axe-test-fixtures#v1", "chai": "^4.3.10", diff --git a/packages/webdriverio/package.json b/packages/webdriverio/package.json index 0d6a05ab..68aaac64 100644 --- a/packages/webdriverio/package.json +++ b/packages/webdriverio/package.json @@ -54,6 +54,7 @@ "devDependencies": { "@types/chai": "^4.3.3", "@types/chromedriver": "^81.0.1", + "chromedriver": "^146", "@types/cssesc": "^3.0.0", "@types/express": "^5.0.3", "@types/mocha": "^10.0.6", @@ -70,7 +71,7 @@ "rimraf": "^6.0.1", "source-map-support": "^0.5.21", "tsup": "^8.0.1", - "webdriverio": "^8.8.2" + "webdriverio": "^9.27.0" }, "peerDependencies": { "webdriverio": "^5 || ^6 || ^7 || ^8 || ^9" diff --git a/packages/webdriverio/src/index.ts b/packages/webdriverio/src/index.ts index f2d8e188..b98a2261 100644 --- a/packages/webdriverio/src/index.ts +++ b/packages/webdriverio/src/index.ts @@ -11,6 +11,8 @@ import { axeFinishRun, axeRunLegacy, configureAllowedOrigins, + clientSwitchFrame, + clientSwitchWindow, FRAME_LOAD_TIMEOUT } from './utils'; import { getFilename } from 'cross-dirname'; @@ -36,8 +38,7 @@ async function loadAxePath() { if (typeof require === 'function' && typeof require.resolve === 'function') { axeCorePath = require.resolve('axe-core'); } else { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const { createRequire } = (await import('node:module')) as any; + const { createRequire } = await import('node:module'); // `getFilename` is needed because esm's `import.meta.url` is illegal syntax in cjs const filename = pathToFileURL(getFilename()).toString(); @@ -205,9 +206,23 @@ export default class AxeBuilder { * Injects `axe-core` into all frames. */ private async inject( - browsingContext: WdioElement | null = null + browsingContext: WdioElement | null = null, + browsingContextId: string | null = null ): Promise { - await this.setBrowsingContext(browsingContext); + // Navigate to the target browsing context and capture its BiDi context ID. + // In WDIO v9 BiDi mode, switchFrame returns the browsing context ID string, + // which we use later to safely re-enter this frame after deep injection. + // In Classic WebDriver mode, switchFrame returns undefined and we fall back + // to re-entering via the original element reference. + if (browsingContext !== null) { + const result = await clientSwitchFrame(this.client, browsingContext); + if (typeof result === 'string') { + browsingContextId = result; + } + } else { + await clientSwitchFrame(this.client, null); + } + const runPartialSupported = await axeSourceInject( this.client, this.axeSource @@ -226,11 +241,30 @@ export default class AxeBuilder { for (const iframe of iframes) { try { - if (!(await iframe.isExisting())) { + const exists = await iframe.isExisting(); + if (!exists) { continue; } await this.inject(iframe); - await this.client.switchToParentFrame(); + // After injecting into iframe (and its descendants), navigate back to + // this level. switchFrame(null) reliably resets to the top-level context. + // Then re-enter this frame using its BiDi context ID (WDIO v9 BiDi) or + // its element reference (Classic WebDriver). + // + // We use the context ID rather than the element reference because in WDIO + // v9 BiDi mode, Chrome may assign new document IDs to intermediate frame + // contexts after a deep switchFrame(null). An element's SharedId encodes + // the document ID at query time; if the document ID has since changed, the + // SharedId is stale and Chrome rejects it with "no such node". Passing a + // context ID string instead causes WDIO to re-query fresh element + // references via browsingContextLocateNodes, bypassing the stale-ID issue. + await clientSwitchFrame(this.client, null); + if (browsingContextId !== null && 'switchFrame' in this.client) { + // browsingContextId is only set on v9 BiDi clients, so switchFrame is available. + await this.client.switchFrame(browsingContextId); + } else if (browsingContext !== null) { + await clientSwitchFrame(this.client, browsingContext); + } } catch (error) { logOrRethrowError(error); } @@ -253,7 +287,7 @@ export default class AxeBuilder { // ensure we fail quickly if an iframe cannot be loaded (instead of waiting // the default length of 30 seconds) const { pageLoad } = await this.client.getTimeouts(); - (this.client as WebdriverIO.Browser).setTimeout({ + this.client.setTimeout({ pageLoad: FRAME_LOAD_TIMEOUT }); @@ -261,7 +295,7 @@ export default class AxeBuilder { try { partials = await this.runPartialRecursive(context); } finally { - (this.client as WebdriverIO.Browser).setTimeout({ + this.client.setTimeout({ pageLoad }); } @@ -303,20 +337,6 @@ export default class AxeBuilder { return selector; } - /** - * Set browsing context - when `null` sets top level page as context - * - https://webdriver.io/docs/api/webdriver.html#switchtoframe - */ - private async setBrowsingContext( - id: null | WdioElement | WdioBrowser = null - ): Promise { - if (id) { - await this.client.switchToFrame(id); - } else { - await this.client.switchToParentFrame(); - } - } - /** * Get partial results from the current context and its child frames * @param {ContextObject} context @@ -324,8 +344,12 @@ export default class AxeBuilder { private async runPartialRecursive( context: SerialContextObject, - frameStack: WdioElement[] = [] + frameStack: WdioElement[] = [], + topWindow?: string ): Promise { + if (topWindow === undefined) { + topWindow = await this.client.getWindowHandle(); + } const frameContexts = await axeGetFrameContext(this.client, context); const partials: PartialResults = [ await axeRunPartial(this.client, context, this.option) @@ -335,26 +359,35 @@ export default class AxeBuilder { try { const frame = await this.client.$(frameSelector); assert(frame, `Expect frame of "${frameSelector}" to be defined`); - await this.client.switchToFrame(frame); + await clientSwitchFrame(this.client, frame); await axeSourceInject(this.client, this.script); partials.push( - ...(await this.runPartialRecursive(frameContext, [ - ...frameStack, - frame - ])) + ...(await this.runPartialRecursive( + frameContext, + [...frameStack, frame], + topWindow + )) ); } catch { - const [topWindow] = await this.client.getWindowHandles(); - await this.client.switchToWindow(topWindow); + await clientSwitchWindow(this.client, topWindow); for (const frameElm of frameStack) { - await this.client.switchToFrame(frameElm); + await clientSwitchFrame(this.client, frameElm); } partials.push(null); } } - await this.client.switchToParentFrame(); + // Navigate back to the parent context by switching to the top-level window + // (via getWindowHandles + switchToWindow, which correctly sets the BiDi + // context) then re-traversing the frame stack up to (but not including) + // the last frame. This avoids the WDIO v9 BiDi race condition where + // switchToParentFrame synchronously resets #currentContext before the async + // parent lookup resolves, causing subsequent BiDi calls to run in wrong context. + await clientSwitchWindow(this.client, topWindow); + for (let i = 0; i < frameStack.length - 1; i++) { + await clientSwitchFrame(this.client, frameStack[i]); + } return partials; } @@ -368,8 +401,8 @@ export default class AxeBuilder { ); try { - await client.switchToWindow(newWindow.handle); - await (client as WebdriverIO.Browser).url('data:text/html,'); + await clientSwitchWindow(client, newWindow.handle); + await client.url('data:text/html,'); } catch (error) { throw new Error( `switchToWindow failed. Are you using updated browser drivers? \nDriver reported:\n${ @@ -381,7 +414,7 @@ export default class AxeBuilder { const res = await axeFinishRun(client, axeSource, partials, option); // Cleanup await client.closeWindow(); - await client.switchToWindow(win); + await clientSwitchWindow(client, win); return res; } diff --git a/packages/webdriverio/src/types.ts b/packages/webdriverio/src/types.ts index 560f40fe..92e79b38 100644 --- a/packages/webdriverio/src/types.ts +++ b/packages/webdriverio/src/types.ts @@ -1,35 +1,60 @@ import type { AxeResults, BaseSelector } from 'axe-core'; import * as axe from 'axe-core'; -import { type Browser, type Element } from 'webdriverio'; - -/* - This type allows both webdriverio v8 and <=v7 Browser types - to work in the same codebase. The types are incompatible with - each other, but are compatible with the functions that we use. - Every new feature that we use from the Browser type will need - to be added to the Pick list -*/ -export type WdioBrowser = - | Browser - | Pick< - WebdriverIO.Browser, - | '$$' - | '$' - | 'switchToFrame' - | 'switchToParentFrame' - | 'getWindowHandles' - | 'getWindowHandle' - | 'switchToWindow' - | 'createWindow' - | 'url' - | 'getTimeouts' - | 'setTimeout' - | 'closeWindow' - | 'executeAsync' - | 'execute' - >; - -export type WdioElement = Element | WebdriverIO.Element; + +export interface WdioElement { + isExisting(): Promise; +} + +// Shared methods present in all supported WDIO versions. Every new feature that +// we use from the Browser type will need to be added to the list. +// Hand-written rather than Pick because: +// - WebdriverIO.Browser.$$ returns ChainablePromiseArray, whose awaited type +// doesn't expose .concat(), breaking usage in index.ts. +// - Several picked methods carry a `this: Browser` context constraint that +// TypeScript enforces even through our narrower union type +interface WdioBrowserBase { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + $$(selector: string): any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + $(selector: string): any; + + execute( + script: string | ((...args: unknown[]) => unknown), + ...args: unknown[] + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ): Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + executeAsync(script: string, ...args: unknown[]): Promise; + getTimeouts(): Promise<{ pageLoad?: number }>; + setTimeout(options: { pageLoad?: number }): Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + url(url: string): Promise; + getWindowHandles(): Promise; + getWindowHandle(): Promise; + createWindow(type: 'tab' | 'window'): Promise<{ handle: string }>; + closeWindow(): Promise; +} + +// WDIO v5–v8: frame/window navigation via the legacy API. +interface WdioBrowserLegacy extends WdioBrowserBase { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + switchToFrame(element: any): Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + switchToParentFrame(): Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + switchToWindow(handle: any): Promise; +} + +// WDIO v9: frame/window navigation via the new API. +interface WdioBrowserV9 extends WdioBrowserBase { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + switchFrame(element: any): Promise; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + switchWindow(matcher: any): Promise; +} + +// A WDIO browser object from any supported version (v5–v9). +export type WdioBrowser = WdioBrowserLegacy | WdioBrowserV9; export type CallbackFunction = ( error: string | null, diff --git a/packages/webdriverio/src/utils.ts b/packages/webdriverio/src/utils.ts index a9e37df7..4e7f7d88 100644 --- a/packages/webdriverio/src/utils.ts +++ b/packages/webdriverio/src/utils.ts @@ -8,7 +8,7 @@ import type { SerialSelectorList, SerialContextObject } from 'axe-core'; -import type { WdioBrowser } from './types'; +import type { WdioBrowser, WdioElement } from './types'; export const FRAME_LOAD_TIMEOUT = 1000; @@ -24,7 +24,13 @@ export const isWebdriverClient = (client: WdioBrowser): boolean => { return false; } - if (typeof client.switchToFrame !== 'function') { + // @wdio/globals browser uses proxies for the functions so using `'switchToFrame' in client` doesn't work + // @see https://github.com/webdriverio/webdriverio/blob/main/packages/wdio-globals/src/index.ts + const c = client as unknown as Record; + if ( + typeof c.switchToFrame !== 'function' && + typeof c.switchFrame !== 'function' + ) { return false; } @@ -81,6 +87,39 @@ const promisify = (thenable: Promise): Promise => { }); }; +export async function clientSwitchFrame( + client: WdioBrowser, + id: WdioElement | null +): Promise { + const c = client as unknown as Record; + if (typeof c.switchFrame === 'function') { + // WDIO v9 BiDi: switchFrame accepts elements or null. + // It returns the BiDi browsing context ID, which we capture for safe re-entry. + return (await (c.switchFrame as (id: unknown) => Promise)( + id + )) as string; + } else { + // Classic WebDriver (WDIO v5–v8). + await (c.switchToFrame as (id: unknown) => Promise)(id); + } + return undefined; +} + +export async function clientSwitchWindow( + client: WdioBrowser, + handle: string +): Promise { + const c = client as unknown as Record; + // Prefer switchToWindow (handle-based) over switchWindow (pattern match). + // switchWindow matches by title/URL/name in v8, so passing a window handle + // string fails with "No window found". switchToWindow takes a handle directly. + if (typeof c.switchToWindow === 'function') { + await (c.switchToWindow as (handle: string) => Promise)(handle); + } else if (typeof c.switchWindow === 'function') { + await (c.switchWindow as (handle: string) => Promise)(handle); + } +} + export const axeSourceInject = async ( client: WdioBrowser, axeSource: string @@ -89,7 +128,7 @@ export const axeSourceInject = async ( return promisify( // Had to use executeAsync() because we could not use multiline statements in client.execute() // we were able to return a single boolean in a line but not when assigned to a variable. - (client as WebdriverIO.Browser).executeAsync(` + client.executeAsync(` var callback = arguments[arguments.length - 1]; ${axeSource}; window.axe.configure({ @@ -119,9 +158,23 @@ async function assertFrameReady(client: WdioBrowser): Promise { reject(); }, FRAME_LOAD_TIMEOUT); }); - const executePromise = (client as WebdriverIO.Browser).execute(() => { - return document.readyState === 'complete'; - }); + const isBiDi = + typeof (client as unknown as Record).switchFrame === + 'function'; + const executePromise = isBiDi + ? client.execute(() => { + // In WDIO v9 BiDi mode, executing scripts in a lazy-loaded cross-origin + // iframe succeeds even when the frame hasn't loaded its content yet + // (BiDi bypasses same-origin restrictions). The frame's document will be + // about:blank until the browser actually fetches the remote URL. + // Classic WebDriver throws a cross-origin error in this case, which is + // caught and treated as an untestable frame. We replicate that behavior + // here by also checking that the document isn't still at about:blank. + return ( + document.readyState === 'complete' && document.URL !== 'about:blank' + ); + }) + : client.execute(() => document.readyState === 'complete'); const readyState = await Promise.race([timeoutPromise, executePromise]); assert(readyState); } catch { @@ -135,7 +188,7 @@ export const axeRunPartial = ( options?: RunOptions ): Promise => { return promisify( - (client as WebdriverIO.Browser) + client .executeAsync( ` var callback = arguments[arguments.length - 1]; @@ -158,7 +211,7 @@ export const axeGetFrameContext = ( return promisify( // Had to use executeAsync() because we could not use multiline statements in client.execute() // we were able to return a single boolean in a line but not when assigned to a variable. - (client as WebdriverIO.Browser).executeAsync(` + client.executeAsync(` var callback = arguments[arguments.length - 1]; var context = ${JSON.stringify(context)}; var frameContexts = window.axe.utils.getFrameContexts(context); @@ -174,7 +227,7 @@ export const axeRunLegacy = ( config?: Spec ): Promise => { return promisify( - (client as WebdriverIO.Browser) + client .executeAsync( `var callback = arguments[arguments.length - 1]; var context = ${JSON.stringify(context)} || document; @@ -207,7 +260,7 @@ export const axeFinishRun = ( function chunkResults(result: string): Promise { const chunk = JSON.stringify(result.substring(0, sizeLimit)); return promisify( - (client as WebdriverIO.Browser).execute( + client.execute( ` window.partialResults ??= ''; window.partialResults += ${chunk}; @@ -224,7 +277,7 @@ export const axeFinishRun = ( return chunkResults(partialString) .then(() => { return promisify( - (client as WebdriverIO.Browser).executeAsync( + client.executeAsync( `var callback = arguments[arguments.length - 1]; ${axeSource}; window.axe.configure({ @@ -244,7 +297,7 @@ export const axeFinishRun = ( export const configureAllowedOrigins = (client: WdioBrowser): Promise => { return promisify( - (client as WebdriverIO.Browser).execute(` + client.execute(` window.axe.configure({ allowedOrigins: [''] }) `) ); diff --git a/packages/webdriverio/test/axe-webdriverio.spec.ts b/packages/webdriverio/test/axe-webdriverio.spec.ts index 3f4db5e5..471fbc71 100644 --- a/packages/webdriverio/test/axe-webdriverio.spec.ts +++ b/packages/webdriverio/test/axe-webdriverio.spec.ts @@ -4,62 +4,55 @@ import listen from 'async-listen'; import { assert } from 'chai'; import path from 'path'; import { Server, createServer } from 'http'; -import net from 'net'; import fs from 'fs'; -import delay from 'delay'; import { AxeBuilder } from '../src'; -import { logOrRethrowError } from '../src/utils'; +import { + logOrRethrowError, + clientSwitchFrame, + clientSwitchWindow +} from '../src/utils'; import type { AxeResults, Result } from 'axe-core'; import child_process from 'child_process'; import { ChildProcessWithoutNullStreams } from 'child_process'; import { fixturesPath } from 'axe-test-fixtures'; -import { config } from 'dotenv'; -import os from 'os'; import sinon from 'sinon'; -const HOME_DIR = os.homedir(); -const BDM_CACHE_DIR = path.resolve(HOME_DIR, '.browser-driver-manager'); - -config({ path: path.resolve(BDM_CACHE_DIR, '.env') }); - -const connectToChromeDriver = (port: number): Promise => { - let socket: net.Socket; - return new Promise((resolve, reject) => { - // Give up after 1s - const timer = setTimeout(() => { - socket.destroy(); - reject(new Error('Unable to connect to ChromeDriver')); - }, 1000); - - const connectionListener = (): void => { - clearTimeout(timer); - socket.destroy(); - return resolve(); - }; - - socket = net.createConnection( - { host: 'localhost', port }, - connectionListener - ); - - // Fail on error - socket.once('error', (err: Error) => { - clearTimeout(timer); - socket.destroy(); - return reject(err); - }); - }); -}; +const { + getFreePort, + connectToChromeDriver, + loadBdmEnv +} = require('./testUtils'); + +loadBdmEnv(); + +// devtools protocol was removed in WDIO v9. +// require('webdriverio/package.json') fails when the package uses an exports +// field that doesn't include ./package.json, so walk up from the resolved +// entry point instead (fs.readFileSync bypasses the exports restriction). +const wdioMajorVersion = (() => { + let dir = path.dirname(require.resolve('webdriverio')); + while (dir !== path.dirname(dir)) { + try { + const pkg = JSON.parse( + fs.readFileSync(path.join(dir, 'package.json'), 'utf-8') + ); + if (pkg.name === 'webdriverio') return parseInt(pkg.version, 10); + } catch { + /* continue walking */ + } + dir = path.dirname(dir); + } + return 0; +})(); describe('@axe-core/webdriverio', () => { let port: number; for (const protocol of ['devtools', 'webdriver'] as const) { if (protocol === 'webdriver') { - port = 9515; - let chromedriverProcess: ChildProcessWithoutNullStreams; before(async () => { + port = await getFreePort(); assert( process.env.CHROME_TEST_PATH, 'CHROME_TEST_PATH is not set. Run `npx browser-driver-manager install chrome`' @@ -74,7 +67,6 @@ describe('@axe-core/webdriverio', () => { ]); chromedriverProcess.stdout.pipe(process.stdout); chromedriverProcess.stderr.pipe(process.stderr); - await delay(500); await connectToChromeDriver(port); }); @@ -83,7 +75,13 @@ describe('@axe-core/webdriverio', () => { }); } - describe(`WebdriverIO Async (${protocol} protocol)`, () => { + describe(`WebdriverIO Async (${protocol} protocol)`, function () { + before(function () { + if (protocol === 'devtools' && wdioMajorVersion >= 9) { + this.skip(); + } + }); + let server: Server; let addr: string; let client: WebdriverIO.Browser; @@ -114,7 +112,7 @@ describe('@axe-core/webdriverio', () => { // this removes the unnecessary trailing forward slash addr = (await listen(server)).toString().replace(/\/$/, ''); - const options: webdriverio.RemoteOptions = { + const options: Parameters[0] = { path: '/', automationProtocol: protocol, capabilities: { @@ -147,14 +145,14 @@ describe('@axe-core/webdriverio', () => { describe('AxeBuilder', () => { if (protocol === 'devtools') { it('check to make sure that client is running devtools protocol', () => { - assert.isTrue(client.isDevTools); + assert.isTrue((client as any).isDevTools); }); } if (protocol === 'webdriver') { it('check to make sure that client is running webdriver protocol', () => { // there is no `isWebdriver` option - assert.isUndefined(client.isDevTools); + assert.isUndefined((client as any).isDevTools); }); } @@ -191,10 +189,25 @@ describe('@axe-core/webdriverio', () => { ); }); - it('allows client to be a function (@wdio/globals)', () => { - const client = () => {}; - client.execute = () => {}; - client.switchToFrame = () => {}; + it('does not throw when client is valid (v9 API)', () => { + assert.doesNotThrow( + () => + new AxeBuilder({ + client: { execute() {}, switchFrame() {} } + } as any) + ); + }); + + it('allows client to be an object with proxies (@wdio/globals)', () => { + const target = { + execute() {}, + switchToFrame() {} + }; + const client = new Proxy(target, { + has() { + return false; + } + }); assert.doesNotThrow(() => new AxeBuilder({ client } as any)); }); @@ -1537,4 +1550,55 @@ describe('@axe-core/webdriverio', () => { }); }); } + + describe('clientSwitchFrame', () => { + it('calls switchFrame with an element and returns the context ID on a v9-style client', async () => { + const contextId = 'some-bidi-context-id'; + const stubClient = { switchFrame: sinon.stub().resolves(contextId) }; + const element = {} as any; + const result = await clientSwitchFrame(stubClient as any, element); + assert.isTrue(stubClient.switchFrame.calledOnceWith(element)); + assert.equal(result, contextId); + }); + + it('calls switchFrame with null on a v9-style client', async () => { + const contextId = 'top-level-context-id'; + const stubClient = { switchFrame: sinon.stub().resolves(contextId) }; + const result = await clientSwitchFrame(stubClient as any, null); + assert.isTrue(stubClient.switchFrame.calledOnceWith(null)); + assert.equal(result, contextId); + }); + + it('calls switchToFrame with an element and returns undefined on a v8-style client', async () => { + const stubClient = { switchToFrame: sinon.stub().resolves(undefined) }; + const element = {} as any; + const result = await clientSwitchFrame(stubClient as any, element); + assert.isTrue(stubClient.switchToFrame.calledOnceWith(element)); + assert.isUndefined(result); + }); + }); + + describe('clientSwitchWindow', () => { + it('calls switchToWindow with a handle on a v8-style client', async () => { + const stubClient = { switchToWindow: sinon.stub().resolves() }; + await clientSwitchWindow(stubClient as any, 'window-handle'); + assert.isTrue(stubClient.switchToWindow.calledOnceWith('window-handle')); + }); + + it('calls switchWindow with a handle on a v9-style client', async () => { + const stubClient = { switchWindow: sinon.stub().resolves() }; + await clientSwitchWindow(stubClient as any, 'window-handle'); + assert.isTrue(stubClient.switchWindow.calledOnceWith('window-handle')); + }); + + it('prefers switchToWindow over switchWindow when both are present', async () => { + const stubClient = { + switchToWindow: sinon.stub().resolves(), + switchWindow: sinon.stub().resolves() + }; + await clientSwitchWindow(stubClient as any, 'window-handle'); + assert.isTrue(stubClient.switchToWindow.calledOnceWith('window-handle')); + assert.isFalse(stubClient.switchWindow.called); + }); + }); }); diff --git a/packages/webdriverio/test/esmTest.mjs b/packages/webdriverio/test/esmTest.mjs index e4767b6c..0b8522e8 100644 --- a/packages/webdriverio/test/esmTest.mjs +++ b/packages/webdriverio/test/esmTest.mjs @@ -5,6 +5,10 @@ import * as webdriverio from 'webdriverio'; import { pathToFileURL } from 'url'; import { join } from 'path'; import { fixturesPath } from 'axe-test-fixtures'; +import { spawn } from 'child_process'; +import { getFreePort, connectToChromeDriver } from './testUtils.js'; + +const { default: { path: chromedriverPath } } = await import('chromedriver'); assert(typeof defaultExport === 'function', 'default export is not a function'); assert(typeof AxeBuilder === 'function', 'named export is not a function'); @@ -14,24 +18,40 @@ assert( ); async function integrationTest() { - const path = join(fixturesPath, 'index.html'); - - const options = { - automationProtocol: 'devtools', - path: '/', - capabilities: { - browserName: 'chrome', - 'goog:chromeOptions': { - args: ['--headless'] - } - }, - logLevel: 'error' - }; - const client = await webdriverio.remote(options); - await client.url(pathToFileURL(path).toString()); - - const results = await new defaultExport({ client }).analyze(); - assert(results.violations.length > 0, 'could not find violations'); + const port = await getFreePort(); + + const chromedriverProcess = spawn(chromedriverPath, [ + `--port=${port}` + ], { stdio: 'inherit' }); + + await connectToChromeDriver(port); + + let client; + try { + const options = { + path: '/', + hostname: 'localhost', + port, + capabilities: { + browserName: 'chrome', + 'goog:chromeOptions': { + args: ['--headless', '--no-sandbox'] + } + }, + logLevel: 'error' + }; + + client = await webdriverio.remote(options); + await client.url(pathToFileURL(join(fixturesPath, 'index.html')).toString()); + + const results = await new defaultExport({ client }).analyze(); + assert(results.violations.length > 0, 'could not find violations'); + } finally { + await client?.deleteSession(); + chromedriverProcess.kill(); + } + process.exit(0); } + integrationTest(); diff --git a/packages/webdriverio/test/testUtils.js b/packages/webdriverio/test/testUtils.js new file mode 100644 index 00000000..47e5913f --- /dev/null +++ b/packages/webdriverio/test/testUtils.js @@ -0,0 +1,55 @@ +'use strict'; + +const net = require('net'); +const path = require('path'); +const os = require('os'); +const { config } = require('dotenv'); + +const getFreePort = () => { + return new Promise((resolve, reject) => { + const server = net.createServer(); + server.listen(0, '127.0.0.1', () => { + const { port } = server.address(); + server.close(err => { + if (err) reject(err); + else resolve(port); + }); + }); + server.once('error', reject); + }); +}; + +const connectToChromeDriver = (port, retries = 10, interval = 200) => { + const attempt = () => { + return new Promise((resolve, reject) => { + const socket = net.createConnection({ host: 'localhost', port }, () => { + socket.destroy(); + resolve(); + }); + socket.once('error', err => { + socket.destroy(); + reject(err); + }); + }); + }; + + const retry = remaining => { + return attempt().catch(err => { + if (remaining <= 0) { + throw new Error(`Unable to connect to ChromeDriver: ${err.message}`); + } + return new Promise(resolve => setTimeout(resolve, interval)).then(() => + retry(remaining - 1) + ); + }); + }; + + return retry(retries); +}; + +const loadBdmEnv = () => { + const bdmCacheDir = path.resolve(os.homedir(), '.browser-driver-manager'); + config({ path: path.resolve(bdmCacheDir, '.env') }); +}; + +module.exports = { getFreePort, connectToChromeDriver, loadBdmEnv };