ai-worktool 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/view.js ADDED
@@ -0,0 +1,225 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.COVERAGE_FILE_PATH = void 0;
7
+ exports.showCoverageView = showCoverageView;
8
+ exports.stopMonitoring = stopMonitoring;
9
+ exports.loadAndDisplayCoverage = loadAndDisplayCoverage;
10
+ exports.disposeTimer = disposeTimer;
11
+ const fs_1 = __importDefault(require("fs"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const table_1 = require("table");
14
+ const cli_color_1 = __importDefault(require("cli-color"));
15
+ let REFRESH_INTERVAL = 3000;
16
+ // 存储上一次的覆盖率数据
17
+ let previousCoverage = null;
18
+ // 存储变化的单元格位置
19
+ let changedCells = [];
20
+ // 文件监听对象
21
+ let fileWatcher = null;
22
+ // 定时器ID
23
+ let refreshTimer = null;
24
+ const output = console.log;
25
+ exports.COVERAGE_FILE_PATH = 'coverage-summary.json';
26
+ // 主函数
27
+ async function showCoverageView(rootDir, coverageFilePath = exports.COVERAGE_FILE_PATH) {
28
+ const coveragePath = path_1.default.join(rootDir, coverageFilePath);
29
+ console.log(cli_color_1.default.cyan('开始监控覆盖率数据...' + coveragePath));
30
+ // 初始加载
31
+ await loadAndDisplayCoverage(coveragePath);
32
+ // 设置文件监听
33
+ fileWatcher = fs_1.default.watch(coveragePath, async (eventType, filename) => {
34
+ if (filename && eventType === 'change') {
35
+ await loadAndDisplayCoverage(coveragePath);
36
+ }
37
+ });
38
+ // 定时刷新,确保捕获所有变化
39
+ // refreshTimer = setInterval(() => loadAndDisplayCoverage(coveragePath), refershTime) as any;
40
+ }
41
+ // 停止监控函数
42
+ function stopMonitoring() {
43
+ // 清理文件监听器
44
+ if (fileWatcher) {
45
+ fileWatcher.close();
46
+ fileWatcher = null;
47
+ }
48
+ // 清理定时器
49
+ if (refreshTimer) {
50
+ clearInterval(refreshTimer);
51
+ refreshTimer = null;
52
+ }
53
+ }
54
+ let timer = null;
55
+ // 加载并显示覆盖率数据
56
+ async function loadAndDisplayCoverage(coveragePath, refreshTimeout = REFRESH_INTERVAL) {
57
+ try {
58
+ const coverage = await readCoverageFile(coveragePath);
59
+ // 生成表格数据
60
+ const tableData = generateTableData(coverage);
61
+ // 比较并标记变化
62
+ if (previousCoverage && refreshTimeout > 0) {
63
+ changedCells = compareCoverage(previousCoverage, coverage);
64
+ // 如果有变化,3秒后清除标记
65
+ if (changedCells.length > 0) {
66
+ timer = setTimeout(() => {
67
+ timer = null;
68
+ changedCells = [];
69
+ loadAndDisplayCoverage(coveragePath);
70
+ }, refreshTimeout);
71
+ }
72
+ }
73
+ // 存储当前覆盖率数据供下次比较
74
+ previousCoverage = coverage;
75
+ // 渲染表格
76
+ renderTable(tableData);
77
+ }
78
+ catch (error) {
79
+ console.error(cli_color_1.default.red(`加载覆盖率数据时出错: ${error.message}`));
80
+ }
81
+ }
82
+ // 读取覆盖率文件
83
+ async function readCoverageFile(coveragePath) {
84
+ return new Promise((resolve, reject) => {
85
+ fs_1.default.readFile(coveragePath, 'utf8', (err, data) => {
86
+ if (err) {
87
+ reject(new Error(`无法读取覆盖率文件: ${err.message}`));
88
+ return;
89
+ }
90
+ try {
91
+ const coverage = JSON.parse(data);
92
+ resolve(coverage);
93
+ }
94
+ catch (parseError) {
95
+ reject(new Error(`解析覆盖率文件失败: ${parseError.message}`));
96
+ }
97
+ });
98
+ });
99
+ }
100
+ // 生成表格数据
101
+ function generateTableData(coverage) {
102
+ const tableData = [['文件路径', '行覆盖率(%)', '语句覆盖率(%)', '函数覆盖率(%)', '分支覆盖率(%)']];
103
+ // 提取并排序文件路径
104
+ const filePaths = Object.keys(coverage).filter((key) => key !== 'total');
105
+ filePaths.sort();
106
+ // 添加总计行
107
+ tableData.push([
108
+ cli_color_1.default.bold('总计'),
109
+ formatPercentage(coverage.total.lines.pct),
110
+ formatPercentage(coverage.total.statements.pct),
111
+ formatPercentage(coverage.total.functions.pct),
112
+ formatPercentage(coverage.total.branches.pct)
113
+ ]);
114
+ // 添加分隔线
115
+ tableData.push(['---', '---', '---', '---', '---']);
116
+ // 添加每个文件的覆盖率数据
117
+ filePaths.forEach((filePath) => {
118
+ const fileCoverage = coverage[filePath];
119
+ tableData.push([
120
+ path_1.default.basename(filePath),
121
+ formatPercentage(fileCoverage.lines.pct),
122
+ formatPercentage(fileCoverage.statements.pct),
123
+ formatPercentage(fileCoverage.functions.pct),
124
+ formatPercentage(fileCoverage.branches.pct)
125
+ ]);
126
+ });
127
+ return tableData;
128
+ }
129
+ // 格式化百分比并应用颜色
130
+ function formatPercentage(pct) {
131
+ if (pct === 'Unknown')
132
+ return cli_color_1.default.italic('Unknown');
133
+ let formatted = pct.toFixed(2) + '%';
134
+ // 根据覆盖率设置颜色
135
+ if (pct < 50) {
136
+ formatted = cli_color_1.default.red(formatted);
137
+ }
138
+ else if (pct < 80) {
139
+ formatted = cli_color_1.default.yellow(formatted);
140
+ }
141
+ else {
142
+ formatted = cli_color_1.default.green(formatted);
143
+ }
144
+ return formatted;
145
+ }
146
+ // 比较覆盖率数据,找出变化的单元格
147
+ function compareCoverage(oldData, newData) {
148
+ const changes = [];
149
+ // 比较总计
150
+ const compareMetrics = (oldMetrics, newMetrics, rowIndex) => {
151
+ if (oldMetrics.lines.pct !== newMetrics.lines.pct) {
152
+ changes.push([rowIndex, 1]);
153
+ }
154
+ if (oldMetrics.statements.pct !== newMetrics.statements.pct) {
155
+ changes.push([rowIndex, 2]);
156
+ }
157
+ if (oldMetrics.functions.pct !== newMetrics.functions.pct) {
158
+ changes.push([rowIndex, 3]);
159
+ }
160
+ if (oldMetrics.branches.pct !== newMetrics.branches.pct) {
161
+ changes.push([rowIndex, 4]);
162
+ }
163
+ };
164
+ // 比较总计行
165
+ compareMetrics(oldData.total, newData.total, 1);
166
+ // 比较文件行
167
+ const filePaths = Object.keys(oldData).filter((key) => key !== 'total');
168
+ filePaths.forEach((filePath, index) => {
169
+ if (newData[filePath]) {
170
+ compareMetrics(oldData[filePath], newData[filePath], index + 3);
171
+ }
172
+ });
173
+ return changes;
174
+ }
175
+ // 渲染表格
176
+ function renderTable(tableData) {
177
+ // 应用变化标记
178
+ const markedData = tableData.map((row, rowIndex) => {
179
+ return row.map((cell, colIndex) => {
180
+ // 检查是否是变化的单元格
181
+ if (changedCells.some(([r, c]) => r === rowIndex && c === colIndex)) {
182
+ return cli_color_1.default.bgRed.white(cell);
183
+ }
184
+ return cell;
185
+ });
186
+ });
187
+ // 配置表格样式
188
+ const tableConfig = {
189
+ columns: {
190
+ 0: { width: 30 },
191
+ 1: { width: 15, alignment: 'right' },
192
+ 2: { width: 15, alignment: 'right' },
193
+ 3: { width: 15, alignment: 'right' },
194
+ 4: { width: 15, alignment: 'right' }
195
+ },
196
+ border: {
197
+ topBody: `─`,
198
+ topJoin: `┬`,
199
+ topLeft: `┌`,
200
+ topRight: `┐`,
201
+ bottomBody: `─`,
202
+ bottomJoin: `┴`,
203
+ bottomLeft: `└`,
204
+ bottomRight: `┘`,
205
+ bodyLeft: `│`,
206
+ bodyRight: `│`,
207
+ bodyJoin: `│`,
208
+ joinBody: `─`,
209
+ joinLeft: `├`,
210
+ joinRight: `┤`,
211
+ joinJoin: `┼`
212
+ }
213
+ };
214
+ // 清除控制台并打印表格
215
+ console.clear();
216
+ console.log(cli_color_1.default.cyan('单测覆盖率监控工具 - 最后更新: ' + new Date().toLocaleString()));
217
+ output((0, table_1.table)(markedData, tableConfig));
218
+ }
219
+ function disposeTimer() {
220
+ if (timer) {
221
+ clearTimeout(timer);
222
+ timer = null;
223
+ }
224
+ }
225
+ //# sourceMappingURL=view.js.map
package/package.json ADDED
@@ -0,0 +1,302 @@
1
+ {
2
+ "name": "ai-worktool",
3
+ "displayName": "吃豆豆:单测智能体",
4
+ "version": "1.0.0",
5
+ "description": "单元测试智能体,帮你开发单元测试用例代码直到100%单测覆盖通过。",
6
+ "author": "jianguoke.cn",
7
+ "license": "MIT",
8
+ "repository": "https://codeup.aliyun.com/666cf9ed29ecbe23053513a3/JianGuoKe/ai-worktools",
9
+ "bin": {
10
+ "testAgent": "dist/cli.js"
11
+ },
12
+ "scripts": {
13
+ "watch": "tsc -w",
14
+ "build": "tsc",
15
+ "test": "jest test/tools",
16
+ "coverage": "jest --coverage test/tools",
17
+ "tools": "ts-node ./tools.ts",
18
+ "plugin": "yarn tools && dify plugin package plugins/dify-plugin",
19
+ "package": "cross-env PKG_CACHE_PATH=./binaries pkg . --targets node20-win-x64 --out-path aicoder ",
20
+ "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
21
+ "ver": "node ./updatever.js",
22
+ "remove-deps": "node ./remove-deps.js",
23
+ "restore-deps": "node ./restore-deps.js",
24
+ "pub": "yarn remove-deps && yarn changelog && vsce publish --yarn --baseContentUrl https://aicoder.jianguoke.cn/assets && yarn restore-deps",
25
+ "pubovsx": "yarn remove-deps && ovsx publish -p 47621ff6-be56-4814-865e-d2a8e8a76f86 --yarn --baseContentUrl https://aicoder.jianguoke.cn/assets && yarn restore-deps",
26
+ "puball": "yarn ver && yarn pub && yarn pubovsx && yarn puboss && npm publish --registry=https://registry.npmjs.org",
27
+ "patch": "yarn remove-deps && vsce publish patch --yarn && yarn restore-deps",
28
+ "vspack": "yarn remove-deps && vsce package -o aicoder-1.0.19.vsix --yarn --baseContentUrl https://aicoder.jianguoke.cn/assets && yarn restore-deps",
29
+ "uposs": "node ./uposs.js",
30
+ "puboss": "yarn vspack && yarn uposs",
31
+ "prepare": "husky"
32
+ },
33
+ "publisher": "JianGuoKe",
34
+ "icon": "resources/logo_white.png",
35
+ "engines": {
36
+ "vscode": "^1.54.0"
37
+ },
38
+ "pkg": {
39
+ "targets": [
40
+ "node18-win-x64",
41
+ "node18-macos-x64",
42
+ "node18-linux-x64"
43
+ ],
44
+ "outputPath": "dist"
45
+ },
46
+ "categories": [
47
+ "Other"
48
+ ],
49
+ "activationEvents": [
50
+ "onView:ai-worktool-apps"
51
+ ],
52
+ "main": "./dist/extension.js",
53
+ "contributes": {
54
+ "commands": [
55
+ {
56
+ "command": "ai-worktool.startTestAgent",
57
+ "category": "单测智能体",
58
+ "title": "启用/停用单测智能体"
59
+ },
60
+ {
61
+ "command": "ai-worktool.loginCommand",
62
+ "category": "单测智能体",
63
+ "title": "登录"
64
+ },
65
+ {
66
+ "command": "ai-worktool.logoutCommand",
67
+ "category": "单测智能体",
68
+ "title": "退出"
69
+ },
70
+ {
71
+ "command": "ai-worktool.cancelLoginCommand",
72
+ "category": "单测智能体",
73
+ "title": "取消登录"
74
+ },
75
+ {
76
+ "command": "ai-worktool.orderCommand",
77
+ "category": "单测智能体",
78
+ "title": "购买"
79
+ },
80
+ {
81
+ "command": "ai-worktool.usageCommand",
82
+ "category": "单测智能体",
83
+ "title": "用量"
84
+ },
85
+ {
86
+ "command": "ai-worktool.homePageCommand",
87
+ "category": "单测智能体",
88
+ "title": "首页"
89
+ }
90
+ ],
91
+ "submenus": [
92
+ {
93
+ "id": "ai-worktool.submenu",
94
+ "label": "更多操作",
95
+ "icon": "$(ellipsis)"
96
+ }
97
+ ],
98
+ "menus": {
99
+ "view/title": [
100
+ {
101
+ "when": "view == ai-worktool-webview && ai-worktool.isLoggedIn == true",
102
+ "submenu": "ai-worktool.submenu",
103
+ "group": "navigation"
104
+ }
105
+ ],
106
+ "ai-worktool.submenu": [
107
+ {
108
+ "command": "ai-worktool.homePageCommand",
109
+ "when": "ai-worktool.isLoggedIn == true",
110
+ "group": "navigation@1"
111
+ },
112
+ {
113
+ "command": "ai-worktool.loginCommand",
114
+ "when": "ai-worktool.isLoggedIn == false",
115
+ "group": "navigation@2"
116
+ },
117
+ {
118
+ "command": "ai-worktool.usageCommand",
119
+ "when": "ai-worktool.isLoggedIn == true",
120
+ "group": "navigation@5"
121
+ },
122
+ {
123
+ "command": "ai-worktool.logoutCommand",
124
+ "when": "ai-worktool.isLoggedIn == true",
125
+ "group": "navigation@10"
126
+ }
127
+ ]
128
+ },
129
+ "viewsContainers": {
130
+ "activitybar": [
131
+ {
132
+ "id": "ai-worktool",
133
+ "title": "吃豆豆:单测智能体",
134
+ "icon": "resources/logo.svg"
135
+ }
136
+ ]
137
+ },
138
+ "views": {
139
+ "ai-worktool": [
140
+ {
141
+ "type": "webview",
142
+ "id": "ai-worktool-webview",
143
+ "icon": "resources/logo.svg",
144
+ "name": "吃豆豆:单测智能体"
145
+ }
146
+ ]
147
+ },
148
+ "configuration": {
149
+ "type": "object",
150
+ "title": "吃豆豆:单测智能体",
151
+ "properties": {
152
+ "ai-worktool.platform": {
153
+ "type": "string",
154
+ "default": "jianguoke",
155
+ "enum": [
156
+ "jianguoke",
157
+ "chanjet"
158
+ ],
159
+ "description": "智能体平台选择"
160
+ },
161
+ "ai-worktool.packageManager": {
162
+ "type": "string",
163
+ "default": "yarn",
164
+ "description": "包管理器选择",
165
+ "enum": [
166
+ "npm",
167
+ "yarn",
168
+ "pnpm",
169
+ "bun"
170
+ ],
171
+ "scope": "resource"
172
+ },
173
+ "ai-worktool.testFramework": {
174
+ "type": "string",
175
+ "default": "jest",
176
+ "description": "测试框架选择",
177
+ "enum": [
178
+ "jest",
179
+ "vitest",
180
+ "mocha",
181
+ "jasmine",
182
+ "junit",
183
+ "pytest",
184
+ "cypress",
185
+ "cucumber",
186
+ "protractor",
187
+ "testcafe"
188
+ ],
189
+ "scope": "resource"
190
+ },
191
+ "ai-worktool.language": {
192
+ "type": "string",
193
+ "default": "typescript",
194
+ "description": "开发语言选择",
195
+ "enum": [
196
+ "typescript",
197
+ "javascript",
198
+ "python",
199
+ "java",
200
+ "c++",
201
+ "ruby",
202
+ "php",
203
+ "go",
204
+ "rust",
205
+ "swift",
206
+ "kotlin",
207
+ "scala",
208
+ "objective-c",
209
+ "c#",
210
+ "fortran",
211
+ "lisp",
212
+ "commonlisp",
213
+ "scheme",
214
+ "cobol",
215
+ "apl",
216
+ "c"
217
+ ],
218
+ "scope": "resource"
219
+ },
220
+ "ai-worktool.coverageDir": {
221
+ "type": "string",
222
+ "default": "coverage",
223
+ "description": "单测报告文件夹",
224
+ "scope": "resource"
225
+ },
226
+ "ai-worktool.testDir": {
227
+ "type": "string",
228
+ "default": "test",
229
+ "description": "单测文件夹",
230
+ "scope": "resource"
231
+ },
232
+ "ai-worktool.srcDir": {
233
+ "type": "string",
234
+ "default": "src",
235
+ "description": "源代码文件夹",
236
+ "scope": "resource"
237
+ },
238
+ "ai-worktool.autoCommit": {
239
+ "type": "boolean",
240
+ "default": true,
241
+ "description": "单测通过修改文件自动提交代码到远程仓库"
242
+ },
243
+ "ai-worktool.withConversation": {
244
+ "type": "boolean",
245
+ "default": false,
246
+ "description": "每次执行单测时是否继续与智能体进行会话对话"
247
+ },
248
+ "ai-worktool.fullCoverageEnd": {
249
+ "type": "boolean",
250
+ "default": true,
251
+ "description": "单测覆盖100%时结束执行"
252
+ }
253
+ }
254
+ }
255
+ },
256
+ "dependencies": {
257
+ "fetch-sse": "^1.1.2",
258
+ "jsonwebtoken": "^9.0.2",
259
+ "simple-git": "^3.28.0",
260
+ "acorn": "^8.15.0",
261
+ "acorn-walk": "^8.3.4",
262
+ "typescript": "^5.9.2",
263
+ "cli-color": "^2.0.4",
264
+ "commander": "^14.0.0",
265
+ "table": "^6.9.0"
266
+ },
267
+ "devDependencies": {
268
+ "@types/adm-zip": "^0.5.7",
269
+ "@types/cli-color": "^2.0.6",
270
+ "@types/jest": "^30.0.0",
271
+ "@types/jsonwebtoken": "^9.0.10",
272
+ "@types/node": "^24.1.0",
273
+ "@types/vscode": "^1.54.0",
274
+ "@typescript-eslint/eslint-plugin": "^4.31.1",
275
+ "@typescript-eslint/parser": "^4.31.1",
276
+ "@vscode/test-electron": "^1.6.2",
277
+ "@yao-pkg/pkg": "^6.6.0",
278
+ "@yao-pkg/pkg-fetch": "^3.5.24",
279
+ "adm-zip": "^0.5.16",
280
+ "ali-oss": "^6.23.0",
281
+ "conventional-changelog-cli": "^2.2.2",
282
+ "cross-env": "^10.0.0",
283
+ "dotenv": "^17.2.1",
284
+ "eslint": "^7.32.0",
285
+ "husky": "^9.1.7",
286
+ "jest": "^29.7.0",
287
+ "lint-staged": "^16.1.2",
288
+ "nexe": "^5.0.0-beta.4",
289
+ "ovsx": "^0.10.5",
290
+ "ts-jest": "^29.4.0",
291
+ "ts-node": "^10.9.2",
292
+ "typescript": "^5.9.2",
293
+ "vsce": "^2.6.4"
294
+ },
295
+ "jest": {
296
+ "setupFiles": [
297
+ "dotenv/config"
298
+ ],
299
+ "preset": "ts-jest",
300
+ "testEnvironment": "node"
301
+ }
302
+ }