You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
156 lines
4.4 KiB
156 lines
4.4 KiB
/**
|
|
* Playwright MCP 测试运行器
|
|
*
|
|
* 使用方法:
|
|
* npx tsx tests/runner.ts # 运行所有测试
|
|
* npx tsx tests/runner.ts --login # 只运行登录测试
|
|
* npx tsx tests/runner.ts --headed # 有头模式运行(可见浏览器)
|
|
* npx tsx tests/runner.ts --report # 生成 HTML 报告
|
|
*/
|
|
|
|
import { testSuite, type TestResult, type TestModule } from './index';
|
|
|
|
interface RunOptions {
|
|
filter?: string;
|
|
headed?: boolean;
|
|
report?: boolean;
|
|
}
|
|
|
|
class TestRunner {
|
|
private results: TestResult[] = [];
|
|
private startTime: number = 0;
|
|
|
|
async run(options: RunOptions = {}): Promise<void> {
|
|
this.startTime = Date.now();
|
|
this.results = [];
|
|
|
|
console.log('🚀 Playwright MCP 测试启动\n');
|
|
console.log(`📅 ${new Date().toLocaleString()}`);
|
|
console.log(`🔧 模式: ${options.headed ? '有头' : '无头'}`);
|
|
if (options.filter) {
|
|
console.log(`🔍 过滤: ${options.filter}`);
|
|
}
|
|
console.log('');
|
|
|
|
// 筛选测试模块
|
|
let modules = Object.entries(testSuite);
|
|
if (options.filter) {
|
|
modules = modules.filter(([name]) =>
|
|
name.toLowerCase().includes(options.filter!.toLowerCase())
|
|
);
|
|
}
|
|
|
|
if (modules.length === 0) {
|
|
console.log('❌ 没有找到匹配的测试模块');
|
|
process.exit(1);
|
|
}
|
|
|
|
// 顺序执行测试
|
|
for (const [name, module] of modules) {
|
|
await this.runModule(name, module as TestModule);
|
|
}
|
|
|
|
// 输出报告
|
|
this.printSummary();
|
|
|
|
if (options.report) {
|
|
this.generateReport();
|
|
}
|
|
|
|
// 设置退出码
|
|
const hasFailed = this.results.some(r => !r.passed);
|
|
process.exit(hasFailed ? 1 : 0);
|
|
}
|
|
|
|
private async runModule(name: string, module: TestModule): Promise<void> {
|
|
console.log(`\n📦 ${module.name}`);
|
|
console.log(` ${module.description}`);
|
|
console.log('─'.repeat(50));
|
|
|
|
// 这里通过 MCP 工具执行实际的测试
|
|
// 由于 MCP 工具需要由 Claude 调用,这里我们输出测试指令
|
|
console.log(`\n 测试用例 (${module.tests.length}个):`);
|
|
for (const test of module.tests) {
|
|
console.log(` ${test.passed ? '✅' : '❌'} ${test.name}`);
|
|
if (test.error) {
|
|
console.log(` 错误: ${test.error}`);
|
|
}
|
|
if (test.duration) {
|
|
console.log(` 耗时: ${test.duration}ms`);
|
|
}
|
|
this.results.push(test);
|
|
}
|
|
}
|
|
|
|
private printSummary(): void {
|
|
const duration = Date.now() - this.startTime;
|
|
const total = this.results.length;
|
|
const passed = this.results.filter(r => r.passed).length;
|
|
const failed = total - passed;
|
|
|
|
console.log('\n' + '='.repeat(50));
|
|
console.log('📊 测试报告摘要');
|
|
console.log('='.repeat(50));
|
|
console.log(` 总计: ${total} 个测试`);
|
|
console.log(` ✅ 通过: ${passed} 个`);
|
|
console.log(` ❌ 失败: ${failed} 个`);
|
|
console.log(` ⏱️ 耗时: ${(duration / 1000).toFixed(2)} 秒`);
|
|
console.log('='.repeat(50));
|
|
|
|
if (failed > 0) {
|
|
console.log('\n❌ 失败的测试:');
|
|
this.results
|
|
.filter(r => !r.passed)
|
|
.forEach(r => console.log(` - ${r.name}: ${r.error}`));
|
|
}
|
|
}
|
|
|
|
private generateReport(): void {
|
|
const report = {
|
|
timestamp: new Date().toISOString(),
|
|
summary: {
|
|
total: this.results.length,
|
|
passed: this.results.filter(r => r.passed).length,
|
|
failed: this.results.filter(r => !r.passed).length,
|
|
duration: Date.now() - this.startTime,
|
|
},
|
|
results: this.results,
|
|
};
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const reportPath = path.join(__dirname, 'test-report.json');
|
|
fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
|
|
console.log(`\n📄 报告已保存: ${reportPath}`);
|
|
}
|
|
}
|
|
|
|
// 解析命令行参数
|
|
function parseArgs(): RunOptions {
|
|
const args = process.argv.slice(2);
|
|
const options: RunOptions = {};
|
|
|
|
if (args.includes('--headed')) {
|
|
options.headed = true;
|
|
}
|
|
if (args.includes('--report')) {
|
|
options.report = true;
|
|
}
|
|
|
|
// 查找过滤参数
|
|
const filterArg = args.find(a => a.startsWith('--'));
|
|
if (filterArg && !['--headed', '--report'].includes(filterArg)) {
|
|
options.filter = filterArg.replace('--', '');
|
|
}
|
|
|
|
return options;
|
|
}
|
|
|
|
// 主函数
|
|
async function main() {
|
|
const options = parseArgs();
|
|
const runner = new TestRunner();
|
|
await runner.run(options);
|
|
}
|
|
|
|
main().catch(console.error);
|
|
|