ai-git-tools 2.0.62 → 2.0.63
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/bin/cli.js +1 -0
- package/package.json +1 -1
- package/src/commands/auto-dev.js +1 -66
- package/src/core/test-runner.js +52 -8
package/bin/cli.js
CHANGED
package/package.json
CHANGED
package/src/commands/auto-dev.js
CHANGED
|
@@ -5,13 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { resolve } from 'path';
|
|
8
|
-
import { existsSync, readFileSync } from 'fs';
|
|
9
|
-
import { execSync } from 'child_process';
|
|
10
8
|
import inquirer from 'inquirer';
|
|
11
9
|
import { IssueReader } from '../core/issue-reader.js';
|
|
12
10
|
import { CodeGenerator } from '../core/code-generator.js';
|
|
13
11
|
import { AIClient } from '../core/ai-client.js';
|
|
14
|
-
import { writeAndTestCommand, TEST_TYPE_CHOICES, normalizeTestTypeSelection
|
|
12
|
+
import { writeAndTestCommand, TEST_TYPE_CHOICES, normalizeTestTypeSelection } from './write-and-test.js';
|
|
15
13
|
import { Logger } from '../utils/logger.js';
|
|
16
14
|
|
|
17
15
|
export async function autoDevCommand(options = {}) {
|
|
@@ -123,11 +121,6 @@ export async function autoDevCommand(options = {}) {
|
|
|
123
121
|
requestedTestType: options.testType,
|
|
124
122
|
});
|
|
125
123
|
|
|
126
|
-
const resolvedRuntimeTestTypes = resolveRequestedTestTypes(absoluteFilePath, selectedTestType);
|
|
127
|
-
|
|
128
|
-
logger.step('正在驗證專案可用的測試腳本...');
|
|
129
|
-
ensureJestAvailable(resolvedRuntimeTestTypes);
|
|
130
|
-
logger.success('已確認可執行 Jest 測試。');
|
|
131
124
|
console.log('');
|
|
132
125
|
|
|
133
126
|
// ============================================================
|
|
@@ -261,61 +254,3 @@ async function resolveTestTypeForAutoDev({ noConfirm, requestedTestType }) {
|
|
|
261
254
|
|
|
262
255
|
return testType;
|
|
263
256
|
}
|
|
264
|
-
|
|
265
|
-
function ensureJestAvailable(runtimeTestType = ['unit']) {
|
|
266
|
-
const packageJson = readProjectPackageJson();
|
|
267
|
-
|
|
268
|
-
try {
|
|
269
|
-
execSync('npx jest --version', {
|
|
270
|
-
encoding: 'utf-8',
|
|
271
|
-
stdio: 'pipe',
|
|
272
|
-
cwd: process.cwd(),
|
|
273
|
-
});
|
|
274
|
-
ensureComponentTestDependencies(packageJson, runtimeTestType);
|
|
275
|
-
return;
|
|
276
|
-
} catch {
|
|
277
|
-
const hasJestDependency = Boolean(
|
|
278
|
-
packageJson.dependencies?.jest || packageJson.devDependencies?.jest
|
|
279
|
-
);
|
|
280
|
-
|
|
281
|
-
if (!hasJestDependency) {
|
|
282
|
-
throw new Error('目前專案未安裝 Jest,請先安裝後再執行 auto-dev。');
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
ensureComponentTestDependencies(packageJson, runtimeTestType);
|
|
286
|
-
|
|
287
|
-
throw new Error('偵測到 Jest 依賴,但無法執行 npx jest,請檢查安裝或 lockfile 狀態。');
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
function readProjectPackageJson() {
|
|
292
|
-
const packageJsonPath = resolve(process.cwd(), 'package.json');
|
|
293
|
-
if (!existsSync(packageJsonPath)) {
|
|
294
|
-
throw new Error('目前目錄沒有 package.json,無法執行 Jest 測試。');
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
try {
|
|
298
|
-
return JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
299
|
-
} catch {
|
|
300
|
-
throw new Error('無法讀取目前專案的 package.json,請確認 Jest 已安裝。');
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
function ensureComponentTestDependencies(packageJson, runtimeTestType) {
|
|
305
|
-
if (!runtimeTestType.includes('component')) {
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
const hasJsdomDependency = Boolean(
|
|
310
|
-
packageJson.devDependencies?.['jest-environment-jsdom']
|
|
311
|
-
|| packageJson.dependencies?.['jest-environment-jsdom']
|
|
312
|
-
);
|
|
313
|
-
const hasTestingLibraryDependency = Boolean(
|
|
314
|
-
packageJson.devDependencies?.['@testing-library/react']
|
|
315
|
-
|| packageJson.dependencies?.['@testing-library/react']
|
|
316
|
-
);
|
|
317
|
-
|
|
318
|
-
if (!hasJsdomDependency || !hasTestingLibraryDependency) {
|
|
319
|
-
throw new Error('元件測試需要 jest-environment-jsdom 與 @testing-library/react,請先安裝後再執行 auto-dev。');
|
|
320
|
-
}
|
|
321
|
-
}
|
package/src/core/test-runner.js
CHANGED
|
@@ -1,12 +1,46 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* TestRunner 服務
|
|
3
|
-
* 執行 Jest 測試、解析結果,並在失敗時觸發自動修復(最多 2 次)
|
|
3
|
+
* 執行 Jest / bun test 測試、解析結果,並在失敗時觸發自動修復(最多 2 次)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { execSync } from 'child_process';
|
|
7
|
-
import { existsSync } from 'fs';
|
|
7
|
+
import { existsSync, readFileSync } from 'fs';
|
|
8
8
|
import { resolve } from 'path';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* 偵測目前專案使用的測試執行器
|
|
12
|
+
* 優先順序:bun test > vitest > jest
|
|
13
|
+
*/
|
|
14
|
+
function detectTestRunner() {
|
|
15
|
+
const packageJsonPath = resolve(process.cwd(), 'package.json');
|
|
16
|
+
if (!existsSync(packageJsonPath)) {
|
|
17
|
+
return 'jest';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
22
|
+
const allDeps = {
|
|
23
|
+
...pkg.dependencies,
|
|
24
|
+
...pkg.devDependencies,
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// 偵測 bun:test 腳本含 bun test,或安裝了 bun
|
|
28
|
+
const testScript = pkg.scripts?.test || '';
|
|
29
|
+
if (testScript.includes('bun test')) {
|
|
30
|
+
return 'bun';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 偵測 vitest
|
|
34
|
+
if (allDeps.vitest) {
|
|
35
|
+
return 'vitest';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return 'jest';
|
|
39
|
+
} catch {
|
|
40
|
+
return 'jest';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
10
44
|
export class TestRunner {
|
|
11
45
|
/**
|
|
12
46
|
* 執行指定的測試檔案
|
|
@@ -21,22 +55,32 @@ export class TestRunner {
|
|
|
21
55
|
return { success: false, errors: [`測試檔案不存在:${missingTestFilePath}`] };
|
|
22
56
|
}
|
|
23
57
|
|
|
58
|
+
const runner = detectTestRunner();
|
|
59
|
+
|
|
24
60
|
try {
|
|
25
|
-
const absoluteTestFilePaths = testFilePaths
|
|
26
|
-
|
|
27
|
-
|
|
61
|
+
const absoluteTestFilePaths = testFilePaths.map((filePath) => `"${resolve(filePath)}"`);
|
|
62
|
+
|
|
63
|
+
let cmd;
|
|
64
|
+
if (runner === 'bun') {
|
|
65
|
+
// bun test 接受多個檔案作為參數
|
|
66
|
+
cmd = `bun test ${absoluteTestFilePaths.join(' ')}`;
|
|
67
|
+
} else if (runner === 'vitest') {
|
|
68
|
+
cmd = `npx vitest run ${absoluteTestFilePaths.join(' ')}`;
|
|
69
|
+
} else {
|
|
70
|
+
cmd = `npx jest --runInBand --runTestsByPath ${absoluteTestFilePaths.join(' ')} --no-coverage`;
|
|
71
|
+
}
|
|
28
72
|
|
|
29
|
-
execSync(
|
|
73
|
+
execSync(cmd, {
|
|
30
74
|
encoding: 'utf-8',
|
|
31
75
|
stdio: 'pipe',
|
|
32
76
|
timeout: 120000, // 2 分鐘超時
|
|
33
77
|
cwd: process.cwd(),
|
|
34
78
|
});
|
|
35
|
-
return { success: true, errors: [] };
|
|
79
|
+
return { success: true, errors: [], runner };
|
|
36
80
|
} catch (error) {
|
|
37
81
|
const output = (error.stdout || '') + (error.stderr || '');
|
|
38
82
|
const errors = TestRunner._parseErrors(output);
|
|
39
|
-
return { success: false, errors };
|
|
83
|
+
return { success: false, errors, runner };
|
|
40
84
|
}
|
|
41
85
|
}
|
|
42
86
|
|