From eb006a65f972b454999945a2395bbf011ba7f734 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Mon, 19 Feb 2024 17:12:05 -0800 Subject: [PATCH 01/27] Test multiple browsers with playwright, global fixture to start and stop http server while testing --- fixtures.mjs | 21 + package-lock.json | 1930 +++------------------------------------------ package.json | 7 +- test.mjs | 22 +- 4 files changed, 145 insertions(+), 1835 deletions(-) create mode 100644 fixtures.mjs diff --git a/fixtures.mjs b/fixtures.mjs new file mode 100644 index 0000000..29c4b3d --- /dev/null +++ b/fixtures.mjs @@ -0,0 +1,21 @@ +import { createServer } from 'http-server'; + +let server; + +const startServer = () => { + server = createServer({ + root: '.' + }) + server.listen('8080', '127.0.0.1'); + return server +} + +export const mochaGlobalSetup = async () => { + server = startServer({port: "8080"}); + console.log('server started at 8080') +} + +export const mochaGlobalTeardown = async () => { + await server.close(); + console.log('server stopped'); +} diff --git a/package-lock.json b/package-lock.json index 076ec19..c3442ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,8 +13,8 @@ "http-server": "^14.1.1" }, "devDependencies": { - "mocha": "^10.2.0", - "puppeteer": "^20.9.0" + "@playwright/test": "^1.41.2", + "mocha": "^10.2.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -26,184 +26,6 @@ "node": ">=0.10.0" } }, - "node_modules/@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -372,108 +194,27 @@ "node": ">= 8" } }, - "node_modules/@puppeteer/browsers": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", - "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", + "node_modules/@playwright/test": { + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.2.tgz", + "integrity": "sha512-qQB9h7KbibJzrDpkXkYvsmiDJK14FULCCZgEcoe2AvFAS64oCirWTwzTlAYEbKaRxWs5TFesE1Na6izMv3HfGg==", "dev": true, "dependencies": { - "debug": "4.3.4", - "extract-zip": "2.0.1", - "progress": "2.0.3", - "proxy-agent": "6.3.0", - "tar-fs": "3.0.4", - "unbzip2-stream": "1.4.3", - "yargs": "17.7.1" + "playwright": "1.41.2" }, "bin": { - "browsers": "lib/cjs/main-cli.js" - }, - "engines": { - "node": ">=16.3.0" - }, - "peerDependencies": { - "typescript": ">= 4.7.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@puppeteer/browsers/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" + "playwright": "cli.js" }, "engines": { - "node": ">=12" - } - }, - "node_modules/@puppeteer/browsers/node_modules/yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@puppeteer/browsers/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" + "node": ">=16" } }, - "node_modules/@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true - }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "peer": true }, - "node_modules/@types/node": { - "version": "20.10.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.3.tgz", - "integrity": "sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==", - "dev": true, - "optional": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "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==", - "dev": true, - "optional": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -501,18 +242,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "dev": true, - "dependencies": { - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -686,18 +415,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "dev": true, - "dependencies": { - "tslib": "^2.0.1" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/async": { "version": "2.6.4", "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", @@ -718,37 +435,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/b4a": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", - "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==", - "dev": true - }, "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==" }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -765,15 +456,6 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "node_modules/basic-ftp": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.3.tgz", - "integrity": "sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g==", - "dev": true, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -810,39 +492,6 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "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==", - "dev": true, - "engines": { - "node": "*" - } - }, "node_modules/call-bind": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", @@ -860,6 +509,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "peer": true, "engines": { "node": ">=6" } @@ -929,18 +579,6 @@ "fsevents": "~2.3.2" } }, - "node_modules/chromium-bidi": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", - "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", - "dev": true, - "dependencies": { - "mitt": "3.0.0" - }, - "peerDependencies": { - "devtools-protocol": "*" - } - }, "node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -981,33 +619,6 @@ "node": ">= 0.4.0" } }, - "node_modules/cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", - "dev": true, - "dependencies": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - } - }, - "node_modules/cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "dev": true, - "dependencies": { - "node-fetch": "^2.6.12" - } - }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1022,15 +633,6 @@ "node": ">= 8" } }, - "node_modules/data-uri-to-buffer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz", - "integrity": "sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==", - "dev": true, - "engines": { - "node": ">= 14" - } - }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1100,26 +702,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", - "dev": true, - "dependencies": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/devtools-protocol": { - "version": "0.0.1147663", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", - "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", - "dev": true - }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -1147,24 +729,6 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, "node_modules/es-abstract": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", @@ -1278,27 +842,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, "node_modules/eslint": { "version": "8.55.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", @@ -1561,19 +1104,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -1602,6 +1132,7 @@ "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "peer": true, "engines": { "node": ">=4.0" } @@ -1610,6 +1141,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -1619,38 +1151,12 @@ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, - "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==", - "dev": true, - "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/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==", "peer": true }, - "node_modules/fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true - }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1672,15 +1178,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==", - "dev": true, - "dependencies": { - "pend": "~1.2.0" - } - }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -1777,20 +1274,6 @@ "is-callable": "^1.1.3" } }, - "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1868,21 +1351,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -1899,21 +1367,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/get-uri": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.2.tgz", - "integrity": "sha512-5KLucCJobh8vBY1K07EFV4+cPZH3mrV9YeAruUseCQKHB58SGjjT2l9/eA9LD082IiuMjSlFJEcdJ27TXvbZNw==", - "dev": true, - "dependencies": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.0", - "debug": "^4.3.4", - "fs-extra": "^8.1.0" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -2006,12 +1459,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "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 - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -2126,19 +1573,6 @@ "node": ">=8.0.0" } }, - "node_modules/http-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", - "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", - "dev": true, - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/http-server": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", @@ -2165,19 +1599,6 @@ "node": ">=12" } }, - "node_modules/https-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", - "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", - "dev": true, - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -2189,26 +1610,6 @@ "node": ">=0.10.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, "node_modules/ignore": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", @@ -2222,6 +1623,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "peer": true, "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -2270,12 +1672,6 @@ "node": ">= 0.4" } }, - "node_modules/ip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", - "dev": true - }, "node_modules/is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -2290,12 +1686,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, "node_modules/is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -2566,12 +1956,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "peer": true }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -2589,12 +1973,6 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "peer": true }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2619,15 +1997,6 @@ "json5": "lib/cli.js" } }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2650,12 +2019,6 @@ "node": ">= 0.8.0" } }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2697,15 +2060,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "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, - "engines": { - "node": ">=12" - } - }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -2737,12 +2091,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mitt": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", - "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==", - "dev": true - }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -2754,12 +2102,6 @@ "mkdirp": "bin/cmd.js" } }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, "node_modules/mocha": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", @@ -2823,35 +2165,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "peer": true }, - "node_modules/netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "dev": true, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3000,46 +2313,14 @@ "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pac-proxy-agent": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", - "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", - "dev": true, - "dependencies": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", - "pac-resolver": "^7.0.0", - "socks-proxy-agent": "^8.0.2" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/pac-resolver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.0.tgz", - "integrity": "sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg==", - "dev": true, - "dependencies": { - "degenerator": "^5.0.0", - "ip": "^1.1.8", - "netmask": "^2.0.2" - }, - "engines": { - "node": ">= 14" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "peer": true, "dependencies": { "callsites": "^3.0.0" }, @@ -3047,24 +2328,6 @@ "node": ">=6" } }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3096,21 +2359,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "peer": true }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true - }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -3123,6 +2371,50 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.2.tgz", + "integrity": "sha512-v0bOa6H2GJChDL8pAeLa/LZC4feoAMbSQm1/jF/ySsWWoaNItvrMP7GEkvEEFyCTUYKMxjQKaTSg5up7nR6/8A==", + "dev": true, + "dependencies": { + "playwright-core": "1.41.2" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.2.tgz", + "integrity": "sha512-VaTvwCA4Y8kxEe+kfm2+uUUw5Lubf38RxF7FpBxLPmGe5sdNkSg5e3ChEigaGrX7qdqT3pt2m/98LiyvU2x6CA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/portfinder": { "version": "1.0.32", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", @@ -3153,50 +2445,6 @@ "node": ">= 0.8.0" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/proxy-agent": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", - "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", - "dev": true, - "dependencies": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.0", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.1" - }, - "engines": { - "node": ">= 14" - } - }, - "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==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "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", @@ -3206,46 +2454,6 @@ "node": ">=6" } }, - "node_modules/puppeteer": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-20.9.0.tgz", - "integrity": "sha512-kAglT4VZ9fWEGg3oLc4/de+JcONuEJhlh3J6f5R1TLkrY/EHHIHxWXDOzXvaxQCtedmyVXBwg8M+P8YCO/wZjw==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "@puppeteer/browsers": "1.4.6", - "cosmiconfig": "8.2.0", - "puppeteer-core": "20.9.0" - }, - "engines": { - "node": ">=16.3.0" - } - }, - "node_modules/puppeteer-core": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", - "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", - "dev": true, - "dependencies": { - "@puppeteer/browsers": "1.4.6", - "chromium-bidi": "0.4.16", - "cross-fetch": "4.0.0", - "debug": "4.3.4", - "devtools-protocol": "0.0.1147663", - "ws": "8.13.0" - }, - "engines": { - "node": ">=16.3.0" - }, - "peerDependencies": { - "typescript": ">= 4.7.4" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/qs": { "version": "6.11.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", @@ -3280,12 +2488,6 @@ ], "peer": true }, - "node_modules/queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "dev": true - }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -3359,6 +2561,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "peer": true, "engines": { "node": ">=4" } @@ -3553,70 +2756,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", - "dev": true, - "dependencies": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.13.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", - "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", - "dev": true, - "dependencies": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "socks": "^2.7.1" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/socks/node_modules/ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", - "dev": true - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/streamx": { - "version": "2.15.5", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.5.tgz", - "integrity": "sha512-9thPGMkKC2GctCzyCUjME3yR03x2xNo0GPKGkRw2UMYN+gqWa9uqpyNWhmsNCutU5zHmkUum0LsCRQTXUgUCAg==", - "dev": true, - "dependencies": { - "fast-fifo": "^1.1.0", - "queue-tick": "^1.0.1" - } - }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -3734,40 +2873,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/tar-fs": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", - "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", - "dev": true, - "dependencies": { - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - } - }, - "node_modules/tar-stream": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz", - "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==", - "dev": true, - "dependencies": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "peer": true }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -3780,12 +2891,6 @@ "node": ">=8.0" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -3798,12 +2903,6 @@ "strip-bom": "^3.0.0" } }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3908,23 +3007,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "dev": true, - "dependencies": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true, - "optional": true - }, "node_modules/union": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", @@ -3936,15 +3018,6 @@ "node": ">= 0.8.0" } }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -3959,12 +3032,6 @@ "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true - }, "node_modules/whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", @@ -3976,16 +3043,6 @@ "node": ">=12" } }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4064,27 +3121,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "node_modules/ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "dev": true, - "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_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -4136,16 +3172,6 @@ "node": ">=10" } }, - "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==", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", @@ -4165,149 +3191,6 @@ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", "peer": true }, - "@babel/code-frame": { - "version": "7.23.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", - "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true - }, - "@babel/highlight": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", - "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, "@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -4437,87 +3320,21 @@ "fastq": "^1.6.0" } }, - "@puppeteer/browsers": { - "version": "1.4.6", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-1.4.6.tgz", - "integrity": "sha512-x4BEjr2SjOPowNeiguzjozQbsc6h437ovD/wu+JpaenxVLm3jkgzHY2xOslMTp50HoTvQreMjiexiGQw1sqZlQ==", + "@playwright/test": { + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.2.tgz", + "integrity": "sha512-qQB9h7KbibJzrDpkXkYvsmiDJK14FULCCZgEcoe2AvFAS64oCirWTwzTlAYEbKaRxWs5TFesE1Na6izMv3HfGg==", "dev": true, "requires": { - "debug": "4.3.4", - "extract-zip": "2.0.1", - "progress": "2.0.3", - "proxy-agent": "6.3.0", - "tar-fs": "3.0.4", - "unbzip2-stream": "1.4.3", - "yargs": "17.7.1" - }, - "dependencies": { - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "yargs": { - "version": "17.7.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.1.tgz", - "integrity": "sha512-cwiTb08Xuv5fqF4AovYacTFNxk62th7LKJ6BL9IGUpTJrWoU7/7WdQGTP2SjKf1dUNBGzDd28p/Yfs/GI6JrLw==", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - } + "playwright": "1.41.2" } }, - "@tootallnate/quickjs-emscripten": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", - "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", - "dev": true - }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "peer": true }, - "@types/node": { - "version": "20.10.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.3.tgz", - "integrity": "sha512-XJavIpZqiXID5Yxnxv3RUDKTN5b81ddNC3ecsA0SoFXz/QU8OGBwZGMomiq0zw+uuqbL/krztv/DINAQ/EV4gg==", - "dev": true, - "optional": true, - "requires": { - "undici-types": "~5.26.4" - } - }, - "@types/yauzl": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", - "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", - "dev": true, - "optional": true, - "requires": { - "@types/node": "*" - } - }, "@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -4537,15 +3354,6 @@ "peer": true, "requires": {} }, - "agent-base": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.0.tgz", - "integrity": "sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==", - "dev": true, - "requires": { - "debug": "^4.3.4" - } - }, "ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -4662,18 +3470,9 @@ "call-bind": "^1.0.2", "define-properties": "^1.2.0", "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - } - }, - "ast-types": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", - "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", - "dev": true, - "requires": { - "tslib": "^2.0.1" + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" } }, "async": { @@ -4690,23 +3489,11 @@ "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", "peer": true }, - "b4a": { - "version": "1.6.4", - "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz", - "integrity": "sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==", - "dev": true - }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true - }, "basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", @@ -4722,12 +3509,6 @@ } } }, - "basic-ftp": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.0.3.tgz", - "integrity": "sha512-QHX8HLlncOLpy54mh+k/sWIFd0ThmRqwe9ZjELybGZK+tZ8rUb9VO0saKJUROTbE+KhzDUT7xziGpGrW8Kmd+g==", - "dev": true - }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -4758,22 +3539,6 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, - "buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", - "dev": true - }, "call-bind": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", @@ -4787,7 +3552,8 @@ "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "peer": true }, "camelcase": { "version": "6.3.0", @@ -4830,15 +3596,6 @@ "readdirp": "~3.6.0" } }, - "chromium-bidi": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.4.16.tgz", - "integrity": "sha512-7ZbXdWERxRxSwo3txsBjjmc/NLxqb1Bk30mRb0BMS4YIaiV6zvKZqL/UAH+DdqcDYayDWk2n/y8klkBDODrPvA==", - "dev": true, - "requires": { - "mitt": "3.0.0" - } - }, "cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", @@ -4873,27 +3630,6 @@ "resolved": "https://registry.npmjs.org/corser/-/corser-2.0.1.tgz", "integrity": "sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==" }, - "cosmiconfig": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz", - "integrity": "sha512-3rTMnFJA1tCOPwRxtgF4wd7Ab2qvDbL8jX+3smjIbS4HlZBagTlpERbdN7iAbWlrfxE3M8c27kTwTawQ7st+OQ==", - "dev": true, - "requires": { - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "parse-json": "^5.0.0", - "path-type": "^4.0.0" - } - }, - "cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "dev": true, - "requires": { - "node-fetch": "^2.6.12" - } - }, "cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -4905,12 +3641,6 @@ "which": "^2.0.1" } }, - "data-uri-to-buffer": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz", - "integrity": "sha512-MZd3VlchQkp8rdend6vrx7MmVDJzSNTBvghvKjirLkD+WTChA3KUf0jkE68Q4UyctNqI11zZO9/x2Yx+ub5Cvg==", - "dev": true - }, "debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -4959,23 +3689,6 @@ "object-keys": "^1.1.1" } }, - "degenerator": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", - "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", - "dev": true, - "requires": { - "ast-types": "^0.13.4", - "escodegen": "^2.1.0", - "esprima": "^4.0.1" - } - }, - "devtools-protocol": { - "version": "0.0.1147663", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1147663.tgz", - "integrity": "sha512-hyWmRrexdhbZ1tcJUGpO95ivbRhWXz++F4Ko+n21AY5PNln2ovoJw+8ZMNDTtip+CNFQfrtLVh/w4009dXO/eQ==", - "dev": true - }, "diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -4997,24 +3710,6 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, "es-abstract": { "version": "1.22.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", @@ -5104,18 +3799,6 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, - "escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "source-map": "~0.6.1" - } - }, "eslint": { "version": "8.55.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.55.0.tgz", @@ -5330,12 +4013,6 @@ "eslint-visitor-keys": "^3.4.1" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, "esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -5357,42 +4034,26 @@ "estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "peer": true }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "peer": true }, "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, - "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==", - "dev": true, - "requires": { - "@types/yauzl": "^2.9.1", - "debug": "^4.1.1", - "get-stream": "^5.1.0", - "yauzl": "^2.10.0" - } - }, "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==", "peer": true }, - "fast-fifo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", - "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "dev": true - }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -5414,15 +4075,6 @@ "reusify": "^1.0.4" } }, - "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==", - "dev": true, - "requires": { - "pend": "~1.2.0" - } - }, "file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", @@ -5487,17 +4139,6 @@ "is-callable": "^1.1.3" } }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -5550,15 +4191,6 @@ "hasown": "^2.0.0" } }, - "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==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, "get-symbol-description": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", @@ -5569,18 +4201,6 @@ "get-intrinsic": "^1.1.1" } }, - "get-uri": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.2.tgz", - "integrity": "sha512-5KLucCJobh8vBY1K07EFV4+cPZH3mrV9YeAruUseCQKHB58SGjjT2l9/eA9LD082IiuMjSlFJEcdJ27TXvbZNw==", - "dev": true, - "requires": { - "basic-ftp": "^5.0.2", - "data-uri-to-buffer": "^6.0.0", - "debug": "^4.3.4", - "fs-extra": "^8.1.0" - } - }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -5648,12 +4268,6 @@ "get-intrinsic": "^1.1.3" } }, - "graceful-fs": { - "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 - }, "graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -5729,16 +4343,6 @@ "requires-port": "^1.0.0" } }, - "http-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", - "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", - "dev": true, - "requires": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - } - }, "http-server": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/http-server/-/http-server-14.1.1.tgz", @@ -5759,16 +4363,6 @@ "url-join": "^4.0.1" } }, - "https-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", - "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", - "dev": true, - "requires": { - "agent-base": "^7.0.2", - "debug": "4" - } - }, "iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -5777,12 +4371,6 @@ "safer-buffer": ">= 2.1.2 < 3.0.0" } }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "dev": true - }, "ignore": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", @@ -5793,6 +4381,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "peer": true, "requires": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -5829,12 +4418,6 @@ "side-channel": "^1.0.4" } }, - "ip": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", - "integrity": "sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==", - "dev": true - }, "is-array-buffer": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", @@ -5846,12 +4429,6 @@ "is-typed-array": "^1.1.10" } }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, "is-bigint": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", @@ -6029,12 +4606,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "peer": true }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -6049,12 +4620,6 @@ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "peer": true }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -6076,15 +4641,6 @@ "minimist": "^1.2.0" } }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, "keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -6104,12 +4660,6 @@ "type-check": "~0.4.0" } }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, "locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -6139,12 +4689,6 @@ "is-unicode-supported": "^0.1.0" } }, - "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 - }, "mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -6164,12 +4708,6 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==" }, - "mitt": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.0.tgz", - "integrity": "sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==", - "dev": true - }, "mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", @@ -6178,12 +4716,6 @@ "minimist": "^1.2.6" } }, - "mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "dev": true - }, "mocha": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", @@ -6230,21 +4762,6 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "peer": true }, - "netmask": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", - "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", - "dev": true - }, - "node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dev": true, - "requires": { - "whatwg-url": "^5.0.0" - } - }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -6351,53 +4868,15 @@ "p-limit": "^3.0.2" } }, - "pac-proxy-agent": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz", - "integrity": "sha512-ASV8yU4LLKBAjqIPMbrgtaKIvxQri/yh2OpI+S6hVa9JRkUI3Y3NPFbfngDtY7oFtSMD3w31Xns89mDa3Feo5A==", - "dev": true, - "requires": { - "@tootallnate/quickjs-emscripten": "^0.23.0", - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "get-uri": "^6.0.1", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", - "pac-resolver": "^7.0.0", - "socks-proxy-agent": "^8.0.2" - } - }, - "pac-resolver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.0.tgz", - "integrity": "sha512-Fd9lT9vJbHYRACT8OhCbZBbxr6KRSawSovFpy8nDGshaK99S/EBhVIHp9+crhxrsZOuvLpgL1n23iyPg6Rl2hg==", - "dev": true, - "requires": { - "degenerator": "^5.0.0", - "ip": "^1.1.8", - "netmask": "^2.0.2" - } - }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "peer": true, "requires": { "callsites": "^3.0.0" } }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, "path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -6420,24 +4899,37 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "peer": true }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true - }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, + "playwright": { + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.2.tgz", + "integrity": "sha512-v0bOa6H2GJChDL8pAeLa/LZC4feoAMbSQm1/jF/ySsWWoaNItvrMP7GEkvEEFyCTUYKMxjQKaTSg5up7nR6/8A==", + "dev": true, + "requires": { + "fsevents": "2.3.2", + "playwright-core": "1.41.2" + }, + "dependencies": { + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + } + } + }, + "playwright-core": { + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.2.tgz", + "integrity": "sha512-VaTvwCA4Y8kxEe+kfm2+uUUw5Lubf38RxF7FpBxLPmGe5sdNkSg5e3ChEigaGrX7qdqT3pt2m/98LiyvU2x6CA==", + "dev": true + }, "portfinder": { "version": "1.0.32", "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-1.0.32.tgz", @@ -6464,75 +4956,12 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "peer": true }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "proxy-agent": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.3.0.tgz", - "integrity": "sha512-0LdR757eTj/JfuU7TL2YCuAZnxWXu3tkJbg4Oq3geW/qFNT/32T0sp2HnZ9O0lMR4q3vwAt0+xCA8SR0WAD0og==", - "dev": true, - "requires": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", - "lru-cache": "^7.14.1", - "pac-proxy-agent": "^7.0.0", - "proxy-from-env": "^1.1.0", - "socks-proxy-agent": "^8.0.1" - } - }, - "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==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, "punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "peer": true }, - "puppeteer": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-20.9.0.tgz", - "integrity": "sha512-kAglT4VZ9fWEGg3oLc4/de+JcONuEJhlh3J6f5R1TLkrY/EHHIHxWXDOzXvaxQCtedmyVXBwg8M+P8YCO/wZjw==", - "dev": true, - "requires": { - "@puppeteer/browsers": "1.4.6", - "cosmiconfig": "8.2.0", - "puppeteer-core": "20.9.0" - } - }, - "puppeteer-core": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-20.9.0.tgz", - "integrity": "sha512-H9fYZQzMTRrkboEfPmf7m3CLDN6JvbxXA3qTtS+dFt27tR+CsFHzPsT6pzp6lYL6bJbAPaR0HaPO6uSi+F94Pg==", - "dev": true, - "requires": { - "@puppeteer/browsers": "1.4.6", - "chromium-bidi": "0.4.16", - "cross-fetch": "4.0.0", - "debug": "4.3.4", - "devtools-protocol": "0.0.1147663", - "ws": "8.13.0" - } - }, "qs": { "version": "6.11.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", @@ -6547,12 +4976,6 @@ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", "peer": true }, - "queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "dev": true - }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -6607,7 +5030,8 @@ "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "peer": true }, "reusify": { "version": "1.0.4", @@ -6734,58 +5158,6 @@ "object-inspect": "^1.9.0" } }, - "smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "dev": true - }, - "socks": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz", - "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==", - "dev": true, - "requires": { - "ip": "^2.0.0", - "smart-buffer": "^4.2.0" - }, - "dependencies": { - "ip": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", - "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", - "dev": true - } - } - }, - "socks-proxy-agent": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.2.tgz", - "integrity": "sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==", - "dev": true, - "requires": { - "agent-base": "^7.0.2", - "debug": "^4.3.4", - "socks": "^2.7.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - }, - "streamx": { - "version": "2.15.5", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.5.tgz", - "integrity": "sha512-9thPGMkKC2GctCzyCUjME3yR03x2xNo0GPKGkRw2UMYN+gqWa9uqpyNWhmsNCutU5zHmkUum0LsCRQTXUgUCAg==", - "dev": true, - "requires": { - "fast-fifo": "^1.1.0", - "queue-tick": "^1.0.1" - } - }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -6864,40 +5236,12 @@ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "peer": true }, - "tar-fs": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.4.tgz", - "integrity": "sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w==", - "dev": true, - "requires": { - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^3.1.5" - } - }, - "tar-stream": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.6.tgz", - "integrity": "sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==", - "dev": true, - "requires": { - "b4a": "^1.6.4", - "fast-fifo": "^1.2.0", - "streamx": "^2.15.0" - } - }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "peer": true }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true - }, "to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6907,12 +5251,6 @@ "is-number": "^7.0.0" } }, - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, "tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -6925,12 +5263,6 @@ "strip-bom": "^3.0.0" } }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", - "dev": true - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -7005,23 +5337,6 @@ "which-boxed-primitive": "^1.0.2" } }, - "unbzip2-stream": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", - "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", - "dev": true, - "requires": { - "buffer": "^5.2.1", - "through": "^2.3.8" - } - }, - "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true, - "optional": true - }, "union": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/union/-/union-0.5.0.tgz", @@ -7030,12 +5345,6 @@ "qs": "^6.4.0" } }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -7050,12 +5359,6 @@ "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true - }, "whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", @@ -7064,16 +5367,6 @@ "iconv-lite": "0.6.3" } }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -7131,13 +5424,6 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "ws": { - "version": "8.13.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz", - "integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==", - "dev": true, - "requires": {} - }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -7177,16 +5463,6 @@ "is-plain-obj": "^2.1.0" } }, - "yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", - "dev": true, - "requires": { - "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, "yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index e43c6ee..3a15766 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "main": "thresholdmann.js", "scripts": { "start": "http-server .", - "test": "mocha --exit" + "test": "npm run install:browsers; mocha --require fixtures.mjs --exit", + "install:browsers": "npx playwright install" }, "repository": { "type": "git", @@ -26,8 +27,8 @@ }, "homepage": "https://github.com/neuroanatomy/thresholdmann#readme", "devDependencies": { - "mocha": "^10.2.0", - "puppeteer": "^20.9.0" + "@playwright/test": "^1.41.2", + "mocha": "^10.2.0" }, "dependencies": { "eslint-config-naat": "github:neuroanatomy/eslint-config-naat", diff --git a/test.mjs b/test.mjs index d536cd3..0ca446f 100644 --- a/test.mjs +++ b/test.mjs @@ -1,20 +1,30 @@ import assert from 'assert'; -import puppeteer from 'puppeteer'; +import { chromium, firefox, webkit } from '@playwright/test'; describe('Test Thresholdmann', () => { - let browser, page; + const browsers = [ + chromium, + firefox, + webkit + ] + + browsers.forEach((browser) => { + describe(`With ${browser.name()} browser`, () => { + + let page, context; let sdim; - before(async function () { + before(async function() { // eslint-disable-next-line no-invalid-this this.timeout(5000); - browser = await puppeteer.launch({ headless: 'new' }); + browser = await browser.launch({headless: true}); + context = await browser.newContext() page = await browser.newPage(); // page.on('console', (msg) => { // console.log(`PAGE ${msg.type().toUpperCase()}: ${msg.text()}`); // }); - await page.setViewport({ width: 1200, height: 800 }); + await page.setViewportSize({width: 1200, height: 800}); await page.goto('http://127.0.0.1:8080'); }); @@ -141,5 +151,7 @@ describe('Test Thresholdmann', () => { }); }); }); + }); + }); }); From 05aed09b86afdfc04cff3858b2556bb80c1c4f1a Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Mon, 19 Feb 2024 17:14:50 -0800 Subject: [PATCH 02/27] linting --- test.mjs | 259 +++++++++++++++++++++++++++---------------------------- 1 file changed, 129 insertions(+), 130 deletions(-) diff --git a/test.mjs b/test.mjs index 0ca446f..35e6a4e 100644 --- a/test.mjs +++ b/test.mjs @@ -7,151 +7,150 @@ describe('Test Thresholdmann', () => { chromium, firefox, webkit - ] + ]; browsers.forEach((browser) => { - describe(`With ${browser.name()} browser`, () => { + describe(`With ${browser.name()} browser`, () => { - let page, context; - let sdim; + let page; + let sdim; - before(async function() { - // eslint-disable-next-line no-invalid-this - this.timeout(5000); - browser = await browser.launch({headless: true}); - context = await browser.newContext() - page = await browser.newPage(); - // page.on('console', (msg) => { - // console.log(`PAGE ${msg.type().toUpperCase()}: ${msg.text()}`); - // }); - await page.setViewportSize({width: 1200, height: 800}); - await page.goto('http://127.0.0.1:8080'); - }); - - describe('Unit tests', () => { - describe('Trivial test', () => { - it('should say hey', async () => { - const res = await page.evaluate(() => window.location); - assert.ok(res.origin); + before(async function () { + // eslint-disable-next-line no-invalid-this + this.timeout(5000); + browser = await browser.launch({headless: true}); + page = await browser.newPage(); + // page.on('console', (msg) => { + // console.log(`PAGE ${msg.type().toUpperCase()}: ${msg.text()}`); + // }); + await page.setViewportSize({width: 1200, height: 800}); + await page.goto('http://127.0.0.1:8080'); }); - }); - }); - describe('End to end tests', () => { - describe('Load a file', () => { - it('should display title', async () => { - const title = await page.evaluate(() => document.title); - assert.strictEqual(title, 'Thresholdmann'); - }); - it('should display "Choose..." message', async () => { - const msg = await page.evaluate(() => document.querySelector('.box_input').innerText); - assert.strictEqual(msg, '\nChoose a .nii.gz or a .nii file or drag it here.'); + describe('Unit tests', () => { + describe('Trivial test', () => { + it('should say hey', async () => { + const res = await page.evaluate(() => window.location); + assert.ok(res.origin); + }); + }); }); - it('init with test nifti file', async () => { - const pathString = './img/bear_uchar.nii.gz'; - sdim = await page.evaluate(async (path) => { - await window.initWithPath(path); - return window.globals.mv.mri.s2v.sdim; - }, pathString); - assert.ok(Array.isArray(sdim)); - assert.strictEqual(sdim.length, 3); - }).timeout(5000); - }); + describe('End to end tests', () => { + describe('Load a file', () => { + it('should display title', async () => { + const title = await page.evaluate(() => document.title); + assert.strictEqual(title, 'Thresholdmann'); + }); + it('should display "Choose..." message', async () => { + const msg = await page.evaluate(() => document.querySelector('.box_input').innerText); + assert.strictEqual(msg, '\nChoose a .nii.gz or a .nii file or drag it here.'); + }); + it('init with test nifti file', async () => { + const pathString = './img/bear_uchar.nii.gz'; + sdim = await page.evaluate(async (path) => { + await window.initWithPath(path); - describe('can switch planes', () => { - it('can switch to coronal plane', async () => { - await page.click('.cor-btn'); - const plane = await page.evaluate(() => window.globals.mv.views[0].plane); - assert.strictEqual(plane, 'cor'); - }); - it('can switch to axial plane', async () => { - await page.click('.axi-btn'); - const plane = await page.evaluate(() => window.globals.mv.views[0].plane); - assert.strictEqual(plane, 'axi'); - }); - it('can switch to sagittal plane', async () => { - await page.click('.sag-btn'); - const plane = await page.evaluate(() => window.globals.mv.views[0].plane); - assert.strictEqual(plane, 'sag'); - }); - }); + return window.globals.mv.mri.s2v.sdim; + }, pathString); + assert.ok(Array.isArray(sdim)); + assert.strictEqual(sdim.length, 3); + }).timeout(5000); + }); - describe('can change settings', () => { - it('change threshold direction to down', async () => { - await page.click('#direction .mui[title="SelectDown"]'); - const direction = await page.evaluate(() => window.globals.selectedDirection); - assert.strictEqual(direction, 'SelectDown'); - }); - it('change threshold direction to up', async () => { - await page.click('#direction .mui[title="SelectUp"]'); - const direction = await page.evaluate(() => window.globals.selectedDirection); - assert.strictEqual(direction, 'SelectUp'); - }); - it('switch to threshold value view', async () => { - await page.click('#overlay .mui[title="Threshold Value"]'); - const overlay = await page.evaluate(() => window.globals.selectedOverlay); - assert.strictEqual(overlay, 'Threshold Value'); - }); - it('switch to threshold mask view', async () => { - await page.click('#overlay .mui[title="Threshold Mask"]'); - const overlay = await page.evaluate(() => window.globals.selectedOverlay); - assert.strictEqual(overlay, 'Threshold Mask'); - }); - }); + describe('can switch planes', () => { + it('can switch to coronal plane', async () => { + await page.click('.cor-btn'); + const plane = await page.evaluate(() => window.globals.mv.views[0].plane); + assert.strictEqual(plane, 'cor'); + }); + it('can switch to axial plane', async () => { + await page.click('.axi-btn'); + const plane = await page.evaluate(() => window.globals.mv.views[0].plane); + assert.strictEqual(plane, 'axi'); + }); + it('can switch to sagittal plane', async () => { + await page.click('.sag-btn'); + const plane = await page.evaluate(() => window.globals.mv.views[0].plane); + assert.strictEqual(plane, 'sag'); + }); + }); + + describe('can change settings', () => { + it('change threshold direction to down', async () => { + await page.click('#direction .mui[title="SelectDown"]'); + const direction = await page.evaluate(() => window.globals.selectedDirection); + assert.strictEqual(direction, 'SelectDown'); + }); + it('change threshold direction to up', async () => { + await page.click('#direction .mui[title="SelectUp"]'); + const direction = await page.evaluate(() => window.globals.selectedDirection); + assert.strictEqual(direction, 'SelectUp'); + }); + it('switch to threshold value view', async () => { + await page.click('#overlay .mui[title="Threshold Value"]'); + const overlay = await page.evaluate(() => window.globals.selectedOverlay); + assert.strictEqual(overlay, 'Threshold Value'); + }); + it('switch to threshold mask view', async () => { + await page.click('#overlay .mui[title="Threshold Mask"]'); + const overlay = await page.evaluate(() => window.globals.selectedOverlay); + assert.strictEqual(overlay, 'Threshold Mask'); + }); + }); - describe('can manage points', () => { - let canvasBoundingBox; - let points; - it('check initial point', async () => { - canvasBoundingBox = await page.evaluate(() => { - const { x, y, width, height } = document.querySelector('.viewer').getBoundingClientRect(); + describe('can manage points', () => { + let canvasBoundingBox; + let points; + it('check initial point', async () => { + canvasBoundingBox = await page.evaluate(() => { + const { x, y, width, height } = document.querySelector('.viewer').getBoundingClientRect(); - return { x, y, width, height }; + return { x, y, width, height }; + }); + points = await page.evaluate(() => window.globals.points); + assert.strictEqual(points.length, 1); + // point is in the middle + assert.ok(points[0].every((v, i) => v === Math.floor(sdim[i] / 2))); + }); + it('can add a point', async () => { + await page.click('#tools .mui[title="Add"]'); + const clickX = canvasBoundingBox.x + canvasBoundingBox.width / 3; + const clickY = canvasBoundingBox.y + canvasBoundingBox.height / 3; + await page.mouse.click(clickX, clickY); + points = await page.evaluate(() => window.globals.points); + assert.strictEqual(points.length, 2); + assert.ok(points[0][0] === points[1][0]); + assert.ok(points[0][1] > points[1][1]); + assert.ok(points[0][2] < points[1][2]); + }); + it('can move a point', async () => { + await page.click('#tools .mui[title="Move"]'); + const startX = canvasBoundingBox.x + canvasBoundingBox.width / 3; + const startY = canvasBoundingBox.y + canvasBoundingBox.height / 3; + const endX = canvasBoundingBox.x + canvasBoundingBox.width * 2 / 3; + const endY = canvasBoundingBox.y + canvasBoundingBox.height * 2 / 3; + await page.mouse.move(startX, startY); + await page.mouse.down(); + await page.mouse.move(endX, endY); + await page.mouse.up(); + points = await page.evaluate(() => window.globals.points); + assert.strictEqual(points.length, 2); + assert.ok(points[0][0] === points[1][0]); + assert.ok(points[0][1] < points[1][1]); + assert.ok(points[0][2] > points[1][2]); + }); + it('can remove a point', async () => { + await page.click('#tools .mui[title="Remove"]'); + const clickX = canvasBoundingBox.x + canvasBoundingBox.width * 2 / 3; + const clickY = canvasBoundingBox.y + canvasBoundingBox.height * 2 / 3; + await page.mouse.click(clickX, clickY); + points = await page.evaluate(() => window.globals.points); + assert.strictEqual(points.length, 1); + }); }); - points = await page.evaluate(() => window.globals.points); - assert.strictEqual(points.length, 1); - // point is in the middle - assert.ok(points[0].every((v, i) => v === Math.floor(sdim[i] / 2))); - }); - it('can add a point', async () => { - await page.click('#tools .mui[title="Add"]'); - const clickX = canvasBoundingBox.x + canvasBoundingBox.width / 3; - const clickY = canvasBoundingBox.y + canvasBoundingBox.height / 3; - await page.mouse.click(clickX, clickY); - points = await page.evaluate(() => window.globals.points); - assert.strictEqual(points.length, 2); - assert.ok(points[0][0] === points[1][0]); - assert.ok(points[0][1] > points[1][1]); - assert.ok(points[0][2] < points[1][2]); - }); - it('can move a point', async () => { - await page.click('#tools .mui[title="Move"]'); - const startX = canvasBoundingBox.x + canvasBoundingBox.width / 3; - const startY = canvasBoundingBox.y + canvasBoundingBox.height / 3; - const endX = canvasBoundingBox.x + canvasBoundingBox.width * 2 / 3; - const endY = canvasBoundingBox.y + canvasBoundingBox.height * 2 / 3; - await page.mouse.move(startX, startY); - await page.mouse.down(); - await page.mouse.move(endX, endY); - await page.mouse.up(); - points = await page.evaluate(() => window.globals.points); - assert.strictEqual(points.length, 2); - assert.ok(points[0][0] === points[1][0]); - assert.ok(points[0][1] < points[1][1]); - assert.ok(points[0][2] > points[1][2]); - }); - it('can remove a point', async () => { - await page.click('#tools .mui[title="Remove"]'); - const clickX = canvasBoundingBox.x + canvasBoundingBox.width * 2 / 3; - const clickY = canvasBoundingBox.y + canvasBoundingBox.height * 2 / 3; - await page.mouse.click(clickX, clickY); - points = await page.evaluate(() => window.globals.points); - assert.strictEqual(points.length, 1); }); }); }); - }); - }); }); From a8ffd7f2c7010704287f03a60da3e2950e736e6c Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Mon, 19 Feb 2024 17:38:58 -0800 Subject: [PATCH 03/27] Remove launching server from test action, since test fixtures handle launching http server --- .circleci/config.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 27b30d0..aae0760 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,10 +6,6 @@ jobs: steps: - checkout - run: npm install - - run: - name: Run server in background - command: 'npm start' - background: true - run: name: Run tests command: 'npm test' From 16cec4faa087c0161ee65df9cbed6dd0707fb4e6 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Mon, 19 Feb 2024 19:08:17 -0800 Subject: [PATCH 04/27] remove commented out or duplicate functions :) --- thresholdmann-worker.js | 16 ---------------- thresholdmann.js | 30 ------------------------------ 2 files changed, 46 deletions(-) diff --git a/thresholdmann-worker.js b/thresholdmann-worker.js index 6d1bc16..8be7c85 100644 --- a/thresholdmann-worker.js +++ b/thresholdmann-worker.js @@ -1,19 +1,3 @@ -// const interpolation = (pos, points, values) => { -// let val = 0; -// let totalw = 0; -// for (let k=0; k { const backgroundValue = 255; const wBackground = 0.00001; diff --git a/thresholdmann.js b/thresholdmann.js index a3f442e..f774dc9 100644 --- a/thresholdmann.js +++ b/thresholdmann.js @@ -493,36 +493,6 @@ const saveMask = () => { thresholdJob(); }; -/** Save the selection mask produced by the - * threshold, but in the main thread. - * @deprecated - * @returns {void} - */ -// eslint-disable-next-line no-unused-vars -const saveMaskOLD = () => { - const {mv, interpolate} = globals; - const [dim] = mv.mri; - const data = new Float32Array(dim[0] * dim[1] * dim[2]); - let val; - let i, j, k; - let ijk; - for (i = 0; i < dim[0]; i++) { - for (j = 0; j < dim[1]; j++) { - for (k = 0; k < dim[2]; k++) { - ijk = k * dim[1] * dim[0] + j * dim[0] + i; - val = interpolate([i, j, k]) * mv.maxValue / 255; - - if (mv.mri.data[ijk] <= val) { - data[ijk] = 0; - } else { - data[ijk] = 1; - } - } - } - } - saveNifti(data); -}; - const saveControlPoints = () => { const a = document.createElement('a'); const {points, values} = globals; From 48e940a36169cb619f0be4a8c99634251d826a1a Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Mon, 19 Feb 2024 19:23:57 -0800 Subject: [PATCH 05/27] fix incomplete attribute selectors in keyboard shortcuts --- thresholdmann.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/thresholdmann.js b/thresholdmann.js index a3f442e..562a7d9 100644 --- a/thresholdmann.js +++ b/thresholdmann.js @@ -626,22 +626,22 @@ const initKeyboardShortcuts = () => { case 's': globals.selectedTool = 'Select'; document.querySelector('#tools').querySelector('.mui-pressed').classList.remove('mui-pressed'); - document.querySelector('#tools').querySelector('[title="Select"').classList.add('mui-pressed'); + document.querySelector('#tools').querySelector('[title="Select"]').classList.add('mui-pressed'); break; case 'a': globals.selectedTool = 'Add'; document.querySelector('#tools').querySelector('.mui-pressed').classList.remove('mui-pressed'); - document.querySelector('#tools').querySelector('[title="Add"').classList.add('mui-pressed'); + document.querySelector('#tools').querySelector('[title="Add"]').classList.add('mui-pressed'); break; case 'r': globals.selectedTool = 'Remove'; document.querySelector('#tools').querySelector('.mui-pressed').classList.remove('mui-pressed'); - document.querySelector('#tools').querySelector('[title="Remove"').classList.add('mui-pressed'); + document.querySelector('#tools').querySelector('[title="Remove"]').classList.add('mui-pressed'); break; case 'm': globals.selectedTool = 'Move'; document.querySelector('#tools').querySelector('.mui-pressed').classList.remove('mui-pressed'); - document.querySelector('#tools').querySelector('[title="Move"').classList.add('mui-pressed'); + document.querySelector('#tools').querySelector('[title="Move"]').classList.add('mui-pressed'); break; } }); From 20666062832a851054f77617fab82ef1678a0b49 Mon Sep 17 00:00:00 2001 From: Nicolas Traut Date: Wed, 6 Mar 2024 15:19:12 +0100 Subject: [PATCH 06/27] use pre-built Playwright Docker image in circleci --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index aae0760..567ea8f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -2,7 +2,7 @@ version : 2.1 jobs: build: docker: - - image: circleci/node:16-browsers + - image: mcr.microsoft.com/playwright:v1.41.2-jammy steps: - checkout - run: npm install From 1c52e10684890f1e75dddd9e600711a66deee4be Mon Sep 17 00:00:00 2001 From: nicolas Date: Wed, 13 Mar 2024 17:12:09 +0100 Subject: [PATCH 07/27] perform some linting --- fixtures.mjs | 17 +++++++++-------- index.html | 4 ++-- thresholdmann.js | 30 ++++++++++-------------------- 3 files changed, 21 insertions(+), 30 deletions(-) diff --git a/fixtures.mjs b/fixtures.mjs index 29c4b3d..c549146 100644 --- a/fixtures.mjs +++ b/fixtures.mjs @@ -5,17 +5,18 @@ let server; const startServer = () => { server = createServer({ root: '.' - }) + }); server.listen('8080', '127.0.0.1'); - return server -} -export const mochaGlobalSetup = async () => { - server = startServer({port: "8080"}); - console.log('server started at 8080') -} + return server; +}; + +export const mochaGlobalSetup = () => { + server = startServer({port: '8080'}); + console.log('server started at 8080'); +}; export const mochaGlobalTeardown = async () => { await server.close(); console.log('server stopped'); -} +}; diff --git a/index.html b/index.html index a048975..f9c0dd8 100644 --- a/index.html +++ b/index.html @@ -153,8 +153,8 @@

