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

/**
* 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);