You've already forked iFlow-Settings-Editor-GUI
新增 单元测试框架和测试用例
This commit is contained in:
743
package-lock.json
generated
743
package-lock.json
generated
@@ -12,17 +12,29 @@
|
||||
"vue-i18n": "^9.14.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"-": "^0.0.1",
|
||||
"@icon-park/vue-next": "^1.4.2",
|
||||
"@vitejs/plugin-vue": "^6.0.6",
|
||||
"@vitest/ui": "^4.1.4",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"concurrently": "^8.2.2",
|
||||
"electron": "^28.0.0",
|
||||
"electron-builder": "^24.13.3",
|
||||
"happy-dom": "^20.9.0",
|
||||
"less": "^4.6.4",
|
||||
"less-loader": "^12.3.2",
|
||||
"vite": "^8.0.8",
|
||||
"vitest": "^4.1.4",
|
||||
"vue": "^3.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/-": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/-/-/--0.0.1.tgz",
|
||||
"integrity": "sha512-3HfneK3DGAm05fpyj20sT3apkNcvPpCuccOThOPdzz8sY7GgQGe0l93XH9bt+YzibcTIgUAIMoyVJI740RtgyQ==",
|
||||
"dev": true,
|
||||
"license": "UNLICENSED"
|
||||
},
|
||||
"node_modules/@babel/helper-string-parser": {
|
||||
"version": "7.27.1",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
||||
@@ -668,6 +680,13 @@
|
||||
"@emnapi/runtime": "^1.7.1"
|
||||
}
|
||||
},
|
||||
"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/@oxc-project/types": {
|
||||
"version": "0.124.0",
|
||||
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz",
|
||||
@@ -689,6 +708,13 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@polka/url": {
|
||||
"version": "1.0.0-next.29",
|
||||
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
|
||||
"integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@rolldown/binding-android-arm64": {
|
||||
"version": "1.0.0-rc.15",
|
||||
"resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz",
|
||||
@@ -966,6 +992,13 @@
|
||||
"url": "https://github.com/sindresorhus/is?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@standard-schema/spec": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
|
||||
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@szmarczak/http-timer": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
|
||||
@@ -1013,6 +1046,17 @@
|
||||
"@types/responselike": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/chai": {
|
||||
"version": "5.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
|
||||
"integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/deep-eql": "*",
|
||||
"assertion-error": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/debug": {
|
||||
"version": "4.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
|
||||
@@ -1023,6 +1067,20 @@
|
||||
"@types/ms": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/deep-eql": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
|
||||
"integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/fs-extra": {
|
||||
"version": "9.0.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
|
||||
@@ -1097,6 +1155,23 @@
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/@types/whatwg-mimetype": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/whatwg-mimetype/-/whatwg-mimetype-3.0.2.tgz",
|
||||
"integrity": "sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
||||
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/yauzl": {
|
||||
"version": "2.10.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
|
||||
@@ -1125,6 +1200,151 @@
|
||||
"vue": "^3.2.25"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.4.tgz",
|
||||
"integrity": "sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@standard-schema/spec": "^1.1.0",
|
||||
"@types/chai": "^5.2.2",
|
||||
"@vitest/spy": "4.1.4",
|
||||
"@vitest/utils": "4.1.4",
|
||||
"chai": "^6.2.2",
|
||||
"tinyrainbow": "^3.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.4.tgz",
|
||||
"integrity": "sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/spy": "4.1.4",
|
||||
"estree-walker": "^3.0.3",
|
||||
"magic-string": "^0.30.21"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"msw": "^2.4.9",
|
||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"msw": {
|
||||
"optional": true
|
||||
},
|
||||
"vite": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/mocker/node_modules/estree-walker": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
|
||||
"integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/pretty-format": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.4.tgz",
|
||||
"integrity": "sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tinyrainbow": "^3.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.4.tgz",
|
||||
"integrity": "sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "4.1.4",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.4.tgz",
|
||||
"integrity": "sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "4.1.4",
|
||||
"@vitest/utils": "4.1.4",
|
||||
"magic-string": "^0.30.21",
|
||||
"pathe": "^2.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.4.tgz",
|
||||
"integrity": "sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/ui": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.1.4.tgz",
|
||||
"integrity": "sha512-EgFR7nlj5iTDYZYCvavjFokNYwr3c3ry0sFiCg+N7B233Nwp+NNx7eoF/XvMWDCKY71xXAG3kFkt97ZHBJVL8A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/utils": "4.1.4",
|
||||
"fflate": "^0.8.2",
|
||||
"flatted": "^3.4.2",
|
||||
"pathe": "^2.0.3",
|
||||
"sirv": "^3.0.2",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"tinyrainbow": "^3.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vitest": "4.1.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.4.tgz",
|
||||
"integrity": "sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/pretty-format": "4.1.4",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"tinyrainbow": "^3.1.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/@vue/compiler-core": {
|
||||
"version": "3.5.32",
|
||||
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.32.tgz",
|
||||
@@ -1231,6 +1451,17 @@
|
||||
"integrity": "sha512-ksNyrmRQzWJJ8n3cRDuSF7zNNontuJg1YHnmWRJd2AMu8Ij2bqwiiri2lH5rHtYPZjj4STkNcgcmiQqlOjiYGg==",
|
||||
"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/@xmldom/xmldom": {
|
||||
"version": "0.8.12",
|
||||
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz",
|
||||
@@ -1248,6 +1479,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"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/agent-base": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||
@@ -1512,6 +1753,16 @@
|
||||
"node": ">=0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/assertion-error": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
|
||||
"integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/astral-regex": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
|
||||
@@ -1809,6 +2060,16 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/chai": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz",
|
||||
"integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||
@@ -2023,6 +2284,17 @@
|
||||
"url": "https://github.com/open-cli-tools/concurrently?sponsor=1"
|
||||
}
|
||||
},
|
||||
"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/config-file-ts": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz",
|
||||
@@ -2082,6 +2354,13 @@
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/convert-source-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/copy-anything": {
|
||||
"version": "3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz",
|
||||
@@ -2463,6 +2742,64 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/editorconfig": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.7.tgz",
|
||||
"integrity": "sha512-e0GOtq/aTQhVdNyDU9e02+wz9oDDM+SIOQxWME2QRjzRX5yyLAuHDE+0aE8vHb9XRC8XD37eO2u57+F09JqFhw==",
|
||||
"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.9",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
|
||||
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/editorconfig/node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
|
||||
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/ejs": {
|
||||
"version": "3.1.10",
|
||||
"resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
|
||||
@@ -2769,6 +3106,13 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
|
||||
"integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
||||
@@ -2836,6 +3180,16 @@
|
||||
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/expect-type": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz",
|
||||
"integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/extract-zip": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
|
||||
@@ -2910,6 +3264,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fflate": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
|
||||
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/filelist": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",
|
||||
@@ -2920,6 +3281,13 @@
|
||||
"minimatch": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/flatted": {
|
||||
"version": "3.4.2",
|
||||
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
|
||||
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||
@@ -3243,6 +3611,24 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/happy-dom": {
|
||||
"version": "20.9.0",
|
||||
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-20.9.0.tgz",
|
||||
"integrity": "sha512-GZZ9mKe8r646NUAf/zemnGbjYh4Bt8/MqASJY+pSm5ZDtc3YQox+4gsLI7yi1hba6o+eCsGxpHn5+iEVn31/FQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": ">=20.0.0",
|
||||
"@types/whatwg-mimetype": "^3.0.2",
|
||||
"@types/ws": "^8.18.1",
|
||||
"entities": "^7.0.1",
|
||||
"whatwg-mimetype": "^3.0.0",
|
||||
"ws": "^8.18.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
@@ -3457,6 +3843,13 @@
|
||||
"dev": true,
|
||||
"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/is-ci": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",
|
||||
@@ -3555,6 +3948,86 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/js-beautify": {
|
||||
"version": "1.15.4",
|
||||
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz",
|
||||
"integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"config-chain": "^1.1.13",
|
||||
"editorconfig": "^1.0.4",
|
||||
"glob": "^10.4.2",
|
||||
"js-cookie": "^3.0.5",
|
||||
"nopt": "^7.2.1"
|
||||
},
|
||||
"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/glob": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
||||
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
|
||||
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
|
||||
"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/minimatch": {
|
||||
"version": "9.0.9",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
|
||||
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/js-beautify/node_modules/minipass": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
|
||||
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"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-yaml": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
@@ -4256,6 +4729,16 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/mrmime": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
|
||||
"integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
@@ -4307,6 +4790,22 @@
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"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/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
@@ -4342,6 +4841,17 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/obug": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz",
|
||||
"integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
"https://github.com/sponsors/sxzz",
|
||||
"https://opencollective.com/debug"
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
@@ -4423,6 +4933,13 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/pathe": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
|
||||
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pend": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
||||
@@ -4535,6 +5052,13 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"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/prr": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
|
||||
@@ -4852,6 +5376,13 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/siginfo": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
|
||||
"integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||
@@ -4891,6 +5422,21 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/sirv": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
|
||||
"integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@polka/url": "^1.0.0-next.24",
|
||||
"mrmime": "^2.0.0",
|
||||
"totalist": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/slice-ansi": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
|
||||
@@ -4963,6 +5509,13 @@
|
||||
"license": "BSD-3-Clause",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/stackback": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
|
||||
"integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/stat-mode": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz",
|
||||
@@ -4973,6 +5526,13 @@
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/std-env": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz",
|
||||
"integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
@@ -5157,6 +5717,23 @@
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tinybench": {
|
||||
"version": "2.9.0",
|
||||
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
|
||||
"integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyexec": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz",
|
||||
"integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.16",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz",
|
||||
@@ -5174,6 +5751,16 @@
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyrainbow": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz",
|
||||
"integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tmp": {
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
|
||||
@@ -5194,6 +5781,16 @@
|
||||
"tmp": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/totalist": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
|
||||
"integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tree-kill": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
|
||||
@@ -5385,6 +5982,96 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "4.1.4",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.4.tgz",
|
||||
"integrity": "sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@vitest/expect": "4.1.4",
|
||||
"@vitest/mocker": "4.1.4",
|
||||
"@vitest/pretty-format": "4.1.4",
|
||||
"@vitest/runner": "4.1.4",
|
||||
"@vitest/snapshot": "4.1.4",
|
||||
"@vitest/spy": "4.1.4",
|
||||
"@vitest/utils": "4.1.4",
|
||||
"es-module-lexer": "^2.0.0",
|
||||
"expect-type": "^1.3.0",
|
||||
"magic-string": "^0.30.21",
|
||||
"obug": "^2.1.1",
|
||||
"pathe": "^2.0.3",
|
||||
"picomatch": "^4.0.3",
|
||||
"std-env": "^4.0.0-rc.1",
|
||||
"tinybench": "^2.9.0",
|
||||
"tinyexec": "^1.0.2",
|
||||
"tinyglobby": "^0.2.15",
|
||||
"tinyrainbow": "^3.1.0",
|
||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
|
||||
"why-is-node-running": "^2.3.0"
|
||||
},
|
||||
"bin": {
|
||||
"vitest": "vitest.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^20.0.0 || ^22.0.0 || >=24.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/vitest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edge-runtime/vm": "*",
|
||||
"@opentelemetry/api": "^1.9.0",
|
||||
"@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0",
|
||||
"@vitest/browser-playwright": "4.1.4",
|
||||
"@vitest/browser-preview": "4.1.4",
|
||||
"@vitest/browser-webdriverio": "4.1.4",
|
||||
"@vitest/coverage-istanbul": "4.1.4",
|
||||
"@vitest/coverage-v8": "4.1.4",
|
||||
"@vitest/ui": "4.1.4",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*",
|
||||
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@edge-runtime/vm": {
|
||||
"optional": true
|
||||
},
|
||||
"@opentelemetry/api": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/browser-playwright": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/browser-preview": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/browser-webdriverio": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/coverage-istanbul": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/coverage-v8": {
|
||||
"optional": true
|
||||
},
|
||||
"@vitest/ui": {
|
||||
"optional": true
|
||||
},
|
||||
"happy-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"jsdom": {
|
||||
"optional": true
|
||||
},
|
||||
"vite": {
|
||||
"optional": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue": {
|
||||
"version": "3.5.32",
|
||||
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.32.tgz",
|
||||
@@ -5406,6 +6093,13 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/vue-component-type-helpers": {
|
||||
"version": "2.2.12",
|
||||
"resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz",
|
||||
"integrity": "sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/vue-i18n": {
|
||||
"version": "9.14.5",
|
||||
"resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-9.14.5.tgz",
|
||||
@@ -5427,6 +6121,16 @@
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/whatwg-mimetype": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
|
||||
"integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
@@ -5443,6 +6147,23 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/why-is-node-running": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
|
||||
"integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"siginfo": "^2.0.0",
|
||||
"stackback": "0.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"why-is-node-running": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
@@ -5487,6 +6208,28 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
|
||||
"integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/xmlbuilder": {
|
||||
"version": "15.1.1",
|
||||
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
|
||||
|
||||
11
package.json
11
package.json
@@ -20,7 +20,11 @@
|
||||
"build:win32": "vite build && electron-builder --win --ia32",
|
||||
"build:win-portable": "vite build && electron-builder --win portable",
|
||||
"build:win-installer": "vite build && electron-builder --win nsis",
|
||||
"dist": "vite build && electron-builder"
|
||||
"dist": "vite build && electron-builder",
|
||||
"test": "vitest",
|
||||
"test:ui": "vitest --ui",
|
||||
"test:coverage": "vitest --coverage",
|
||||
"test:run": "vitest run"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.iflow.settings-editor",
|
||||
@@ -84,14 +88,19 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"-": "^0.0.1",
|
||||
"@icon-park/vue-next": "^1.4.2",
|
||||
"@vitejs/plugin-vue": "^6.0.6",
|
||||
"@vitest/ui": "^4.1.4",
|
||||
"@vue/test-utils": "^2.4.6",
|
||||
"concurrently": "^8.2.2",
|
||||
"electron": "^28.0.0",
|
||||
"electron-builder": "^24.13.3",
|
||||
"happy-dom": "^20.9.0",
|
||||
"less": "^4.6.4",
|
||||
"less-loader": "^12.3.2",
|
||||
"vite": "^8.0.8",
|
||||
"vitest": "^4.1.4",
|
||||
"vue": "^3.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
BIN
screenshots/theme-dark.png
Normal file
BIN
screenshots/theme-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
screenshots/theme-solarized-dark.png
Normal file
BIN
screenshots/theme-solarized-dark.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
BIN
screenshots/theme-xcode.png
Normal file
BIN
screenshots/theme-xcode.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
80
src/components/Footer.test.js
Normal file
80
src/components/Footer.test.js
Normal file
@@ -0,0 +1,80 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import Footer from './Footer.vue';
|
||||
|
||||
describe('Footer.vue', () => {
|
||||
it('renders correctly with default props', () => {
|
||||
const wrapper = mount(Footer, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
expect(wrapper.find('.footer').exists()).toBe(true);
|
||||
expect(wrapper.find('.footer-status').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('displays current profile correctly', () => {
|
||||
const wrapper = mount(Footer, {
|
||||
props: {
|
||||
currentProfile: 'dev',
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const statusText = wrapper.find('.footer-status').text();
|
||||
expect(statusText).toContain('dev');
|
||||
});
|
||||
|
||||
it('displays default profile when no prop provided', () => {
|
||||
const wrapper = mount(Footer, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const statusText = wrapper.find('.footer-status').text();
|
||||
expect(statusText).toContain('default');
|
||||
});
|
||||
|
||||
it('displays status dot', () => {
|
||||
const wrapper = mount(Footer, {
|
||||
props: {
|
||||
currentProfile: 'production',
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.find('.footer-status-dot').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('applies translation correctly', () => {
|
||||
const wrapper = mount(Footer, {
|
||||
props: {
|
||||
currentProfile: 'test-profile',
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => `translated-${key}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const statusText = wrapper.find('.footer-status').text();
|
||||
expect(statusText).toContain('translated-api.currentConfig');
|
||||
expect(statusText).toContain('test-profile');
|
||||
});
|
||||
});
|
||||
164
src/components/SideBar.test.js
Normal file
164
src/components/SideBar.test.js
Normal file
@@ -0,0 +1,164 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import SideBar from './SideBar.vue';
|
||||
|
||||
describe('SideBar.vue', () => {
|
||||
it('renders correctly with default props', () => {
|
||||
const wrapper = mount(SideBar, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
expect(wrapper.find('.sidebar').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('has three nav items', () => {
|
||||
const wrapper = mount(SideBar, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const navItems = wrapper.findAll('.nav-item');
|
||||
expect(navItems.length).toBe(3);
|
||||
});
|
||||
|
||||
it('has two sections', () => {
|
||||
const wrapper = mount(SideBar, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const sections = wrapper.findAll('.sidebar-section');
|
||||
expect(sections.length).toBe(2);
|
||||
});
|
||||
|
||||
it('highlights active section correctly', () => {
|
||||
const wrapper = mount(SideBar, {
|
||||
props: {
|
||||
currentSection: 'api',
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const navItems = wrapper.findAll('.nav-item');
|
||||
expect(navItems[0].classes('active')).toBe(false);
|
||||
expect(navItems[1].classes('active')).toBe(true);
|
||||
expect(navItems[2].classes('active')).toBe(false);
|
||||
});
|
||||
|
||||
it('emits navigate event when nav item is clicked', async () => {
|
||||
const wrapper = mount(SideBar, {
|
||||
props: {
|
||||
currentSection: 'general',
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const navItems = wrapper.findAll('.nav-item');
|
||||
await navItems[1].trigger('click');
|
||||
|
||||
expect(wrapper.emitted('navigate')).toBeTruthy();
|
||||
expect(wrapper.emitted('navigate')[0][0]).toBe('api');
|
||||
});
|
||||
|
||||
it('displays server count badge correctly', () => {
|
||||
const wrapper = mount(SideBar, {
|
||||
props: {
|
||||
currentSection: 'general',
|
||||
serverCount: 5
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const badges = wrapper.findAll('.nav-item-badge');
|
||||
expect(badges.length).toBe(1);
|
||||
expect(badges[0].text()).toBe('5');
|
||||
});
|
||||
|
||||
it('displays zero server count', () => {
|
||||
const wrapper = mount(SideBar, {
|
||||
props: {
|
||||
currentSection: 'general',
|
||||
serverCount: 0
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const badges = wrapper.findAll('.nav-item-badge');
|
||||
expect(badges.length).toBe(1);
|
||||
expect(badges[0].text()).toBe('0');
|
||||
});
|
||||
|
||||
it('applies translation to section titles', () => {
|
||||
const wrapper = mount(SideBar, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => `translated-${key}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const sectionTitles = wrapper.findAll('.sidebar-title');
|
||||
expect(sectionTitles[0].text()).toBe('translated-sidebar.general');
|
||||
expect(sectionTitles[1].text()).toBe('translated-sidebar.advanced');
|
||||
});
|
||||
|
||||
it('applies translation to nav item texts', () => {
|
||||
const wrapper = mount(SideBar, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => `translated-${key}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const navItems = wrapper.findAll('.nav-item-text');
|
||||
expect(navItems[0].text()).toBe('translated-sidebar.basicSettings');
|
||||
expect(navItems[1].text()).toBe('translated-sidebar.apiConfig');
|
||||
expect(navItems[2].text()).toBe('translated-sidebar.mcpServers');
|
||||
});
|
||||
|
||||
it('handles null currentSection', () => {
|
||||
const wrapper = mount(SideBar, {
|
||||
props: {
|
||||
currentSection: null,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const navItems = wrapper.findAll('.nav-item');
|
||||
expect(navItems[0].classes('active')).toBe(false);
|
||||
expect(navItems[1].classes('active')).toBe(false);
|
||||
expect(navItems[2].classes('active')).toBe(false);
|
||||
});
|
||||
});
|
||||
131
src/components/TitleBar.test.js
Normal file
131
src/components/TitleBar.test.js
Normal file
@@ -0,0 +1,131 @@
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import TitleBar from './TitleBar.vue';
|
||||
|
||||
describe('TitleBar.vue', () => {
|
||||
beforeEach(() => {
|
||||
// Mock window.electronAPI
|
||||
global.window.electronAPI = {
|
||||
minimize: vi.fn(),
|
||||
maximize: vi.fn(),
|
||||
close: vi.fn()
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders correctly', () => {
|
||||
const wrapper = mount(TitleBar, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
expect(wrapper.find('.titlebar').exists()).toBe(true);
|
||||
expect(wrapper.find('.titlebar-title').exists()).toBe(true);
|
||||
expect(wrapper.find('.titlebar-controls').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('displays app title', () => {
|
||||
const wrapper = mount(TitleBar, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.find('.titlebar-title').text()).toBe('app.title');
|
||||
});
|
||||
|
||||
it('has three window control buttons', () => {
|
||||
const wrapper = mount(TitleBar, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const buttons = wrapper.findAll('.titlebar-btn');
|
||||
expect(buttons.length).toBe(3);
|
||||
});
|
||||
|
||||
it('calls minimize when minimize button is clicked', async () => {
|
||||
const wrapper = mount(TitleBar, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const minimizeButton = wrapper.findAll('.titlebar-btn')[0];
|
||||
await minimizeButton.trigger('click');
|
||||
|
||||
expect(window.electronAPI.minimize).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('calls maximize when maximize button is clicked', async () => {
|
||||
const wrapper = mount(TitleBar, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const maximizeButton = wrapper.findAll('.titlebar-btn')[1];
|
||||
await maximizeButton.trigger('click');
|
||||
|
||||
expect(window.electronAPI.maximize).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('calls close when close button is clicked', async () => {
|
||||
const wrapper = mount(TitleBar, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const closeButton = wrapper.findAll('.titlebar-btn')[2];
|
||||
await closeButton.trigger('click');
|
||||
|
||||
expect(window.electronAPI.close).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('has close button with close class', () => {
|
||||
const wrapper = mount(TitleBar, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const closeButton = wrapper.findAll('.titlebar-btn')[2];
|
||||
expect(closeButton.classes()).toContain('close');
|
||||
});
|
||||
|
||||
it('applies translation to button tooltips', () => {
|
||||
const wrapper = mount(TitleBar, {
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => `translated-${key}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const buttons = wrapper.findAll('.titlebar-btn');
|
||||
expect(buttons[0].attributes('title')).toBe('translated-window.minimize');
|
||||
expect(buttons[1].attributes('title')).toBe('translated-window.maximize');
|
||||
expect(buttons[2].attributes('title')).toBe('translated-window.close');
|
||||
});
|
||||
});
|
||||
@@ -280,6 +280,11 @@ body {
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
|
||||
.form-required {
|
||||
color: var(--danger);
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
padding: 10px 14px;
|
||||
@@ -434,6 +439,12 @@ body {
|
||||
transform: translateY(0) scale(0.98);
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--bg-secondary);
|
||||
color: var(--text-secondary);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: var(--bg-tertiary);
|
||||
color: var(--text-primary);
|
||||
@@ -447,7 +458,7 @@ body {
|
||||
.btn-danger {
|
||||
background: var(--danger);
|
||||
color: white;
|
||||
box-shadow: 0 2px 4px rgba(239, 68, 68, 0.3);
|
||||
border: 1px solid var(--danger);
|
||||
}
|
||||
|
||||
.btn-danger:hover {
|
||||
@@ -470,6 +481,32 @@ body {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
// Side panel close button (used by ServerPanel and ApiProfileDialog)
|
||||
.side-panel-close {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--text-tertiary);
|
||||
cursor: pointer;
|
||||
border-radius: var(--radius);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.side-panel-close:hover {
|
||||
background: var(--bg-hover);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.side-panel-close svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
stroke: currentColor;
|
||||
stroke-width: 1.5;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
// Empty state
|
||||
.empty-state {
|
||||
display: flex;
|
||||
@@ -518,6 +555,10 @@ body {
|
||||
animation: fadeIn 0.15s ease;
|
||||
}
|
||||
|
||||
.dialog-overlay-top {
|
||||
z-index: 1400;
|
||||
}
|
||||
|
||||
.dialog {
|
||||
background: var(--bg-secondary);
|
||||
border-radius: var(--radius-lg);
|
||||
@@ -542,6 +583,12 @@ body {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.dialog-body {
|
||||
padding: 20px 24px;
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.dialog-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
|
||||
344
src/views/ApiConfig.test.js
Normal file
344
src/views/ApiConfig.test.js
Normal file
@@ -0,0 +1,344 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import ApiConfig from './ApiConfig.vue';
|
||||
|
||||
describe('ApiConfig.vue', () => {
|
||||
const mockSettings = {
|
||||
apiProfiles: {
|
||||
'default': {
|
||||
baseUrl: 'https://api.default.com',
|
||||
selectedAuthType: 'openai-compatible',
|
||||
apiKey: '',
|
||||
modelName: '',
|
||||
searchApiKey: '',
|
||||
cna: ''
|
||||
},
|
||||
'dev': {
|
||||
baseUrl: 'https://api.dev.com',
|
||||
selectedAuthType: 'openai-compatible',
|
||||
apiKey: 'dev-key',
|
||||
modelName: 'gpt-4',
|
||||
searchApiKey: '',
|
||||
cna: ''
|
||||
},
|
||||
'prod': {
|
||||
baseUrl: 'https://api.prod.com',
|
||||
selectedAuthType: 'openai-compatible',
|
||||
apiKey: 'prod-key',
|
||||
modelName: 'gpt-4',
|
||||
searchApiKey: '',
|
||||
cna: ''
|
||||
}
|
||||
},
|
||||
currentApiProfile: 'default'
|
||||
};
|
||||
|
||||
const mockProfiles = [
|
||||
{ name: 'default' },
|
||||
{ name: 'dev' },
|
||||
{ name: 'prod' }
|
||||
];
|
||||
|
||||
it('renders correctly with props', () => {
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: mockProfiles,
|
||||
currentProfile: 'default',
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
expect(wrapper.find('.content-title').exists()).toBe(true);
|
||||
expect(wrapper.find('.card').exists()).toBe(true);
|
||||
expect(wrapper.find('.profile-list').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('displays all profiles', () => {
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: mockProfiles,
|
||||
currentProfile: 'default',
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const profileItems = wrapper.findAll('.profile-item');
|
||||
expect(profileItems.length).toBe(3);
|
||||
});
|
||||
|
||||
it('highlights current profile', () => {
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: mockProfiles,
|
||||
currentProfile: 'dev',
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const profileItems = wrapper.findAll('.profile-item');
|
||||
expect(profileItems[0].classes('active')).toBe(false);
|
||||
expect(profileItems[1].classes('active')).toBe(true);
|
||||
expect(profileItems[2].classes('active')).toBe(false);
|
||||
});
|
||||
|
||||
it('shows status badge only for current profile', () => {
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: mockProfiles,
|
||||
currentProfile: 'default',
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const statusBadges = wrapper.findAll('.status-badge');
|
||||
expect(statusBadges.length).toBe(1);
|
||||
expect(wrapper.findAll('.profile-item')[0].find('.status-badge').exists()).toBe(true);
|
||||
expect(wrapper.findAll('.profile-item')[1].find('.status-badge').exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('emits create-profile event when create button is clicked', async () => {
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: mockProfiles,
|
||||
currentProfile: 'default',
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await wrapper.find('.btn-primary').trigger('click');
|
||||
expect(wrapper.emitted('create-profile')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('emits select-profile event when profile is clicked', async () => {
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: mockProfiles,
|
||||
currentProfile: 'default',
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const profileItems = wrapper.findAll('.profile-item');
|
||||
await profileItems[1].trigger('click');
|
||||
|
||||
expect(wrapper.emitted('select-profile')).toBeTruthy();
|
||||
expect(wrapper.emitted('select-profile')[0][0]).toBe('dev');
|
||||
});
|
||||
|
||||
it('emits edit-profile event when edit button is clicked', async () => {
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: mockProfiles,
|
||||
currentProfile: 'default',
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const editButtons = wrapper.findAll('.action-btn');
|
||||
await editButtons[0].trigger('click');
|
||||
|
||||
expect(wrapper.emitted('edit-profile')).toBeTruthy();
|
||||
expect(wrapper.emitted('edit-profile')[0][0]).toBe('default');
|
||||
});
|
||||
|
||||
it('emits duplicate-profile event when duplicate button is clicked', async () => {
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: mockProfiles,
|
||||
currentProfile: 'default',
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const duplicateButtons = wrapper.findAll('.action-btn');
|
||||
await duplicateButtons[1].trigger('click');
|
||||
|
||||
expect(wrapper.emitted('duplicate-profile')).toBeTruthy();
|
||||
expect(wrapper.emitted('duplicate-profile')[0][0]).toBe('default');
|
||||
});
|
||||
|
||||
it('shows delete button only for non-default profiles', () => {
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: mockProfiles,
|
||||
currentProfile: 'default',
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const profileItems = wrapper.findAll('.profile-item');
|
||||
const deleteButtons = wrapper.findAll('.action-btn-danger');
|
||||
|
||||
expect(deleteButtons.length).toBe(2);
|
||||
expect(profileItems[0].find('.action-btn-danger').exists()).toBe(false);
|
||||
expect(profileItems[1].find('.action-btn-danger').exists()).toBe(true);
|
||||
expect(profileItems[2].find('.action-btn-danger').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('emits delete-profile event when delete button is clicked', async () => {
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: mockProfiles,
|
||||
currentProfile: 'default',
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const deleteButtons = wrapper.findAll('.action-btn-danger');
|
||||
await deleteButtons[0].trigger('click');
|
||||
|
||||
expect(wrapper.emitted('delete-profile')).toBeTruthy();
|
||||
expect(wrapper.emitted('delete-profile')[0][0]).toBe('dev');
|
||||
});
|
||||
|
||||
it('displays correct profile names', () => {
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: mockProfiles,
|
||||
currentProfile: 'default',
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const profileNames = wrapper.findAll('.profile-name');
|
||||
expect(profileNames[0].text()).toBe('default');
|
||||
expect(profileNames[1].text()).toBe('dev');
|
||||
expect(profileNames[2].text()).toBe('prod');
|
||||
});
|
||||
|
||||
it('displays correct profile URLs', () => {
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: mockProfiles,
|
||||
currentProfile: 'default',
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const profileUrls = wrapper.findAll('.profile-url');
|
||||
expect(profileUrls[0].text()).toBe('https://api.default.com');
|
||||
expect(profileUrls[1].text()).toBe('https://api.dev.com');
|
||||
expect(profileUrls[2].text()).toBe('https://api.prod.com');
|
||||
});
|
||||
|
||||
it('displays correct profile initials', () => {
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: mockProfiles,
|
||||
currentProfile: 'default',
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const iconTexts = wrapper.findAll('.profile-icon-text');
|
||||
expect(iconTexts[0].text()).toBe('D');
|
||||
expect(iconTexts[1].text()).toBe('D');
|
||||
expect(iconTexts[2].text()).toBe('P');
|
||||
});
|
||||
|
||||
it('handles empty profiles array', () => {
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: [],
|
||||
currentProfile: 'default',
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const profileItems = wrapper.findAll('.profile-item');
|
||||
expect(profileItems.length).toBe(0);
|
||||
});
|
||||
|
||||
it('handles missing apiProfiles in settings', () => {
|
||||
const settingsWithoutProfiles = { currentApiProfile: 'default' };
|
||||
|
||||
const wrapper = mount(ApiConfig, {
|
||||
props: {
|
||||
profiles: mockProfiles,
|
||||
currentProfile: 'default',
|
||||
settings: settingsWithoutProfiles,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const profileUrls = wrapper.findAll('.profile-url');
|
||||
expect(profileUrls[0].text()).toBe('');
|
||||
expect(profileUrls[1].text()).toBe('');
|
||||
expect(profileUrls[2].text()).toBe('');
|
||||
});
|
||||
});
|
||||
157
src/views/GeneralSettings.test.js
Normal file
157
src/views/GeneralSettings.test.js
Normal file
@@ -0,0 +1,157 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import { nextTick } from 'vue';
|
||||
import GeneralSettings from './GeneralSettings.vue';
|
||||
|
||||
describe('GeneralSettings.vue', () => {
|
||||
const mockSettings = {
|
||||
language: 'zh-CN',
|
||||
theme: 'Xcode',
|
||||
bootAnimationShown: true,
|
||||
checkpointing: { enabled: true },
|
||||
};
|
||||
|
||||
it('renders correctly with props', () => {
|
||||
const wrapper = mount(GeneralSettings, {
|
||||
props: {
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
expect(wrapper.find('.content-title').exists()).toBe(true);
|
||||
expect(wrapper.findAll('.card').length).toBe(2);
|
||||
});
|
||||
|
||||
it('displays language options correctly', () => {
|
||||
const wrapper = mount(GeneralSettings, {
|
||||
props: {
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const languageOptions = wrapper.findAll('.form-select')[0].findAll('option');
|
||||
expect(languageOptions.length).toBe(3);
|
||||
expect(languageOptions[0].attributes('value')).toBe('zh-CN');
|
||||
expect(languageOptions[1].attributes('value')).toBe('en-US');
|
||||
expect(languageOptions[2].attributes('value')).toBe('ja-JP');
|
||||
});
|
||||
|
||||
it('displays theme options correctly', () => {
|
||||
const wrapper = mount(GeneralSettings, {
|
||||
props: {
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const themeOptions = wrapper.findAll('.form-select')[1].findAll('option');
|
||||
expect(themeOptions.length).toBe(4);
|
||||
expect(themeOptions[0].attributes('value')).toBe('Xcode');
|
||||
expect(themeOptions[1].attributes('value')).toBe('Dark');
|
||||
expect(themeOptions[2].attributes('value')).toBe('Light');
|
||||
expect(themeOptions[3].attributes('value')).toBe('Solarized Dark');
|
||||
});
|
||||
|
||||
it('reflects current settings in form controls', async () => {
|
||||
const wrapper = mount(GeneralSettings, {
|
||||
props: {
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await nextTick();
|
||||
const selectElements = wrapper.findAll('.form-select');
|
||||
expect(selectElements[0].element.value).toBe('zh-CN');
|
||||
expect(selectElements[1].element.value).toBe('Xcode');
|
||||
expect(selectElements[2].element.value).toBe('true');
|
||||
expect(selectElements[3].element.value).toBe('true');
|
||||
});
|
||||
|
||||
it('applies translation correctly', () => {
|
||||
const wrapper = mount(GeneralSettings, {
|
||||
props: {
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => `translated-${key}`,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.find('.content-title').text()).toBe('translated-general.title');
|
||||
expect(wrapper.find('.content-desc').text()).toBe('translated-general.description');
|
||||
});
|
||||
|
||||
it('has two cards for settings sections', () => {
|
||||
const wrapper = mount(GeneralSettings, {
|
||||
props: {
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const cards = wrapper.findAll('.card');
|
||||
expect(cards.length).toBe(2);
|
||||
});
|
||||
|
||||
it('displays card titles with icons', () => {
|
||||
const wrapper = mount(GeneralSettings, {
|
||||
props: {
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const cardTitles = wrapper.findAll('.card-title');
|
||||
expect(cardTitles.length).toBe(2);
|
||||
expect(cardTitles[0].text()).toContain('general.languageInterface');
|
||||
expect(cardTitles[1].text()).toContain('general.otherSettings');
|
||||
});
|
||||
|
||||
it('shows all form controls with proper structure', () => {
|
||||
const wrapper = mount(GeneralSettings, {
|
||||
props: {
|
||||
settings: mockSettings,
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.findAll('.form-row').length).toBe(2);
|
||||
expect(wrapper.findAll('.form-group').length).toBe(4);
|
||||
expect(wrapper.findAll('.form-label').length).toBe(4);
|
||||
expect(wrapper.findAll('.form-select').length).toBe(4);
|
||||
});
|
||||
});
|
||||
256
src/views/McpServers.test.js
Normal file
256
src/views/McpServers.test.js
Normal file
@@ -0,0 +1,256 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import McpServers from './McpServers.vue';
|
||||
|
||||
describe('McpServers.vue', () => {
|
||||
const mockServers = {
|
||||
'server1': {
|
||||
description: '第一个服务器',
|
||||
command: 'node server.js',
|
||||
args: ['--port', '3000'],
|
||||
env: {}
|
||||
},
|
||||
'server2': {
|
||||
description: '第二个服务器',
|
||||
command: 'python server.py',
|
||||
args: [],
|
||||
env: { 'PYTHONPATH': '/path/to/python' }
|
||||
},
|
||||
'server3': {
|
||||
command: 'java -jar server.jar',
|
||||
args: [],
|
||||
env: {}
|
||||
}
|
||||
};
|
||||
|
||||
it('renders correctly with props', () => {
|
||||
const wrapper = mount(McpServers, {
|
||||
props: {
|
||||
servers: mockServers,
|
||||
selectedServer: 'server1',
|
||||
serverCount: 3
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.exists()).toBe(true);
|
||||
expect(wrapper.find('.content-title').exists()).toBe(true);
|
||||
expect(wrapper.find('.server-list').exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('displays all servers', () => {
|
||||
const wrapper = mount(McpServers, {
|
||||
props: {
|
||||
servers: mockServers,
|
||||
selectedServer: 'server1',
|
||||
serverCount: 3
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const serverItems = wrapper.findAll('.server-item');
|
||||
expect(serverItems.length).toBe(3);
|
||||
});
|
||||
|
||||
it('highlights selected server', () => {
|
||||
const wrapper = mount(McpServers, {
|
||||
props: {
|
||||
servers: mockServers,
|
||||
selectedServer: 'server2',
|
||||
serverCount: 3
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const serverItems = wrapper.findAll('.server-item');
|
||||
expect(serverItems[0].classes('selected')).toBe(false);
|
||||
expect(serverItems[1].classes('selected')).toBe(true);
|
||||
expect(serverItems[2].classes('selected')).toBe(false);
|
||||
});
|
||||
|
||||
it('shows empty state when no servers', () => {
|
||||
const wrapper = mount(McpServers, {
|
||||
props: {
|
||||
servers: {},
|
||||
selectedServer: null,
|
||||
serverCount: 0
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.find('.empty-state').exists()).toBe(true);
|
||||
expect(wrapper.find('.empty-state-title').exists()).toBe(true);
|
||||
expect(wrapper.find('.empty-state-desc').exists()).toBe(true);
|
||||
expect(wrapper.findAll('.server-item').length).toBe(0);
|
||||
});
|
||||
|
||||
it('emits add-server event when add button is clicked', async () => {
|
||||
const wrapper = mount(McpServers, {
|
||||
props: {
|
||||
servers: mockServers,
|
||||
selectedServer: 'server1',
|
||||
serverCount: 3
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await wrapper.find('.btn-primary').trigger('click');
|
||||
expect(wrapper.emitted('add-server')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('emits select-server event when server is clicked', async () => {
|
||||
const wrapper = mount(McpServers, {
|
||||
props: {
|
||||
servers: mockServers,
|
||||
selectedServer: 'server1',
|
||||
serverCount: 3
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const serverItems = wrapper.findAll('.server-item');
|
||||
await serverItems[1].trigger('click');
|
||||
|
||||
expect(wrapper.emitted('select-server')).toBeTruthy();
|
||||
expect(wrapper.emitted('select-server')[0][0]).toBe('server2');
|
||||
});
|
||||
|
||||
it('displays correct server names', () => {
|
||||
const wrapper = mount(McpServers, {
|
||||
props: {
|
||||
servers: mockServers,
|
||||
selectedServer: 'server1',
|
||||
serverCount: 3
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const serverNames = wrapper.findAll('.server-name');
|
||||
expect(serverNames[0].text()).toBe('server1');
|
||||
expect(serverNames[1].text()).toBe('server2');
|
||||
expect(serverNames[2].text()).toBe('server3');
|
||||
});
|
||||
|
||||
it('displays correct server descriptions', () => {
|
||||
const wrapper = mount(McpServers, {
|
||||
props: {
|
||||
servers: mockServers,
|
||||
selectedServer: 'server1',
|
||||
serverCount: 3
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const serverDescs = wrapper.findAll('.server-desc');
|
||||
expect(serverDescs[0].text()).toBe('第一个服务器');
|
||||
expect(serverDescs[1].text()).toBe('第二个服务器');
|
||||
expect(serverDescs[2].text()).toBe('mcp.noDescription');
|
||||
});
|
||||
|
||||
it('displays status indicators for all servers', () => {
|
||||
const wrapper = mount(McpServers, {
|
||||
props: {
|
||||
servers: mockServers,
|
||||
selectedServer: 'server1',
|
||||
serverCount: 3
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const statusIndicators = wrapper.findAll('.server-status');
|
||||
expect(statusIndicators.length).toBe(3);
|
||||
});
|
||||
|
||||
it('handles null selectedServer prop', () => {
|
||||
const wrapper = mount(McpServers, {
|
||||
props: {
|
||||
servers: mockServers,
|
||||
selectedServer: null,
|
||||
serverCount: 3
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const serverItems = wrapper.findAll('.server-item');
|
||||
expect(serverItems.length).toBe(3);
|
||||
expect(serverItems[0].classes('selected')).toBe(false);
|
||||
expect(serverItems[1].classes('selected')).toBe(false);
|
||||
expect(serverItems[2].classes('selected')).toBe(false);
|
||||
});
|
||||
|
||||
it('handles zero serverCount with empty servers object', () => {
|
||||
const wrapper = mount(McpServers, {
|
||||
props: {
|
||||
servers: {},
|
||||
selectedServer: null,
|
||||
serverCount: 0
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.find('.empty-state').exists()).toBe(true);
|
||||
expect(wrapper.findAll('.server-item').length).toBe(0);
|
||||
});
|
||||
|
||||
it('displays empty state title correctly', () => {
|
||||
const wrapper = mount(McpServers, {
|
||||
props: {
|
||||
servers: {},
|
||||
selectedServer: null,
|
||||
serverCount: 0
|
||||
},
|
||||
global: {
|
||||
mocks: {
|
||||
$t: (key) => key,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(wrapper.find('.empty-state-title').text()).toBe('mcp.noServers');
|
||||
expect(wrapper.find('.empty-state-desc').text()).toBe('mcp.addFirstServer');
|
||||
});
|
||||
});
|
||||
29
vitest.config.js
Normal file
29
vitest.config.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [vue()],
|
||||
test: {
|
||||
environment: 'happy-dom',
|
||||
globals: true,
|
||||
setupFiles: [],
|
||||
include: ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
exclude: ['node_modules', 'dist', 'release', '.git'],
|
||||
coverage: {
|
||||
provider: 'v8',
|
||||
reporter: ['text', 'json', 'html'],
|
||||
exclude: [
|
||||
'node_modules/',
|
||||
'dist/',
|
||||
'release/',
|
||||
'test/',
|
||||
'**/*.config.js',
|
||||
'main.js',
|
||||
'preload.js'
|
||||
]
|
||||
}
|
||||
},
|
||||
server: {
|
||||
port: 5174
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user