Files
claude-task-master/tests/e2e/utils/logger.js
2025-07-19 00:18:37 +03:00

135 lines
3.0 KiB
JavaScript

import { writeFileSync, mkdirSync, existsSync } from 'fs';
import { join } from 'path';
import chalk from 'chalk';
export class TestLogger {
constructor(logDir, testRunId) {
this.logDir = logDir;
this.testRunId = testRunId;
this.startTime = Date.now();
this.stepCount = 0;
this.logFile = join(logDir, `e2e_run_${testRunId}.log`);
this.logBuffer = [];
this.totalCost = 0;
// Ensure log directory exists
if (!existsSync(logDir)) {
mkdirSync(logDir, { recursive: true });
}
}
formatDuration(milliseconds) {
const totalSeconds = Math.floor(milliseconds / 1000);
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
return `${minutes}m${seconds.toString().padStart(2, '0')}s`;
}
getElapsedTime() {
return this.formatDuration(Date.now() - this.startTime);
}
formatLogEntry(level, message) {
const timestamp = new Date().toISOString();
const elapsed = this.getElapsedTime();
return `[${level}] [${elapsed}] ${timestamp} ${message}`;
}
log(level, message, options = {}) {
const formattedMessage = this.formatLogEntry(level, message);
// Add to buffer
this.logBuffer.push(formattedMessage);
// Console output with colors
let coloredMessage = formattedMessage;
switch (level) {
case 'INFO':
coloredMessage = chalk.blue(formattedMessage);
break;
case 'SUCCESS':
coloredMessage = chalk.green(formattedMessage);
break;
case 'ERROR':
coloredMessage = chalk.red(formattedMessage);
break;
case 'WARNING':
coloredMessage = chalk.yellow(formattedMessage);
break;
}
// Only output to console if debugging or it's an error
if ((process.env.DEBUG_TESTS || level === 'ERROR') && !process.env.JEST_SILENT_MODE) {
console.log(coloredMessage);
}
// Write to file if immediate flush requested
if (options.flush) {
this.flush();
}
}
info(message) {
this.log('INFO', message);
}
success(message) {
this.log('SUCCESS', message);
}
error(message) {
this.log('ERROR', message);
}
warning(message) {
this.log('WARNING', message);
}
step(message) {
this.stepCount++;
const separator = '='.repeat(45);
this.log(
'STEP',
`\n${separator}\n STEP ${this.stepCount}: ${message}\n${separator}`
);
}
addCost(cost) {
if (typeof cost === 'number' && !Number.isNaN(cost)) {
this.totalCost += cost;
}
}
extractAndAddCost(output) {
const costRegex = /Est\. Cost: \$(\d+\.\d+)/g;
let match;
while ((match = costRegex.exec(output)) !== null) {
const cost = parseFloat(match[1]);
this.addCost(cost);
}
}
flush() {
writeFileSync(this.logFile, this.logBuffer.join('\n'), 'utf8');
}
getSummary() {
const duration = this.formatDuration(Date.now() - this.startTime);
const successCount = this.logBuffer.filter((line) =>
line.includes('[SUCCESS]')
).length;
const errorCount = this.logBuffer.filter((line) =>
line.includes('[ERROR]')
).length;
return {
duration,
totalSteps: this.stepCount,
successCount,
errorCount,
totalCost: this.totalCost.toFixed(6),
logFile: this.logFile
};
}
}