import { TestInfo } from '@playwright/test'; import fs from 'fs/promises'; import path from 'path'; export enum VerbosityLevel { SILENT = 0, MINIMAL = 1, NORMAL = 2, VERBOSE = 3, DEBUG = 4, ERROR = 5 // Added ERROR level for error logging } export interface VerbosityOptions { consoleOutput: boolean; screenshots: boolean; htmlDumps: boolean; fileLogs: boolean; performanceMetrics: boolean; } export class VerbosityController { private static instance: VerbosityController; private currentLevel: VerbosityLevel; private options: Map; private constructor() { this.currentLevel = VerbosityLevel.NORMAL; // Default level this.options = new Map([ [VerbosityLevel.SILENT, { consoleOutput: false, screenshots: false, htmlDumps: false, fileLogs: false, performanceMetrics: false }], [VerbosityLevel.MINIMAL, { consoleOutput: true, screenshots: false, htmlDumps: false, fileLogs: true, performanceMetrics: false }], [VerbosityLevel.NORMAL, { consoleOutput: true, screenshots: true, htmlDumps: true, fileLogs: true, performanceMetrics: true }], [VerbosityLevel.VERBOSE, { consoleOutput: true, screenshots: true, htmlDumps: true, fileLogs: true, performanceMetrics: true }], [VerbosityLevel.DEBUG, { consoleOutput: true, screenshots: true, htmlDumps: true, fileLogs: true, performanceMetrics: true }], [VerbosityLevel.ERROR, { consoleOutput: true, screenshots: true, htmlDumps: true, fileLogs: true, performanceMetrics: true }] ]); } static getInstance(): VerbosityController { if (!VerbosityController.instance) { VerbosityController.instance = new VerbosityController(); } return VerbosityController.instance; } setLevel(level: VerbosityLevel): void { this.currentLevel = level; } getLevel(): VerbosityLevel { return this.currentLevel; } shouldLog(messageLevel: VerbosityLevel): boolean { return messageLevel <= this.currentLevel; } getOptions(): VerbosityOptions { return this.options.get(this.currentLevel) || this.options.get(VerbosityLevel.NORMAL)!; } async log( message: string, level: VerbosityLevel, testInfo?: TestInfo, error?: Error ): Promise { if (!this.shouldLog(level)) return; const options = this.getOptions(); if (!options.consoleOutput) return; const timestamp = new Date().toISOString(); const prefix = `[${timestamp}] [${VerbosityLevel[level]}]`; // Console output if (level === VerbosityLevel.ERROR || error) { console.error(`${prefix} ${message}`, error || ''); } else { console.log(`${prefix} ${message}`); } // File logging if (options.fileLogs && testInfo) { await this.logToFile(testInfo, `${prefix} ${message}\n${error ? error.stack : ''}\n`); } } async shouldTakeScreenshot(testInfo: TestInfo): Promise { const options = this.getOptions(); if (!options.screenshots) return false; // Additional conditions based on verbosity level switch (this.currentLevel) { case VerbosityLevel.VERBOSE: case VerbosityLevel.DEBUG: return true; // Take screenshots for all actions case VerbosityLevel.NORMAL: return testInfo.status !== 'passed'; // Only for non-passing tests case VerbosityLevel.ERROR: return testInfo.status === 'failed'; // Only for failed tests default: return false; } } async shouldCaptureHtmlDump(testInfo: TestInfo): Promise { const options = this.getOptions(); if (!options.htmlDumps) return false; // Additional conditions based on verbosity level switch (this.currentLevel) { case VerbosityLevel.DEBUG: case VerbosityLevel.VERBOSE: return true; // Capture all HTML dumps case VerbosityLevel.NORMAL: return testInfo.status !== 'passed'; // Only for non-passing tests case VerbosityLevel.ERROR: return testInfo.status === 'failed'; // Only for failed tests default: return false; } } shouldCapturePerformanceMetrics(): boolean { return this.getOptions().performanceMetrics; } private async logToFile(testInfo: TestInfo, message: string): Promise { const logDir = path.join(testInfo.project.outputDir, 'logs'); const logFile = path.join(logDir, `${testInfo.testId}.log`); await fs.mkdir(logDir, { recursive: true }); await fs.appendFile(logFile, message); } } // Command line argument parser for verbosity export function parseVerbosityArgs(args: string[]): VerbosityLevel { const verbosityArg = args.find(arg => arg.startsWith('--verbosity=') || arg.startsWith('-v=') ); if (!verbosityArg) return VerbosityLevel.NORMAL; const value = verbosityArg.split('=')[1].toUpperCase(); const level = VerbosityLevel[value as keyof typeof VerbosityLevel]; return typeof level === 'number' ? level : VerbosityLevel.NORMAL; }