Files
bigwo/dev-assistant-mcp/dist/tools/runTests.js

137 lines
4.9 KiB
JavaScript
Raw Permalink Normal View History

2026-03-12 12:47:56 +08:00
import { exec } from "child_process";
import { promisify } from "util";
import { existsSync } from "fs";
import { join } from "path";
const execAsync = promisify(exec);
export const runTestsTool = {
name: "run_tests",
description: "运行项目测试。自动检测测试框架Jest、Mocha、Pytest 等),执行测试并返回结构化的结果(通过/失败/跳过数量及失败详情)。",
inputSchema: {
type: "object",
properties: {
project_path: {
type: "string",
description: "项目根目录(绝对路径)",
},
test_file: {
type: "string",
description: "指定测试文件(可选,默认运行全部测试)",
},
test_name: {
type: "string",
description: "指定测试名称或模式(可选)",
},
},
required: ["project_path"],
},
};
async function runCommand(cmd, cwd) {
try {
const { stdout, stderr } = await execAsync(cmd, {
cwd,
timeout: 120000,
maxBuffer: 1024 * 1024 * 10,
shell: process.platform === "win32" ? "powershell.exe" : "/bin/bash",
});
return { stdout, stderr, code: 0 };
}
catch (error) {
return {
stdout: error.stdout || "",
stderr: error.stderr || "",
code: error.code ?? 1,
};
}
}
export async function executeRunTests(args) {
const { project_path, test_file, test_name } = args;
const hasFile = (name) => existsSync(join(project_path, name));
let testCmd = "";
let framework = "";
// 检测测试框架
if (hasFile("jest.config.js") || hasFile("jest.config.ts") || hasFile("jest.config.mjs")) {
framework = "Jest";
testCmd = "npx jest --verbose --no-coverage";
if (test_file)
testCmd += ` "${test_file}"`;
if (test_name)
testCmd += ` -t "${test_name}"`;
}
else if (hasFile("vitest.config.ts") || hasFile("vitest.config.js")) {
framework = "Vitest";
testCmd = "npx vitest run --reporter verbose";
if (test_file)
testCmd += ` "${test_file}"`;
}
else if (hasFile("pytest.ini") || hasFile("pyproject.toml") || hasFile("setup.cfg")) {
framework = "Pytest";
testCmd = "python -m pytest -v";
if (test_file)
testCmd += ` "${test_file}"`;
if (test_name)
testCmd += ` -k "${test_name}"`;
}
else if (hasFile("package.json")) {
// 尝试 package.json 中的 test 脚本
framework = "npm test";
testCmd = "npm test 2>&1";
}
else if (hasFile("requirements.txt")) {
framework = "Pytest (default)";
testCmd = "python -m pytest -v";
if (test_file)
testCmd += ` "${test_file}"`;
}
if (!testCmd) {
return `# 测试结果\n\n⚠️ 未检测到测试框架\n项目路径: ${project_path}\n\n建议:\n- JS/TS: npm install -D jest 或 vitest\n- Python: pip install pytest`;
}
const result = await runCommand(testCmd, project_path);
const fullOutput = (result.stdout + "\n" + result.stderr).trim();
// 解析测试结果
let passed = 0, failed = 0, skipped = 0;
// Jest / Vitest 格式
const jestMatch = fullOutput.match(/Tests:\s+(?:(\d+) failed,?\s*)?(?:(\d+) skipped,?\s*)?(?:(\d+) passed)?/);
if (jestMatch) {
failed = parseInt(jestMatch[1] || "0");
skipped = parseInt(jestMatch[2] || "0");
passed = parseInt(jestMatch[3] || "0");
}
// Pytest 格式
const pytestMatch = fullOutput.match(/(\d+) passed(?:.*?(\d+) failed)?(?:.*?(\d+) skipped)?/);
if (pytestMatch) {
passed = parseInt(pytestMatch[1] || "0");
failed = parseInt(pytestMatch[2] || "0");
skipped = parseInt(pytestMatch[3] || "0");
}
const total = passed + failed + skipped;
const allPassed = failed === 0 && result.code === 0;
const output = [
`# 测试报告`,
``,
`📂 项目: ${project_path}`,
`🔧 框架: ${framework}`,
``,
`## 结果`,
allPassed ? "✅ **全部通过**" : "❌ **存在失败**",
``,
`| 状态 | 数量 |`,
`|------|------|`,
`| ✅ 通过 | ${passed} |`,
`| ❌ 失败 | ${failed} |`,
`| ⏭️ 跳过 | ${skipped} |`,
`| 📊 总计 | ${total} |`,
``,
`## 命令`,
`\`${testCmd}\``,
``,
`## 完整输出`,
"```",
fullOutput.slice(0, 5000),
"```",
];
if (failed > 0) {
output.push(``, `## 建议`, `1. 查看上方失败的测试用例详情`, `2. 使用 code_debug 工具分析失败原因`, `3. 修复后重新运行 run_tests 验证`);
}
return output.join("\n");
}
//# sourceMappingURL=runTests.js.map