feat(logger): Improve logging output and add --logfile support for persistent logs
This commit is contained in:
		| @@ -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; | ||||
|     } | ||||
|      | ||||
|   | ||||
		Reference in New Issue
	
	Block a user