feat(logger): Improve logging output and add --logfile support for persistent logs

This commit is contained in:
2025-05-15 17:50:25 +00:00
parent dc0f859fad
commit 56f0f0be16
8 changed files with 137 additions and 221 deletions

View File

@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@git.zone/tstest',
version: '1.2.0',
version: '1.3.0',
description: 'a test utility to run tests that match test/**/*.ts'
}

View File

@ -33,9 +33,8 @@ export const runCli = async () => {
logOptions.json = true;
break;
case '--log-file':
if (i + 1 < args.length) {
logOptions.logFile = args[++i];
}
case '--logfile':
logOptions.logFile = true; // Set this as a flag, not a value
break;
default:
if (!arg.startsWith('-')) {
@ -52,7 +51,7 @@ export const runCli = async () => {
console.error(' --verbose, -v Verbose output');
console.error(' --no-color Disable colored output');
console.error(' --json Output results as JSON');
console.error(' --log-file Write logs to file');
console.error(' --logfile Write logs to .nogit/testlogs/[testfile].log');
process.exit(1);
}

View File

@ -102,7 +102,8 @@ export class TapParser {
this.activeTapTestResult.addLogLine(logLine);
}
if (this.logger) {
this.logger.tapOutput(logLine);
// This is console output from the test file, not TAP protocol
this.logger.testConsoleOutput(logLine);
}
}

View File

@ -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;
}