diff --git a/package-lock.json b/package-lock.json index 649fd5198..f1e7b8821 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5157,6 +5157,13 @@ "@octokit/openapi-types": "^18.0.0" } }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true, + "license": "MIT" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "dev": true, @@ -7972,6 +7979,17 @@ "version": "3.4.21", "license": "MIT" }, + "node_modules/@vue/test-utils": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.6.tgz", + "integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-beautify": "^1.14.9", + "vue-component-type-helpers": "^2.0.0" + } + }, "node_modules/@yarnpkg/esbuild-plugin-pnp": { "version": "3.0.0-rc.15", "dev": true, @@ -9501,6 +9519,17 @@ "devOptional": true, "license": "MIT" }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, "node_modules/consola": { "version": "3.2.3", "dev": true, @@ -9713,6 +9742,19 @@ "node": ">=4" } }, + "node_modules/cssstyle": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz", + "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "rrweb-cssom": "^0.7.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/csstype": { "version": "3.1.3", "license": "MIT" @@ -9983,6 +10025,57 @@ "node": ">=12" } }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/de-indent": { "version": "1.0.2", "dev": true, @@ -10004,6 +10097,13 @@ } } }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true, + "license": "MIT" + }, "node_modules/decode-named-character-reference": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", @@ -10449,6 +10549,51 @@ "dev": true, "license": "MIT" }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/ee-first": { "version": "1.1.1", "license": "MIT" @@ -12809,6 +12954,19 @@ "dev": true, "license": "ISC" }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "dev": true, @@ -12854,6 +13012,30 @@ "node": ">=8.0.0" } }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/http2-wrapper": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", @@ -12991,6 +13173,13 @@ "version": "2.0.4", "license": "ISC" }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, "node_modules/inline-style-parser": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz", @@ -13487,6 +13676,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-promise": { "version": "2.2.2", "dev": true, @@ -15296,6 +15492,111 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-beautify": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz", + "integrity": "sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.3.3", + "js-cookie": "^3.0.5", + "nopt": "^7.2.0" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-beautify/node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/js-beautify/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-beautify/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/js-beautify/node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/js-stringify": { "version": "1.0.2", "dev": true, @@ -15427,6 +15728,118 @@ "signal-exit": "^3.0.2" } }, + "node_modules/jsdom": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.1.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/jsdom/node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, "node_modules/jsesc": { "version": "2.5.2", "dev": true, @@ -17784,6 +18197,13 @@ "node": ">= 6" } }, + "node_modules/nwsapi": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", + "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/nypm": { "version": "0.3.8", "dev": true, @@ -18295,6 +18715,13 @@ "node": ">=6" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/pad-left": { "version": "2.1.0", "license": "MIT", @@ -18429,7 +18856,9 @@ "license": "MIT" }, "node_modules/path-scurry": { - "version": "1.10.2", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -18437,7 +18866,7 @@ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -18854,6 +19283,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, "node_modules/protocol-buffers-schema": { "version": "3.6.0", "license": "MIT" @@ -20143,6 +20579,13 @@ "fsevents": "~2.3.2" } }, + "node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "dev": true, + "license": "MIT" + }, "node_modules/run-parallel": { "version": "1.2.0", "dev": true, @@ -20207,6 +20650,19 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.23.0", "license": "MIT", @@ -20912,6 +21368,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/synckit": { "version": "0.8.8", "dev": true, @@ -21230,6 +21693,26 @@ "node": ">=14.0.0" } }, + "node_modules/tldts": { + "version": "6.1.69", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.69.tgz", + "integrity": "sha512-Oh/CqRQ1NXNY7cy9NkTPUauOWiTro0jEYZTioGbOmcQh6EC45oribyIMJp0OJO3677r13tO6SKdWoGZUx2BDFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.69" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.69", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.69.tgz", + "integrity": "sha512-nygxy9n2PBUFQUtAXAc122gGo+04/j5qr5TGQFZTHafTKYvmARVXt2cA5rgero2/dnXUfkdPtiJoKmrd3T+wdA==", + "dev": true, + "license": "MIT" + }, "node_modules/tmpl": { "version": "1.0.5", "dev": true, @@ -21295,6 +21778,19 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tough-cookie": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz", + "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/tr46": { "version": "0.0.3", "license": "MIT" @@ -23118,6 +23614,29 @@ "he": "^1.2.0" } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/w3c-xmlserializer/node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, "node_modules/walker": { "version": "1.0.8", "dev": true, @@ -23171,6 +23690,42 @@ "dev": true, "license": "MIT" }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "license": "MIT", @@ -23412,7 +23967,9 @@ "link": true }, "node_modules/ws": { - "version": "8.16.0", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, "license": "MIT", "engines": { @@ -23459,6 +24016,13 @@ "node": ">=4.0" } }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/xtend": { "version": "4.0.2", "dev": true, @@ -23578,10 +24142,12 @@ "@typescript-eslint/eslint-plugin": "7.18.0", "@vitejs/plugin-vue": "^5.0.4", "@vue/eslint-config-prettier": "^9.0.0", + "@vue/test-utils": "^2.4.6", "eslint": "^8.39.0", "eslint-plugin-prettier": "5.1.3", "eslint-plugin-storybook": "0.8.0", "eslint-plugin-vue": "^9.28.0", + "jsdom": "^25.0.1", "prettier": "3.2.5", "storybook": "8.0.5", "vite": "^5.2.7", diff --git a/src/ui/package.json b/src/ui/package.json index 9787cafb2..c01a5f50f 100644 --- a/src/ui/package.json +++ b/src/ui/package.json @@ -55,10 +55,12 @@ "@typescript-eslint/eslint-plugin": "7.18.0", "@vitejs/plugin-vue": "^5.0.4", "@vue/eslint-config-prettier": "^9.0.0", + "@vue/test-utils": "^2.4.6", "eslint": "^8.39.0", "eslint-plugin-prettier": "5.1.3", "eslint-plugin-storybook": "0.8.0", "eslint-plugin-vue": "^9.28.0", + "jsdom": "^25.0.1", "prettier": "3.2.5", "storybook": "8.0.5", "vite": "^5.2.7", diff --git a/src/ui/src/builder/settings/BuilderSettingsProperties.spec.ts b/src/ui/src/builder/settings/BuilderSettingsProperties.spec.ts new file mode 100644 index 000000000..7f469cba1 --- /dev/null +++ b/src/ui/src/builder/settings/BuilderSettingsProperties.spec.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from "vitest"; +import BuilderSettingsProperties from "./BuilderSettingsProperties.vue"; +import { shallowMount } from "@vue/test-utils"; +import { + buildMockComponent, + buildMockCore, + mockInstancePath, + mockProvides, +} from "@/tests/mocks"; +import injectionKeys from "@/injectionKeys"; +import { generateBuilderManager } from "../builderManager"; +import { flattenInstancePath } from "@/renderer/instancePath"; +import WdsFieldWrapper from "@/wds/WdsFieldWrapper.vue"; +import templateMap from "@/core/templateMap"; + +describe("BuilderSettingsProperties", () => { + it.each(Object.keys(templateMap))( + "should render settings for %s", + (type) => { + const { core } = buildMockCore(); + const component = buildMockComponent({ type }); + core.addComponent(component); + + const ssbm = generateBuilderManager(); + ssbm.setSelection( + component.id, + flattenInstancePath(mockInstancePath), + "click", + ); + + const wrapper = shallowMount(BuilderSettingsProperties, { + global: { + provide: { + ...mockProvides, + [injectionKeys.builderManager as symbol]: ssbm, + [injectionKeys.core as symbol]: core, + }, + }, + }); + + // check that each fields is renderer + expect(wrapper.findAllComponents(WdsFieldWrapper)).toHaveLength( + // @ts-expect-error TS doesn't infer the right type for the component + Object.keys(templateMap[type].writer.fields).length, + ); + }, + ); +}); diff --git a/src/ui/src/components/core/input/CoreCheckboxInput.spec.ts b/src/ui/src/components/core/input/CoreCheckboxInput.spec.ts new file mode 100644 index 000000000..a900428ab --- /dev/null +++ b/src/ui/src/components/core/input/CoreCheckboxInput.spec.ts @@ -0,0 +1,58 @@ +import { describe, expect, it, vi } from "vitest"; + +import CoreCheckboxInput from "./CoreCheckboxInput.vue"; +import { flushPromises, mount } from "@vue/test-utils"; +import injectionKeys from "@/injectionKeys"; +import { ref } from "vue"; +import { buildMockComponent, buildMockCore, mockProvides } from "@/tests/mocks"; + +describe("CoreCheckboxInput", () => { + it("should render value from the state and forward emit", async () => { + const { core, userState } = buildMockCore(); + userState.value = { key: ["b"] }; + + core.addComponent( + buildMockComponent({ + handlers: { "wf-options-change": "python_handler" }, + binding: { + eventType: "", + stateRef: "key", + }, + }), + ); + + const wrapper = mount(CoreCheckboxInput, { + global: { + provide: { + ...mockProvides, + [injectionKeys.core as symbol]: core, + [injectionKeys.evaluatedFields as symbol]: { + label: ref("This is the label"), + orientation: ref("horizontal"), + options: ref({ a: "Option A", b: "Option B" }), + }, + }, + }, + }); + + await flushPromises(); + + const options = wrapper.findAll("input"); + expect(options).toHaveLength(2); + + const optionA = options.at(0); + expect(optionA.attributes().value).toBe("a"); + expect(optionA.element.checked).toBe(false); + + const optionB = options.at(1); + expect(optionB.attributes().value).toBe("b"); + expect(optionB.element.checked).toBe(true); + + const dispatchEvent = vi.spyOn(wrapper.vm.$el, "dispatchEvent"); + await optionA.trigger("input"); + await flushPromises(); + expect(dispatchEvent).toHaveBeenCalledOnce(); + + expect(wrapper.element).toMatchSnapshot(); + }); +}); diff --git a/src/ui/src/components/core/input/CoreDateInput.spec.ts b/src/ui/src/components/core/input/CoreDateInput.spec.ts new file mode 100644 index 000000000..41e045fda --- /dev/null +++ b/src/ui/src/components/core/input/CoreDateInput.spec.ts @@ -0,0 +1,50 @@ +import { describe, expect, it, vi } from "vitest"; + +import CoreDateInput from "./CoreDateInput.vue"; +import { flushPromises, mount } from "@vue/test-utils"; +import injectionKeys from "@/injectionKeys"; +import { ref } from "vue"; +import { buildMockComponent, buildMockCore, mockProvides } from "@/tests/mocks"; + +describe("CoreDateInput", () => { + it("should render value from the state and forward emit", async () => { + const { core, userState } = buildMockCore(); + userState.value = { key: "2024-12-14" }; + core.addComponent( + buildMockComponent({ + handlers: { "wf-date-change": "python_handler" }, + binding: { + eventType: "", + stateRef: "key", + }, + }), + ); + + const wrapper = mount(CoreDateInput, { + global: { + provide: { + ...mockProvides, + [injectionKeys.core as symbol]: core, + [injectionKeys.evaluatedFields as symbol]: { + label: ref("This is the label"), + }, + }, + }, + }); + + await flushPromises(); + + const input = wrapper.get("input"); + expect(input.element.value).toStrictEqual("2024-12-14"); + + const dispatchEvent = vi.spyOn(wrapper.vm.$el, "dispatchEvent"); + + await input.trigger("change"); + + await flushPromises(); + + expect(dispatchEvent).toHaveBeenCalledOnce(); + + expect(wrapper.element).toMatchSnapshot(); + }); +}); diff --git a/src/ui/src/components/core/input/CoreRadioInput.spec.ts b/src/ui/src/components/core/input/CoreRadioInput.spec.ts new file mode 100644 index 000000000..13be3f966 --- /dev/null +++ b/src/ui/src/components/core/input/CoreRadioInput.spec.ts @@ -0,0 +1,58 @@ +import { describe, expect, it, vi } from "vitest"; + +import CoreRadioInput from "./CoreRadioInput.vue"; +import { flushPromises, mount } from "@vue/test-utils"; +import injectionKeys from "@/injectionKeys"; +import { ref } from "vue"; +import { buildMockComponent, buildMockCore, mockProvides } from "@/tests/mocks"; + +describe("CoreRadioInput", () => { + it("should render value from the state and forward emit", async () => { + const { core, userState } = buildMockCore(); + userState.value = { key: "b" }; + + core.addComponent( + buildMockComponent({ + handlers: { "wf-option-change": "python_handler" }, + binding: { + eventType: "", + stateRef: "key", + }, + }), + ); + + const wrapper = mount(CoreRadioInput, { + global: { + provide: { + ...mockProvides, + [injectionKeys.core as symbol]: core, + [injectionKeys.evaluatedFields as symbol]: { + label: ref("This is the label"), + orientation: ref("horizontal"), + options: ref({ a: "Option A", b: "Option B" }), + }, + }, + }, + }); + + await flushPromises(); + + const options = wrapper.findAll("input"); + expect(options).toHaveLength(2); + + const optionA = options.at(0); + expect(optionA.attributes().value).toBe("a"); + expect(optionA.element.checked).toBe(false); + + const optionB = options.at(1); + expect(optionB.attributes().value).toBe("b"); + expect(optionB.element.checked).toBe(true); + + const dispatchEvent = vi.spyOn(wrapper.vm.$el, "dispatchEvent"); + await optionA.trigger("input"); + await flushPromises(); + expect(dispatchEvent).toHaveBeenCalledOnce(); + + expect(wrapper.element).toMatchSnapshot(); + }); +}); diff --git a/src/ui/src/components/core/input/__snapshots__/CoreCheckboxInput.spec.ts.snap b/src/ui/src/components/core/input/__snapshots__/CoreCheckboxInput.spec.ts.snap new file mode 100644 index 000000000..16c8995b8 --- /dev/null +++ b/src/ui/src/components/core/input/__snapshots__/CoreCheckboxInput.spec.ts.snap @@ -0,0 +1,56 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`CoreCheckboxInput > should render value from the state and forward emit 1`] = ` +
+ + +
+ +
+ + +
+
+ + +
+ +
+ +
+`; diff --git a/src/ui/src/components/core/input/__snapshots__/CoreDateInput.spec.ts.snap b/src/ui/src/components/core/input/__snapshots__/CoreDateInput.spec.ts.snap new file mode 100644 index 000000000..3e4b825f3 --- /dev/null +++ b/src/ui/src/components/core/input/__snapshots__/CoreDateInput.spec.ts.snap @@ -0,0 +1,21 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`CoreDateInput > should render value from the state and forward emit 1`] = ` +
+ + + + +
+`; diff --git a/src/ui/src/components/core/input/__snapshots__/CoreRadioInput.spec.ts.snap b/src/ui/src/components/core/input/__snapshots__/CoreRadioInput.spec.ts.snap new file mode 100644 index 000000000..9b19ed412 --- /dev/null +++ b/src/ui/src/components/core/input/__snapshots__/CoreRadioInput.spec.ts.snap @@ -0,0 +1,60 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`CoreRadioInput > should render value from the state and forward emit 1`] = ` +
+ + +
+ +
+ + +
+
+ + +
+ +
+ +
+`; diff --git a/src/ui/src/components/shared/SharedJsonViewer/SharedJsonViewer.spec.ts b/src/ui/src/components/shared/SharedJsonViewer/SharedJsonViewer.spec.ts new file mode 100644 index 000000000..c334aa088 --- /dev/null +++ b/src/ui/src/components/shared/SharedJsonViewer/SharedJsonViewer.spec.ts @@ -0,0 +1,52 @@ +import { describe, expect, it } from "vitest"; +import SharedJsonViewer from "./SharedJsonViewer.vue"; +import SharedJsonViewerValue from "./SharedJsonViewerValue.vue"; +import SharedJsonViewerObject from "./SharedJsonViewerObject.vue"; +import { mount } from "@vue/test-utils"; + +describe("SharedJsonViewer", () => { + it("should render a string", () => { + const wrapper = mount(SharedJsonViewer, { + propsData: { data: "string" }, + }); + const jsonValue = wrapper.getComponent(SharedJsonViewerValue); + expect(jsonValue.text()).toBe('"string"'); + }); + + it("should render a boolean", () => { + const wrapper = mount(SharedJsonViewer, { propsData: { data: true } }); + const jsonValue = wrapper.getComponent(SharedJsonViewerValue); + expect(jsonValue.text()).toBe("true"); + }); + + it("should render a null", () => { + const wrapper = mount(SharedJsonViewer, { propsData: { data: null } }); + const jsonValue = wrapper.getComponent(SharedJsonViewerValue); + expect(jsonValue.text()).toBe("null"); + }); + + it("should render an object", () => { + const data = { a: "a", b: "b" }; + const wrapper = mount(SharedJsonViewer, { propsData: { data } }); + const jsonObject = wrapper.getComponent(SharedJsonViewerObject); + expect(jsonObject.props().data).toStrictEqual(data); + + expect(wrapper.findAllComponents(SharedJsonViewerValue)).toHaveLength( + 2, + ); + }); + + it("should render a nested object", async () => { + const data = { array: [1, 2, 3], nested: { foo: "bar", a: { b: 2 } } }; + const wrapper = mount(SharedJsonViewer, { propsData: { data } }); + + expect(wrapper.element).toMatchSnapshot(); + }); + + it("should render an array", async () => { + const data = [1, 2, 3]; + const wrapper = mount(SharedJsonViewer, { propsData: { data } }); + + expect(wrapper.element).toMatchSnapshot(); + }); +}); diff --git a/src/ui/src/components/shared/SharedJsonViewer/SharedJsonViewerObject.spec.ts b/src/ui/src/components/shared/SharedJsonViewer/SharedJsonViewerObject.spec.ts new file mode 100644 index 000000000..c18a087b6 --- /dev/null +++ b/src/ui/src/components/shared/SharedJsonViewer/SharedJsonViewerObject.spec.ts @@ -0,0 +1,30 @@ +import { describe, expect, it } from "vitest"; +import SharedJsonViewerObject from "./SharedJsonViewerObject.vue"; +import { flushPromises, mount } from "@vue/test-utils"; +import SharedJsonViewerCollapsible from "./SharedJsonViewerCollapsible.vue"; +import SharedJsonViewer from "./SharedJsonViewer.vue"; + +describe("SharedJsonViewerObject", () => { + it("should expand a key", async () => { + const data = { array: [1, 2, 3], obj: { a: 1, b: 2, c: 3 } }; + const wrapper = mount(SharedJsonViewerObject, { props: { data } }); + + const collapsers = wrapper.findAllComponents( + SharedJsonViewerCollapsible, + ); + expect(collapsers).toHaveLength(2); + + const arrayCollapser = collapsers.at(0); + + expect(arrayCollapser.props().open).toBeFalsy(); + + arrayCollapser.vm.$emit("toggle", true); + await flushPromises(); + + expect(arrayCollapser.props().open).toBeTruthy(); + + const arrayElement = wrapper.getComponent(SharedJsonViewer); + expect(arrayElement.props().data).toStrictEqual(data.array); + expect(arrayElement.props().path).toStrictEqual(["array"]); + }); +}); diff --git a/src/ui/src/components/shared/SharedJsonViewer/__snapshots__/SharedJsonViewer.spec.ts.snap b/src/ui/src/components/shared/SharedJsonViewer/__snapshots__/SharedJsonViewer.spec.ts.snap new file mode 100644 index 000000000..624df5618 --- /dev/null +++ b/src/ui/src/components/shared/SharedJsonViewer/__snapshots__/SharedJsonViewer.spec.ts.snap @@ -0,0 +1,280 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`SharedJsonViewer > should render a nested object 1`] = ` +
+ + +
+ + +
+ + + Object{2} + +
+ +
+
+ +
+ +
+ + + + + +
+ + +
+ + array + + + Array[3] + +
+ +
+
+ +
+ + +
+ +
+
+ + + + + + +
+ + +
+ + nested + + + Object{2} + +
+ +
+
+ +
+ + +
+ +
+
+ + + +
+ +
+ +
+
+ + + +
+`; + +exports[`SharedJsonViewer > should render an array 1`] = ` +
+ + +
+ + +
+ + + Array[3] + +
+ +
+
+ +
+ +
+ + + +
+ + 0 + + : + + 1 + +
+ + + +
+ + 1 + + : + + 2 + +
+ + + +
+ + 2 + + : + + 3 + +
+ + +
+ +
+ +
+
+ + + +
+`; diff --git a/src/ui/src/tests/mocks.ts b/src/ui/src/tests/mocks.ts new file mode 100644 index 000000000..ee658d7a3 --- /dev/null +++ b/src/ui/src/tests/mocks.ts @@ -0,0 +1,41 @@ +import { generateCore } from "@/core"; +import injectionKeys from "@/injectionKeys"; +import { flattenInstancePath } from "@/renderer/instancePath"; +import type { Component, InstancePath } from "@/writerTypes"; +import { ref } from "vue"; + +export const mockComponentId = "component-id-test"; + +export const mockInstancePath: InstancePath = [ + { + componentId: mockComponentId, + instanceNumber: 0, + }, +]; + +export function buildMockComponent(component: Partial) { + return { + id: mockComponentId, + content: {}, + parentId: "root", + position: 0, + type: "button", + ...component, + }; +} + +export function buildMockCore() { + const core = generateCore(); + const userState = ref({}); + + core.userState = ref(userState); + + return { core, userState }; +} + +export const mockProvides: Record = { + [injectionKeys.componentId as symbol]: mockComponentId, + [injectionKeys.instancePath as symbol]: mockInstancePath, + [injectionKeys.flattenedInstancePath as symbol]: + flattenInstancePath(mockInstancePath), +}; diff --git a/src/ui/vite.config.ts b/src/ui/vite.config.ts index b4e58235a..9d64b142b 100644 --- a/src/ui/vite.config.ts +++ b/src/ui/vite.config.ts @@ -20,6 +20,9 @@ export default defineConfig({ "@": fileURLToPath(new URL("./src", import.meta.url)), }, }, + test: { + environment: "jsdom", + }, server: { proxy: { "/api": {