|
|
|
@ -1,12 +1,14 @@
|
|
|
|
|
import { coloredString as cs } from '@push.rocks/consolecolor';
|
|
|
|
|
import * as plugins from './tstest.plugins.js';
|
|
|
|
|
import * as fs from 'fs';
|
|
|
|
|
import * as path from 'path';
|
|
|
|
|
|
|
|
|
|
export interface LogOptions {
|
|
|
|
|
quiet?: boolean;
|
|
|
|
|
verbose?: boolean;
|
|
|
|
|
noColor?: boolean;
|
|
|
|
|
json?: boolean;
|
|
|
|
|
logFile?: string;
|
|
|
|
|
logFile?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface TestFileResult {
|
|
|
|
@ -37,6 +39,7 @@ export class TsTestLogger {
|
|
|
|
|
private startTime: number;
|
|
|
|
|
private fileResults: TestFileResult[] = [];
|
|
|
|
|
private currentFileResult: TestFileResult | null = null;
|
|
|
|
|
private currentTestLogFile: string | null = null;
|
|
|
|
|
|
|
|
|
|
constructor(options: LogOptions = {}) {
|
|
|
|
|
this.options = options;
|
|
|
|
@ -51,11 +54,44 @@ export class TsTestLogger {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private log(message: string) {
|
|
|
|
|
if (this.options.json) return;
|
|
|
|
|
if (this.options.json) {
|
|
|
|
|
// For JSON mode, skip console output
|
|
|
|
|
// JSON output is handled by logJson method
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log(message);
|
|
|
|
|
|
|
|
|
|
if (this.options.logFile) {
|
|
|
|
|
// TODO: Implement file logging
|
|
|
|
|
// Log to the current test file log if we're in a test and --logfile is specified
|
|
|
|
|
if (this.currentTestLogFile) {
|
|
|
|
|
this.logToTestFile(message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private logToFile(message: string) {
|
|
|
|
|
// This method is no longer used since we use logToTestFile for individual test logs
|
|
|
|
|
// Keeping it for potential future use with a global log file
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private logToTestFile(message: string) {
|
|
|
|
|
try {
|
|
|
|
|
// Remove ANSI color codes for file logging
|
|
|
|
|
const cleanMessage = message.replace(/\u001b\[[0-9;]*m/g, '');
|
|
|
|
|
|
|
|
|
|
// Append to test log file
|
|
|
|
|
fs.appendFileSync(this.currentTestLogFile, cleanMessage + '\n');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// Silently fail to avoid disrupting the test run
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private logJson(data: any) {
|
|
|
|
|
const jsonString = JSON.stringify(data);
|
|
|
|
|
console.log(jsonString);
|
|
|
|
|
|
|
|
|
|
// Also log to test file if --logfile is specified
|
|
|
|
|
if (this.currentTestLogFile) {
|
|
|
|
|
this.logToTestFile(jsonString);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -84,7 +120,7 @@ export class TsTestLogger {
|
|
|
|
|
// Test discovery
|
|
|
|
|
testDiscovery(count: number, pattern: string, executionMode: string) {
|
|
|
|
|
if (this.options.json) {
|
|
|
|
|
console.log(JSON.stringify({ event: 'discovery', count, pattern, executionMode }));
|
|
|
|
|
this.logJson({ event: 'discovery', count, pattern, executionMode });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -109,8 +145,23 @@ export class TsTestLogger {
|
|
|
|
|
tests: []
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Only set up test log file if --logfile option is specified
|
|
|
|
|
if (this.options.logFile) {
|
|
|
|
|
const baseFilename = path.basename(filename, '.ts');
|
|
|
|
|
this.currentTestLogFile = path.join('.nogit', 'testlogs', `${baseFilename}.log`);
|
|
|
|
|
|
|
|
|
|
// Ensure the directory exists
|
|
|
|
|
const logDir = path.dirname(this.currentTestLogFile);
|
|
|
|
|
if (!fs.existsSync(logDir)) {
|
|
|
|
|
fs.mkdirSync(logDir, { recursive: true });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear the log file for this test
|
|
|
|
|
fs.writeFileSync(this.currentTestLogFile, '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.options.json) {
|
|
|
|
|
console.log(JSON.stringify({ event: 'fileStart', filename, runtime, index, total }));
|
|
|
|
|
this.logJson({ event: 'fileStart', filename, runtime, index, total });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -133,7 +184,7 @@ export class TsTestLogger {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.options.json) {
|
|
|
|
|
console.log(JSON.stringify({ event: 'testResult', testName, passed, duration, error }));
|
|
|
|
|
this.logJson({ event: 'testResult', testName, passed, duration, error });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -157,7 +208,7 @@ export class TsTestLogger {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.options.json) {
|
|
|
|
|
console.log(JSON.stringify({ event: 'fileEnd', passed, failed, duration }));
|
|
|
|
|
this.logJson({ event: 'fileEnd', passed, failed, duration });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -167,23 +218,45 @@ export class TsTestLogger {
|
|
|
|
|
const color = failed === 0 ? 'green' : 'red';
|
|
|
|
|
this.log(this.format(` Summary: ${passed}/${total} ${status}`, color));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear the current test log file reference only if using --logfile
|
|
|
|
|
if (this.options.logFile) {
|
|
|
|
|
this.currentTestLogFile = null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TAP output forwarding
|
|
|
|
|
// TAP output forwarding (for TAP protocol messages)
|
|
|
|
|
tapOutput(message: string, isError: boolean = false) {
|
|
|
|
|
if (this.options.json) return;
|
|
|
|
|
|
|
|
|
|
if (this.options.verbose || isError) {
|
|
|
|
|
const prefix = isError ? ' ⚠️ ' : ' ';
|
|
|
|
|
const color = isError ? 'red' : 'dim';
|
|
|
|
|
this.log(this.format(`${prefix}${message}`, color));
|
|
|
|
|
// Never show raw TAP protocol messages in console
|
|
|
|
|
// They are already processed by TapParser and shown in our format
|
|
|
|
|
|
|
|
|
|
// Always log to test file if --logfile is specified
|
|
|
|
|
if (this.currentTestLogFile) {
|
|
|
|
|
this.logToTestFile(` ${message}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Console output from test files (non-TAP output)
|
|
|
|
|
testConsoleOutput(message: string) {
|
|
|
|
|
if (this.options.json) return;
|
|
|
|
|
|
|
|
|
|
// Show console output from test files only in verbose mode
|
|
|
|
|
if (this.options.verbose) {
|
|
|
|
|
this.log(this.format(` ${message}`, 'dim'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Always log to test file if --logfile is specified
|
|
|
|
|
if (this.currentTestLogFile) {
|
|
|
|
|
this.logToTestFile(` ${message}`);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Browser console
|
|
|
|
|
browserConsole(message: string, level: string = 'log') {
|
|
|
|
|
if (this.options.json) {
|
|
|
|
|
console.log(JSON.stringify({ event: 'browserConsole', message, level }));
|
|
|
|
|
this.logJson({ event: 'browserConsole', message, level });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -207,7 +280,7 @@ export class TsTestLogger {
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (this.options.json) {
|
|
|
|
|
console.log(JSON.stringify({ event: 'summary', summary }));
|
|
|
|
|
this.logJson({ event: 'summary', summary });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -266,7 +339,7 @@ export class TsTestLogger {
|
|
|
|
|
// Error display
|
|
|
|
|
error(message: string, file?: string, stack?: string) {
|
|
|
|
|
if (this.options.json) {
|
|
|
|
|
console.log(JSON.stringify({ event: 'error', message, file, stack }));
|
|
|
|
|
this.logJson({ event: 'error', message, file, stack });
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|