-
-
+
+

diff --git a/thresholdmann.js b/thresholdmann.js index 5992c6e..ccce4b9 100644 --- a/thresholdmann.js +++ b/thresholdmann.js @@ -131,7 +131,6 @@ const voxel2canvas = (point) => { const height = H * width / W; const Hlarge = H * heightLarge / height; const offset = (Hlarge - H) / 2; - // eslint-disable-next-line new-cap const [i, j, k] = point; let slice, x, y; switch (plane) { @@ -269,7 +268,6 @@ const threshold = () => { s = slice2volume(plane, x, y, slice, H); ind = y * width + x; _setPixelFromValue( - // eslint-disable-next-line new-cap px, ind, interpolate(/*mv.S2IJK(s)*/s), selectedOverlay); } } @@ -318,8 +316,7 @@ const selectControlPoint = (cpid) => { * @param {HTMLElement} trSelected - the row element * @returns {void} */ -// eslint-disable-next-line no-unused-vars -const selectRow = (trSelected) => { +window.selectRow = (trSelected) => { // select the table row document.querySelectorAll('tr.selected').forEach((tr) => { tr.classList.remove('selected'); @@ -359,11 +356,10 @@ const selectRow = (trSelected) => { /** Handle changes in threshold triggered by the sliders * in the control points table. - * @param {HTMLElement} ob - the slider element + * @param {HTMLElement} ev - the slider element * @returns {void} */ -// eslint-disable-next-line no-unused-vars -const changeThreshold = (ev) => { +window.changeThreshold = (ev) => { ev.preventDefault(); const el = ev.target; @@ -391,8 +387,7 @@ const changeThreshold = (ev) => { } }; -// eslint-disable-next-line no-unused-vars -const inputThreshold = (ob) => { +window.inputThreshold = (ob) => { const {mv} = globals; const val = parseFloat(ob.value); const tr = ob.closest('tr'); @@ -735,8 +730,7 @@ window.init = async (file) => { * the HTML page. * @returns {void} */ -// eslint-disable-next-line no-unused-vars -const loadNifti = () => { +window.loadNifti = () => { const input = document.createElement('input'); input.type = 'file'; input.onchange = function () { @@ -753,7 +747,6 @@ const loadNifti = () => { * @param {string} path - the path to the Nifti file * @returns {void} */ -// eslint-disable-next-line no-unused-vars window.initWithPath = async (path) => { _newMRIViewer({path}); await _display(); @@ -765,33 +758,30 @@ window.initWithPath = async (path) => { * @param {Event} ev - the event * @returns {void} */ -// eslint-disable-next-line no-unused-vars -const changeAlpha = (ev) => { +window.changeAlpha = (ev) => { const newAlpha = Number(ev.target.value) / 100; globals.alpha = newAlpha; globals.mv.draw(); }; -/** Adjust the brightness of the brain MRI. This +/** Adjust the contrast of the brain MRI. This * function is called from the HTML page. * @param {Event} ev - the event * @returns {void} */ -// eslint-disable-next-line no-unused-vars -const changeBrightness = (ev) => { +window.changeContrast = (ev) => { const {brightness} = globals; const contrast = Number(ev.target.value) / 100; globals.contrast = contrast; document.querySelector('canvas.viewer').style.filter = `brightness(${brightness}) contrast(${contrast})`; }; -/** Adjust the contrast of the brain MRI. This +/** Adjust the brightness of the brain MRI. This * function is called from the HTML page. * @param {Event} ev - the event * @returns {void} */ -// eslint-disable-next-line no-unused-vars -const changeContrast = (ev) => { +window.changeBrightness = (ev) => { const brightness = Number(ev.target.value) / 100; const {contrast} = globals; globals.brightness = brightness; From 6ddf70ebc8094a2fe41de4d6a5990ab6d51bcd2e Mon Sep 17 00:00:00 2001 From: nicolas Date: Wed, 13 Mar 2024 17:13:03 +0100 Subject: [PATCH 08/27] add new tests --- test.mjs | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 230 insertions(+), 7 deletions(-) diff --git a/test.mjs b/test.mjs index 35e6a4e..e0366b7 100644 --- a/test.mjs +++ b/test.mjs @@ -1,4 +1,6 @@ +/* eslint-disable max-lines */ import assert from 'assert'; +import { text, buffer } from 'node:stream/consumers'; import { chromium, firefox, webkit } from '@playwright/test'; @@ -27,6 +29,10 @@ describe('Test Thresholdmann', () => { await page.goto('http://127.0.0.1:8080'); }); + after(async () => { + await browser.close(); + }); + describe('Unit tests', () => { describe('Trivial test', () => { it('should say hey', async () => { @@ -44,18 +50,42 @@ describe('Test Thresholdmann', () => { }); it('should display "Choose..." message', async () => { const msg = await page.evaluate(() => document.querySelector('.box_input').innerText); - assert.strictEqual(msg, '\nChoose a .nii.gz or a .nii file or drag it here.'); + assert.strictEqual(msg.trim(), 'Choose a .nii.gz or a .nii file or drag it here.'); }); it('init with test nifti file', async () => { const pathString = './img/bear_uchar.nii.gz'; - sdim = await page.evaluate(async (path) => { - await window.initWithPath(path); - - return window.globals.mv.mri.s2v.sdim; - }, pathString); + const fileChooserPromise = page.waitForEvent('filechooser'); + await page.click('#loadNifti'); + const fileChooser = await fileChooserPromise; + await fileChooser.setFiles(pathString); + await page.waitForSelector('.viewer'); + sdim = await page.evaluate(() => window.globals.mv.mri.s2v.sdim); assert.ok(Array.isArray(sdim)); assert.strictEqual(sdim.length, 3); - }).timeout(5000); + }).timeout(10000); + }); + + describe('can change slice', () => { + it('check initial slice', async () => { + const initialSlice = sdim[0] / 2 | 0; + const slice = await page.evaluate(() => ( + window.globals.mv.views[0].slice + )); + assert.strictEqual(slice, initialSlice); + }); + + it('can change to another slice', async () => { + const targetSlice = sdim[0] * 3 / 4 | 0; + await page.evaluate((newSlice) => { + const slider = document.querySelector('input.slice'); + slider.value = newSlice; + slider.dispatchEvent(new Event('input', { bubbles: true })); + }, targetSlice); + const slice = await page.evaluate(() => ( + window.globals.mv.views[0].slice + )); + assert.strictEqual(slice, targetSlice); + }); }); describe('can switch planes', () => { @@ -140,7 +170,43 @@ describe('Test Thresholdmann', () => { assert.ok(points[0][1] < points[1][1]); assert.ok(points[0][2] > points[1][2]); }); + it('when selecting the first point, the first threshold slider is selected', async () => { + await page.click('#tools .mui[title="Select"]'); + const clickX = canvasBoundingBox.x + canvasBoundingBox.width / 2; + const clickY = canvasBoundingBox.y + canvasBoundingBox.height / 2; + await page.mouse.click(clickX, clickY); + const firstSliderSelected = await page.$eval('#control-table tbody tr:nth-child(1)', (row) => row.classList.contains('selected')); + const secondSliderSelected = await page.$eval('#control-table tbody tr:nth-child(2)', (row) => row.classList.contains('selected')); + assert.strictEqual(firstSliderSelected, true); + assert.strictEqual(secondSliderSelected, false); + }); + it('when selecting the second point, the second threshold slider is selected', async () => { + await page.click('#tools .mui[title="Select"]'); + const clickX = canvasBoundingBox.x + canvasBoundingBox.width * 2 / 3; + const clickY = canvasBoundingBox.y + canvasBoundingBox.height * 2 / 3; + await page.mouse.click(clickX, clickY); + const firstSliderSelected = await page.$eval('#control-table tbody tr:nth-child(1)', (row) => row.classList.contains('selected')); + const secondSliderSelected = await page.$eval('#control-table tbody tr:nth-child(2)', (row) => row.classList.contains('selected')); + assert.strictEqual(firstSliderSelected, false); + assert.strictEqual(secondSliderSelected, true); + }); + it('when selecting the first threshold slider, the first point slice is displayed', async () => { + await page.click('.axi-btn'); + await page.click('#control-table tbody tr:nth-child(1) td:first-child'); + const sliderValue = await page.$eval('input.slice', (slider) => slider.value); + const slice = await page.evaluate(() => (window.globals.mv.views[0].slice)); + assert.strictEqual(parseInt(sliderValue, 10), points[0][2]); + assert.strictEqual(slice, points[0][2]); + }); + it('when selecting the second threshold slider, the second point slice is displayed', async () => { + await page.click('#control-table tbody tr:nth-child(2) td:first-child'); + const sliderValue = await page.$eval('input.slice', (slider) => slider.value); + const slice = await page.evaluate(() => (window.globals.mv.views[0].slice)); + assert.strictEqual(parseInt(sliderValue, 10), points[1][2]); + assert.strictEqual(slice, points[1][2]); + }); it('can remove a point', async () => { + await page.click('.sag-btn'); await page.click('#tools .mui[title="Remove"]'); const clickX = canvasBoundingBox.x + canvasBoundingBox.width * 2 / 3; const clickY = canvasBoundingBox.y + canvasBoundingBox.height * 2 / 3; @@ -149,6 +215,163 @@ describe('Test Thresholdmann', () => { assert.strictEqual(points.length, 1); }); }); + + describe('can adjust threshold', () => { + it('check initial threshold', async () => { + const initThreshold = await page.evaluate(() => ( + window.globals.values[0] + )); + assert.strictEqual(initThreshold, 127); + }); + + it('can adjust threshold with slider', async () => { + const targetThreshold = 75; + const sliderElement = await page.$('#control-table tbody tr:first-child td.slider-val input'); + await page.evaluate(({slider, value}) => { + slider.value = value; + slider.dispatchEvent(new Event('input', { bubbles: true })); + }, {slider: sliderElement, value: targetThreshold.toString()}); + const newThreshold = await page.evaluate(() => ( + window.globals.values[0] + )); + assert.strictEqual(newThreshold, targetThreshold); + }); + + it('can adjust threshold with input', async () => { + const targetThreshold = 125; + const inputElement = await page.$('#control-table tbody tr:first-child td.text-val input'); + await inputElement.fill(String(targetThreshold)); + await inputElement.dispatchEvent('change'); + const newThreshold = await page.evaluate(() => ( + window.globals.values[0] + )); + assert.strictEqual(newThreshold, targetThreshold); + }); + }); + + describe('can adjust opacity, contrast and brightness', () => { + it('check initial values', async () => { + const {opacity, contrast, brightness} = await page.evaluate(() => ({ + opacity: window.globals.alpha, + contrast: window.globals.contrast, + brightness: window.globals.brightness + })); + + assert.strictEqual(opacity, 0.5); + assert.strictEqual(contrast, 1); + assert.strictEqual(brightness, 1); + }); + + it('can adjust opacity', async () => { + const targetOpacity = 40; + await page.evaluate((opacity) => { + const alphaSlider = document.querySelector('#image-adjust input[type="range"][oninput="changeAlpha(event)"]'); + alphaSlider.value = opacity; + alphaSlider.dispatchEvent(new Event('input', { bubbles: true })); + }, targetOpacity); + const newOpacity = await page.evaluate(() => ( + window.globals.alpha + )); + assert.strictEqual(newOpacity, targetOpacity / 100); + }); + + it('can adjust contrast', async () => { + const targetContrast = 80; + await page.evaluate((contrast) => { + const contrastSlider = document.querySelector('#image-adjust input[type="range"][oninput="changeContrast(event)"]'); + contrastSlider.value = contrast; + contrastSlider.dispatchEvent(new Event('input', { bubbles: true })); + }, targetContrast); + const newContrast = await page.evaluate(() => ( + window.globals.contrast + )); + assert.strictEqual(newContrast, targetContrast / 100); + }); + + it('can adjust brightness', async () => { + const targetBrightness = 120; + await page.evaluate((brightness) => { + const brightnessSlider = document.querySelector('#image-adjust input[type="range"][oninput="changeBrightness(event)"]'); + brightnessSlider.value = brightness; + brightnessSlider.dispatchEvent(new Event('input', { bubbles: true })); + }, targetBrightness); + const newBrightness = await page.evaluate(() => ( + window.globals.brightness + )); + assert.strictEqual(newBrightness, targetBrightness / 100); + }); + + }); + + describe('3D render', () => { + let newPage; + + it('can open the 3D volume view window', async () => { + const newPagePromise = page.context().waitForEvent('page'); + await page.click('#render3D'); + newPage = await newPagePromise; + const newPageUrl = newPage.url(); + assert.strictEqual(newPageUrl, 'http://127.0.0.1:8080/render3D/index.html'); + }); + + // after(async () => { + // if (newPage) { + // await newPage.close(); + // } + // }); + }); + + describe('Saving', () => { + it('Save Control Points', async () => { + page.on('dialog', async (dialog) => { + if (dialog.type() === 'prompt') { + await dialog.accept(); + } + }); + const downloadPromise = page.waitForEvent('download'); + await page.click('#saveControlPoints'); + const download = await downloadPromise; + const readStream = await download.createReadStream(); + const downloadedJson = await text(readStream); + const controlPoints = JSON.parse(downloadedJson); + assert.deepStrictEqual(controlPoints, { points: [sdim.map((element) => element / 2 | 0)], values: [125] }); + }); + it('Save Mask', async () => { + page.on('dialog', async (dialog) => { + if (dialog.type() === 'prompt') { + await dialog.accept(); + } + }); + const downloadPromise = page.waitForEvent('download'); + await page.click('#saveMask'); + const download = await downloadPromise; + const readStream = await download.createReadStream(); + const niftiBuffer = await buffer(readStream); + + // check that dowloaded nifti is a gzip file + assert.deepStrictEqual(niftiBuffer.slice(0, 2), Buffer.from([0x1F, 0x8B])); + }).timeout(5000); + }); + + describe('Loading', () => { + it('Load Control Points', async () => { + const fileChooserPromise = page.waitForEvent('filechooser'); + await page.click('#loadControlPoints'); + const fileChooser = await fileChooserPromise; + await fileChooser.setFiles({ + name: 'control-points.json', + mimeType: 'application/json', + buffer: Buffer.from(JSON.stringify({ points: [[50, 50, 50]], values: [100]}), 'utf-8') + }); + // wait for the new points to be loaded + await page.waitForTimeout(10); + const points = await page.evaluate(() => window.globals.points); + const threshold = await page.evaluate(() => window.globals.values[0]); + assert.strictEqual(points.length, 1); + assert.deepStrictEqual(points, [[50, 50, 50]]); + assert.strictEqual(threshold, 100); + }); + }); }); }); }); From 8f314082c9ebeb9243b2f6c3a0693ab8b6f77b86 Mon Sep 17 00:00:00 2001 From: nicolas Date: Thu, 14 Mar 2024 11:10:42 +0100 Subject: [PATCH 09/27] adjust timeouts --- test.mjs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/test.mjs b/test.mjs index e0366b7..a3e52f1 100644 --- a/test.mjs +++ b/test.mjs @@ -48,10 +48,12 @@ describe('Test Thresholdmann', () => { const title = await page.evaluate(() => document.title); assert.strictEqual(title, 'Thresholdmann'); }); + it('should display "Choose..." message', async () => { const msg = await page.evaluate(() => document.querySelector('.box_input').innerText); assert.strictEqual(msg.trim(), 'Choose a .nii.gz or a .nii file or drag it here.'); }); + it('init with test nifti file', async () => { const pathString = './img/bear_uchar.nii.gz'; const fileChooserPromise = page.waitForEvent('filechooser'); @@ -94,11 +96,13 @@ describe('Test Thresholdmann', () => { const plane = await page.evaluate(() => window.globals.mv.views[0].plane); assert.strictEqual(plane, 'cor'); }); + it('can switch to axial plane', async () => { await page.click('.axi-btn'); const plane = await page.evaluate(() => window.globals.mv.views[0].plane); assert.strictEqual(plane, 'axi'); }); + it('can switch to sagittal plane', async () => { await page.click('.sag-btn'); const plane = await page.evaluate(() => window.globals.mv.views[0].plane); @@ -112,16 +116,19 @@ describe('Test Thresholdmann', () => { const direction = await page.evaluate(() => window.globals.selectedDirection); assert.strictEqual(direction, 'SelectDown'); }); + it('change threshold direction to up', async () => { await page.click('#direction .mui[title="SelectUp"]'); const direction = await page.evaluate(() => window.globals.selectedDirection); assert.strictEqual(direction, 'SelectUp'); }); + it('switch to threshold value view', async () => { await page.click('#overlay .mui[title="Threshold Value"]'); const overlay = await page.evaluate(() => window.globals.selectedOverlay); assert.strictEqual(overlay, 'Threshold Value'); }); + it('switch to threshold mask view', async () => { await page.click('#overlay .mui[title="Threshold Mask"]'); const overlay = await page.evaluate(() => window.globals.selectedOverlay); @@ -143,6 +150,7 @@ describe('Test Thresholdmann', () => { // point is in the middle assert.ok(points[0].every((v, i) => v === Math.floor(sdim[i] / 2))); }); + it('can add a point', async () => { await page.click('#tools .mui[title="Add"]'); const clickX = canvasBoundingBox.x + canvasBoundingBox.width / 3; @@ -154,6 +162,7 @@ describe('Test Thresholdmann', () => { assert.ok(points[0][1] > points[1][1]); assert.ok(points[0][2] < points[1][2]); }); + it('can move a point', async () => { await page.click('#tools .mui[title="Move"]'); const startX = canvasBoundingBox.x + canvasBoundingBox.width / 3; @@ -170,6 +179,7 @@ describe('Test Thresholdmann', () => { assert.ok(points[0][1] < points[1][1]); assert.ok(points[0][2] > points[1][2]); }); + it('when selecting the first point, the first threshold slider is selected', async () => { await page.click('#tools .mui[title="Select"]'); const clickX = canvasBoundingBox.x + canvasBoundingBox.width / 2; @@ -180,6 +190,7 @@ describe('Test Thresholdmann', () => { assert.strictEqual(firstSliderSelected, true); assert.strictEqual(secondSliderSelected, false); }); + it('when selecting the second point, the second threshold slider is selected', async () => { await page.click('#tools .mui[title="Select"]'); const clickX = canvasBoundingBox.x + canvasBoundingBox.width * 2 / 3; @@ -190,6 +201,7 @@ describe('Test Thresholdmann', () => { assert.strictEqual(firstSliderSelected, false); assert.strictEqual(secondSliderSelected, true); }); + it('when selecting the first threshold slider, the first point slice is displayed', async () => { await page.click('.axi-btn'); await page.click('#control-table tbody tr:nth-child(1) td:first-child'); @@ -198,6 +210,7 @@ describe('Test Thresholdmann', () => { assert.strictEqual(parseInt(sliderValue, 10), points[0][2]); assert.strictEqual(slice, points[0][2]); }); + it('when selecting the second threshold slider, the second point slice is displayed', async () => { await page.click('#control-table tbody tr:nth-child(2) td:first-child'); const sliderValue = await page.$eval('input.slice', (slider) => slider.value); @@ -205,6 +218,7 @@ describe('Test Thresholdmann', () => { assert.strictEqual(parseInt(sliderValue, 10), points[1][2]); assert.strictEqual(slice, points[1][2]); }); + it('can remove a point', async () => { await page.click('.sag-btn'); await page.click('#tools .mui[title="Remove"]'); @@ -312,7 +326,7 @@ describe('Test Thresholdmann', () => { newPage = await newPagePromise; const newPageUrl = newPage.url(); assert.strictEqual(newPageUrl, 'http://127.0.0.1:8080/render3D/index.html'); - }); + }).timeout(5000); // after(async () => { // if (newPage) { @@ -336,6 +350,7 @@ describe('Test Thresholdmann', () => { const controlPoints = JSON.parse(downloadedJson); assert.deepStrictEqual(controlPoints, { points: [sdim.map((element) => element / 2 | 0)], values: [125] }); }); + it('Save Mask', async () => { page.on('dialog', async (dialog) => { if (dialog.type() === 'prompt') { @@ -350,7 +365,7 @@ describe('Test Thresholdmann', () => { // check that dowloaded nifti is a gzip file assert.deepStrictEqual(niftiBuffer.slice(0, 2), Buffer.from([0x1F, 0x8B])); - }).timeout(5000); + }).timeout(10000); }); describe('Loading', () => { @@ -370,7 +385,7 @@ describe('Test Thresholdmann', () => { assert.strictEqual(points.length, 1); assert.deepStrictEqual(points, [[50, 50, 50]]); assert.strictEqual(threshold, 100); - }); + }).timeout(5000); }); }); }); From b9327f903f75f826bcc8013defbdf39309279542 Mon Sep 17 00:00:00 2001 From: nicolas Date: Fri, 15 Mar 2024 11:22:35 +0100 Subject: [PATCH 10/27] increase timeout for load control points test --- test.mjs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test.mjs b/test.mjs index a3e52f1..29d79b4 100644 --- a/test.mjs +++ b/test.mjs @@ -2,7 +2,7 @@ import assert from 'assert'; import { text, buffer } from 'node:stream/consumers'; -import { chromium, firefox, webkit } from '@playwright/test'; +import { chromium, firefox, webkit, expect } from '@playwright/test'; describe('Test Thresholdmann', () => { const browsers = [ @@ -370,6 +370,7 @@ describe('Test Thresholdmann', () => { describe('Loading', () => { it('Load Control Points', async () => { + page.setDefaultTimeout(60000); const fileChooserPromise = page.waitForEvent('filechooser'); await page.click('#loadControlPoints'); const fileChooser = await fileChooserPromise; @@ -379,13 +380,13 @@ describe('Test Thresholdmann', () => { buffer: Buffer.from(JSON.stringify({ points: [[50, 50, 50]], values: [100]}), 'utf-8') }); // wait for the new points to be loaded - await page.waitForTimeout(10); + await expect(page.locator('#control-table tbody tr:first-child td.text-val input')).toHaveValue('100'); const points = await page.evaluate(() => window.globals.points); const threshold = await page.evaluate(() => window.globals.values[0]); assert.strictEqual(points.length, 1); assert.deepStrictEqual(points, [[50, 50, 50]]); assert.strictEqual(threshold, 100); - }).timeout(5000); + }).timeout(200000); }); }); }); From cd7ec2162ce72197fa5ff79c6228a60403971b85 Mon Sep 17 00:00:00 2001 From: Nicolas Traut Date: Sat, 16 Mar 2024 09:45:00 +0100 Subject: [PATCH 11/27] increase timeout for expect --- test.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.mjs b/test.mjs index 29d79b4..56800a9 100644 --- a/test.mjs +++ b/test.mjs @@ -380,7 +380,7 @@ describe('Test Thresholdmann', () => { buffer: Buffer.from(JSON.stringify({ points: [[50, 50, 50]], values: [100]}), 'utf-8') }); // wait for the new points to be loaded - await expect(page.locator('#control-table tbody tr:first-child td.text-val input')).toHaveValue('100'); + await expect(page.locator('#control-table tbody tr:first-child td.text-val input')).toHaveValue('100', { timeout: 10000 }); const points = await page.evaluate(() => window.globals.points); const threshold = await page.evaluate(() => window.globals.values[0]); assert.strictEqual(points.length, 1); From 6e74c286583e0edb4da7edf2871ea5735ee16338 Mon Sep 17 00:00:00 2001 From: katja heuer Date: Tue, 19 Mar 2024 12:00:28 +0100 Subject: [PATCH 12/27] Add developer notes to Contributing.md --- Contributing.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Contributing.md b/Contributing.md index 18e356c..598e183 100644 --- a/Contributing.md +++ b/Contributing.md @@ -14,14 +14,18 @@ improving the documentation, creating tutorials. We are also open to hearing your ideas for improvement and extension of Thresholdmann. -You can check out if there is an open [issues](https://github.com/neuroanatomy/thresholdmann/issues) you'd like to jump on, and get in touch with us there. +You can check out if there is an open [issue](https://github.com/neuroanatomy/thresholdmann/issues) you'd like to jump on, and get in touch with us there. You can also open a new issue to let us know about bugs, or request a new feature. We will be happy to work with anyone who would love to join our effort. +**Test new functionalities** +If you would like to change the code, please make sure the existing tests run using ```npm install && npm run test```. +If you add a new functionality, please also implement new test(s) to make sure of its correct working. Thank you. + **Get in touch with us!** -You can also write us on twitter to [katjaq](https://twitter.com/katjaQheuer) and [R3RT0](https://twitter.com/r3rt0)! +You can also write us on twitter (X) to [katjaq](https://twitter.com/katjaQheuer) and [R3RT0](https://twitter.com/r3rt0)! **We are looking forward to meeting you!** From d858db495e6a07f56b1479ca6b2900a7d91bc956 Mon Sep 17 00:00:00 2001 From: nicolas Date: Tue, 19 Mar 2024 13:26:56 +0100 Subject: [PATCH 13/27] increase timeouts for test load control points --- test.mjs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test.mjs b/test.mjs index 56800a9..6de30f1 100644 --- a/test.mjs +++ b/test.mjs @@ -370,7 +370,7 @@ describe('Test Thresholdmann', () => { describe('Loading', () => { it('Load Control Points', async () => { - page.setDefaultTimeout(60000); + page.setDefaultTimeout(120000); const fileChooserPromise = page.waitForEvent('filechooser'); await page.click('#loadControlPoints'); const fileChooser = await fileChooserPromise; @@ -380,13 +380,13 @@ describe('Test Thresholdmann', () => { buffer: Buffer.from(JSON.stringify({ points: [[50, 50, 50]], values: [100]}), 'utf-8') }); // wait for the new points to be loaded - await expect(page.locator('#control-table tbody tr:first-child td.text-val input')).toHaveValue('100', { timeout: 10000 }); + await expect(page.locator('#control-table tbody tr:first-child td.text-val input')).toHaveValue('100', { timeout: 15000 }); const points = await page.evaluate(() => window.globals.points); const threshold = await page.evaluate(() => window.globals.values[0]); assert.strictEqual(points.length, 1); assert.deepStrictEqual(points, [[50, 50, 50]]); assert.strictEqual(threshold, 100); - }).timeout(200000); + }).timeout(300000); }); }); }); From 59a3d348dc9cf910e8b751db356270358707bf7d Mon Sep 17 00:00:00 2001 From: katja heuer Date: Tue, 19 Mar 2024 13:52:23 +0100 Subject: [PATCH 14/27] Update Contributing.md --- Contributing.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Contributing.md b/Contributing.md index 598e183..665b801 100644 --- a/Contributing.md +++ b/Contributing.md @@ -20,6 +20,7 @@ You can also open a new issue to let us know about bugs, or request a new featur We will be happy to work with anyone who would love to join our effort. **Test new functionalities** + If you would like to change the code, please make sure the existing tests run using ```npm install && npm run test```. If you add a new functionality, please also implement new test(s) to make sure of its correct working. Thank you. From 58f8f092f28fb7e9bd4b0715ed4908ed4b5bc03d Mon Sep 17 00:00:00 2001 From: katjaq Date: Tue, 19 Mar 2024 16:27:23 +0100 Subject: [PATCH 15/27] add loading demo data option to start screen --- index.html | 4 +++- thresholdmann.js | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index f9c0dd8..7140481 100644 --- a/index.html +++ b/index.html @@ -37,7 +37,8 @@
- Choose a .nii.gz or a .nii file or drag it here. + Choose a .nii.gz or a .nii file or drag it here.

+ Or try this demo data of a bear brain.
Uploading…
Error! .
@@ -238,6 +239,7 @@ }); MUI.push(document.querySelector('#loadNifti'), loadNifti); + MUI.push(document.querySelector('#loadDemoData'), loadDemoData); diff --git a/thresholdmann.js b/thresholdmann.js index ccce4b9..4f5c9dc 100644 --- a/thresholdmann.js +++ b/thresholdmann.js @@ -753,6 +753,16 @@ window.initWithPath = async (path) => { initUI(); }; +/** Load nifti data from a URL. + * This function is called from + * the HTML page. + * @returns {void} + */ +window.loadDemoData = () => { + const url = "img/bear_uchar.nii.gz"; + window.initWithPath(url); +}; + /** Adjust transparency of the thresholding mask. This * function is called from the HTML page. * @param {Event} ev - the event From 3d590658c8aa1079cb141649b9ef79350c44e306 Mon Sep 17 00:00:00 2001 From: katjaq Date: Tue, 19 Mar 2024 16:34:49 +0100 Subject: [PATCH 16/27] add demo data to pass test --- test.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.mjs b/test.mjs index 6de30f1..eea572c 100644 --- a/test.mjs +++ b/test.mjs @@ -51,7 +51,7 @@ describe('Test Thresholdmann', () => { it('should display "Choose..." message', async () => { const msg = await page.evaluate(() => document.querySelector('.box_input').innerText); - assert.strictEqual(msg.trim(), 'Choose a .nii.gz or a .nii file or drag it here.'); + assert.strictEqual(msg.trim(), 'Choose a .nii.gz or a .nii file or drag it here. Or try this demo data of a bear brain.'); }); it('init with test nifti file', async () => { From 1888a8028a728ef55ec017b6ba2bf346acb29fe6 Mon Sep 17 00:00:00 2001 From: katjaq Date: Tue, 19 Mar 2024 16:47:41 +0100 Subject: [PATCH 17/27] fix test to include new demo data --- test.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.mjs b/test.mjs index eea572c..c27fd4f 100644 --- a/test.mjs +++ b/test.mjs @@ -51,7 +51,7 @@ describe('Test Thresholdmann', () => { it('should display "Choose..." message', async () => { const msg = await page.evaluate(() => document.querySelector('.box_input').innerText); - assert.strictEqual(msg.trim(), 'Choose a .nii.gz or a .nii file or drag it here. Or try this demo data of a bear brain.'); + assert.ok(msg.trim().startsWith('Choose a .nii.gz or a .nii file or drag it here.')); }); it('init with test nifti file', async () => { From 03027fbf552846124992f7e035b835720a3f5307 Mon Sep 17 00:00:00 2001 From: katjaq Date: Tue, 19 Mar 2024 16:55:08 +0100 Subject: [PATCH 18/27] nicify the demo data text --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 7140481..b55116f 100644 --- a/index.html +++ b/index.html @@ -38,7 +38,7 @@
Choose a .nii.gz or a .nii file or drag it here.

- Or try this demo data of a bear brain. + Or try with this MRI from a Sloth bear.
Uploading…
Error! .
From d55829c7f33863d828a850ab18bafb997a0e9516 Mon Sep 17 00:00:00 2001 From: nicolas Date: Wed, 20 Mar 2024 14:44:46 +0100 Subject: [PATCH 19/27] resctrict control points movement to the limit of the image --- thresholdmann.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/thresholdmann.js b/thresholdmann.js index 4f5c9dc..640dd87 100644 --- a/thresholdmann.js +++ b/thresholdmann.js @@ -420,7 +420,9 @@ const controlPointMoveHandler = (ev) => { const cpid = globals.selectedControlPoint; const cpidIndex = cpid.replace('cp', '') | 0; const {mv} = globals; - const [i, j, k] = canvas2voxel(ev); + const {dim} = mv.dimensions.voxel; + const [i, j, k] = canvas2voxel(ev) + .map((index, axis) => Math.max(0, Math.min(index, dim[axis] - 1))); // Ensure each value is within bounds globals.points[cpidIndex][0] = i; globals.points[cpidIndex][1] = j; @@ -759,7 +761,7 @@ window.initWithPath = async (path) => { * @returns {void} */ window.loadDemoData = () => { - const url = "img/bear_uchar.nii.gz"; + const url = 'img/bear_uchar.nii.gz'; window.initWithPath(url); }; From 9861286089b7b0ec9b89b77a5b50971820613440 Mon Sep 17 00:00:00 2001 From: katjaq Date: Thu, 21 Mar 2024 12:44:45 +0100 Subject: [PATCH 20/27] add Nicolas --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index b55116f..da44b1c 100644 --- a/index.html +++ b/index.html @@ -194,7 +194,7 @@ From a061d222c34d1954ec79dabb9406d3c29573c53e Mon Sep 17 00:00:00 2001 From: katjaq Date: Tue, 9 Apr 2024 17:03:35 +0200 Subject: [PATCH 21/27] add information on epsilon term --- thresholdmann-worker.js | 2 +- thresholdmann.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/thresholdmann-worker.js b/thresholdmann-worker.js index 8be7c85..305e3ca 100644 --- a/thresholdmann-worker.js +++ b/thresholdmann-worker.js @@ -10,7 +10,7 @@ const interpolation = (pos, points, values) => { (pos[0] - points[k][0]) ** 2 + (pos[1] - points[k][1]) ** 2 + (pos[2] - points[k][2]) ** 2; - const w = 1 / (d + 0.001); + const w = 1 / (d + 0.001); // adding a distance of 0.001 times voxel size to avoid division by 0 at the exact position of control points val += w * values[k]; totalw += w; } diff --git a/thresholdmann.js b/thresholdmann.js index 640dd87..865b6fc 100644 --- a/thresholdmann.js +++ b/thresholdmann.js @@ -39,7 +39,7 @@ const interpolation = (pos) => { (pos[0] - points[k][0]) ** 2 + (pos[1] - points[k][1]) ** 2 + (pos[2] - points[k][2]) ** 2; - const w = 1 / (d + 0.001); + const w = 1 / (d + 0.001); // adding a distance of 0.001 times voxel size to avoid division by 0 at the exact position of control points val += w * values[k]; totalw += w; } From eb6342cb63cfba6d5d6f7a5693078367c4c57a53 Mon Sep 17 00:00:00 2001 From: katjaq Date: Tue, 9 Apr 2024 17:14:35 +0200 Subject: [PATCH 22/27] add performance test to doc --- doc.html | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/doc.html b/doc.html index eb88846..343a7fd 100644 --- a/doc.html +++ b/doc.html @@ -153,9 +153,18 @@

Workflow example<

- Usage example: Final mask. Download the mask created based on your set of control points. Make sure that the brain region is sufficiently disjoint from the rest of the head so that a mathematical morphology closing will be enough to completely separate it. This will give you a mask like shown in this image with its reconstructed surface as an example.


+ Usage example: Final mask. Download the mask created based on your set of control points. Make sure that the brain region is sufficiently disjoint from the rest of the head so that a mathematical morphology closing will be enough to completely separate it. This will give you a mask like shown in this image with its reconstructed surface as an example.

+

Notes on performance

+

+ Data file size. Thresholdmann can load, display and segment very large MRI files. We tested as an extreme case with the 250µm 1.64GB ultra-high resolution human brain in vivo dataset from Lüsebrink et al. (2018). Loading took about 30 seconds until the data was displayed. The interface was perfectly fluid when browsing the slices, adding as many control points as to have a long scrolling list of points, and switching between view planes. Saving the control points was instantaneous, and saving the mask took a bit over a minute. If the browser asks to exit or wait, simply select wait and you should have no problem to save the mask. In general, we recommend to save control points before attempting to save the mask. 🤓 The 3D render took a bit more than half a minute and then was fluidly interactive and zoomable as usual. However, if the data is very large and at the same time noisy, so that the segmentation will have many flying dots, this will result in lots of triangles, and a 3D render on the fly will not be possible.


+

+ + From 4f4185bb7e31d55c63be2d355e09a311594314ff Mon Sep 17 00:00:00 2001 From: katjaq Date: Sat, 27 Apr 2024 15:47:10 +0200 Subject: [PATCH 23/27] improve control point visibility --- thresholdmann.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/thresholdmann.css b/thresholdmann.css index a201740..30dd71f 100644 --- a/thresholdmann.css +++ b/thresholdmann.css @@ -8,6 +8,9 @@ --header-height: 60px; --cpoint-radius: 0.5rem; --cpoint-radius-hover: 0.75rem; + --cpoint-border-style: solid; + --cpoint-border-width: 1px; + --cpoint-border-color: white; } html, body { @@ -48,6 +51,9 @@ html, body { background: #146d84; border-radius: var(--cpoint-radius); transform: translate(-50%,-50%); + border-style: var(--cpoint-border-style); + border-width: var(--cpoint-border-width); + border-color: var(--cpoint-border-color); } .cpoint:hover { width: var(--cpoint-radius-hover); From 344f98746781f6f8778e05d5d2e405e69cca3086 Mon Sep 17 00:00:00 2001 From: katjaq Date: Sat, 27 Apr 2024 16:07:08 +0200 Subject: [PATCH 24/27] disable text selection for ui controls --- thresholdmann.css | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/thresholdmann.css b/thresholdmann.css index 30dd71f..1b3d9c1 100644 --- a/thresholdmann.css +++ b/thresholdmann.css @@ -36,12 +36,19 @@ html, body { flex-direction: column; justify-content: flex-start; } +#buttons { + user-select: none; +} #info { color: white; font-family: 'Montserrat', sans-serif; font-size: var(--normal-font-size); font-weight: 200; line-height: 1.5; + user-select: text; +} +#control { + user-select: none; } .cpoint { position: absolute; From ffbda696ab091556168cc0d2dcc659393436a1a6 Mon Sep 17 00:00:00 2001 From: katjaq Date: Sat, 27 Apr 2024 17:33:48 +0200 Subject: [PATCH 25/27] set threshold value to numeric input --- thresholdmann.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/thresholdmann.js b/thresholdmann.js index 865b6fc..2fe8d94 100644 --- a/thresholdmann.js +++ b/thresholdmann.js @@ -72,7 +72,7 @@ const displayControlPointsTable = () => { - + `; @@ -368,7 +368,7 @@ window.changeThreshold = (ev) => { const tr = el.closest('tr'); const data = tr.dataset.ijk; - tr.querySelector('input[type=text]').value = val.toFixed(0); + tr.querySelector('input[type=number]').value = val.toFixed(0); let i; for (i = globals.points.length - 1; i >= 0; i--) { From 6bdaf8cb84d9b4f00423263ff56400630a5b361f Mon Sep 17 00:00:00 2001 From: roberto Date: Sat, 27 Apr 2024 18:59:10 +0200 Subject: [PATCH 26/27] validate json --- thresholdmann.js | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/thresholdmann.js b/thresholdmann.js index 2fe8d94..3f432ba 100644 --- a/thresholdmann.js +++ b/thresholdmann.js @@ -503,6 +503,22 @@ const saveControlPoints = () => { } }; +const getPoints = (points) => { + const newPoints = points.map((row) => { + const newRow = row.map(Number); + + return (newRow.some(isNaN))?NaN:newRow; + }); + + return (newPoints.some(isNaN))?[]:newPoints; +}; + +const getValues = (values) => { + const newValues = values.map(Number); + + return (newValues.some(isNaN))?[]:newValues; +}; + /** Load control points from a text file. * The control point positions and values are stored * in `globals`. @@ -517,8 +533,17 @@ const loadControlPoints = () => { reader.onload = (ev) => { const str = ev.target.result; const ob = JSON.parse(str); - globals.points = ob.points; - globals.values = ob.values; + + const points = getPoints(ob.points); + const values = getValues(ob.values); + if (points.length === 0 || values.length === 0) { + alert('Invalid control points file'); + + return; + } + + globals.points = points; + globals.values = values; displayControlPointsTable(); globals.mv.draw(); }; From 78f73ff105a04151fb47f576698ec3cdb6ac4003 Mon Sep 17 00:00:00 2001 From: roberto Date: Sat, 27 Apr 2024 20:24:40 +0200 Subject: [PATCH 27/27] fix NaN finding procedure --- thresholdmann.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/thresholdmann.js b/thresholdmann.js index 3f432ba..1346180 100644 --- a/thresholdmann.js +++ b/thresholdmann.js @@ -510,7 +510,7 @@ const getPoints = (points) => { return (newRow.some(isNaN))?NaN:newRow; }); - return (newPoints.some(isNaN))?[]:newPoints; + return (newPoints.some((item) => !Array.isArray(item)))?[]:newPoints; }; const getValues = (values) => {