Files
张翔 08ea5fbe98 feat(admin): 添加用户管理相关文件
添加用户管理视图、API和状态管理文件
2026-03-28 14:37:29 +08:00

131 lines
4.2 KiB
TypeScript

import { FullConfig, Suite, TestCase, TestResult, Reporter } from '@playwright/test/reporter';
import colors from 'ansi-colors';
interface TestProgress {
total: number;
passed: number;
failed: number;
skipped: number;
current: string;
startTime: number;
}
class TestProgressBar {
private progress: TestProgress;
private barWidth: number = 40;
private lastUpdate: number = 0;
constructor(total: number) {
this.progress = {
total,
passed: 0,
failed: 0,
skipped: 0,
current: '',
startTime: Date.now()
};
}
update(testName: string, result?: TestResult) {
if (result) {
if (result.status === 'passed') this.progress.passed++;
else if (result.status === 'failed') this.progress.failed++;
else if (result.status === 'skipped') this.progress.skipped++;
}
this.progress.current = testName;
this.render();
}
private render() {
const now = Date.now();
if (now - this.lastUpdate < 100) return;
this.lastUpdate = now;
const completed = this.progress.passed + this.progress.failed + this.progress.skipped;
const percentage = Math.min(100, Math.round((completed / this.progress.total) * 100));
const filled = Math.round((this.barWidth * percentage) / 100);
const empty = this.barWidth - filled;
const elapsed = Date.now() - this.progress.startTime;
const elapsedSeconds = Math.floor(elapsed / 1000);
const avgTime = completed > 0 ? elapsed / completed : 0;
const remaining = (this.progress.total - completed) * avgTime;
const remainingSeconds = Math.floor(remaining / 1000);
const bar = colors.cyan('█').repeat(filled) + colors.gray('░').repeat(empty);
const statusColor = this.progress.failed > 0 ? colors.red : colors.green;
const statusText = statusColor(`${this.progress.passed} | ✗ ${this.progress.failed} | ⊘ ${this.progress.skipped}`);
const timeText = colors.gray(`${elapsedSeconds}s | ⏳ ~${remainingSeconds}s`);
const currentText = colors.yellow(this.progress.current.substring(0, 50));
process.stdout.write('\r' + ' '.repeat(200));
process.stdout.write(`\r[${bar}] ${percentage}% | ${statusText} | ${timeText}`);
process.stdout.write(`\n ${colors.blue('▶')} ${currentText}`);
}
finalize() {
const elapsed = Date.now() - this.progress.startTime;
const elapsedSeconds = (elapsed / 1000).toFixed(2);
process.stdout.write('\r' + ' '.repeat(200));
process.stdout.write('\n');
const statusColor = this.progress.failed > 0 ? colors.red : colors.green;
const statusText = statusColor(
`测试完成: ${this.progress.passed} 通过, ${this.progress.failed} 失败, ${this.progress.skipped} 跳过`
);
console.log(colors.bold('\n' + '═'.repeat(60)));
console.log(colors.bold(' 测试执行完成'));
console.log('═'.repeat(60));
console.log(` ${statusText}`);
console.log(` ${colors.gray(`总用时: ${elapsedSeconds}`)}`);
console.log(` ${colors.gray(`总测试数: ${this.progress.total}`)}`);
console.log('═'.repeat(60) + '\n');
}
}
class ProgressReporter implements Reporter {
private progressBar: TestProgressBar | null = null;
private totalTests: number = 0;
onBegin(config: FullConfig, suite: Suite) {
this.totalTests = this.countTests(suite);
console.log(colors.bold('\n' + '═'.repeat(60)));
console.log(colors.bold(' 开始执行测试'));
console.log('═'.repeat(60));
console.log(` ${colors.blue(`总测试数: ${this.totalTests}`)}`);
console.log(` ${colors.gray(`测试套件: ${suite.allTests().length}`)}`);
console.log('═'.repeat(60) + '\n');
this.progressBar = new TestProgressBar(this.totalTests);
}
onTestBegin(test: TestCase) {
if (this.progressBar) {
this.progressBar.update(test.title);
}
}
onTestEnd(test: TestCase, result: TestResult) {
if (this.progressBar) {
this.progressBar.update(test.title, result);
}
}
onEnd() {
if (this.progressBar) {
this.progressBar.finalize();
}
}
private countTests(suite: Suite): number {
let count = 0;
suite.allTests().forEach(() => count++);
return count;
}
}
export default ProgressReporter;