@testdino/playwright 1.0.10 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/reporter/index.ts","../src/streaming/websocket.ts","../src/reporter/errors.ts","../src/streaming/http.ts","../src/utils/index.ts","../src/streaming/buffer.ts","../src/metadata/git.ts","../src/metadata/base.ts","../src/metadata/ci.ts","../src/metadata/system.ts","../src/metadata/playwright.ts","../src/metadata/index.ts","../src/uploads/sas-token-client.ts","../src/uploads/artifact-uploader.ts","../src/code-coverage/merger.ts","../src/code-coverage/compact.ts","../src/code-coverage/html-report.ts","../src/code-coverage/fixtures.ts","../src/reporter/log.ts"],"sourcesContent":["/**\n * TestDino Playwright Reporter\n * Streams test execution data in real-time\n */\n\nimport type {\n Reporter,\n FullConfig,\n Suite,\n TestCase,\n TestResult,\n TestStep,\n FullResult,\n TestError,\n} from '@playwright/test/reporter';\nimport { randomUUID } from 'crypto';\nimport { readFileSync, existsSync } from 'fs';\nimport { WebSocketClient } from '../streaming/websocket.js';\nimport { HttpClient } from '../streaming/http.js';\nimport { EventBuffer } from '../streaming/buffer.js';\nimport { createMetadataCollector } from '../metadata/index.js';\nimport type {\n TestdinoConfig,\n TestEvent,\n TestRunBeginEvent,\n TestBeginEvent,\n TestStepBeginEvent,\n TestStepEndEvent,\n TestEndEvent,\n TestRunEndEvent,\n TestRunErrorEvent,\n TestConsoleOutEvent,\n TestConsoleErrEvent,\n CoverageDataEvent,\n RunMetadata,\n Annotation,\n} from '../types/index.js';\nimport { isQuotaError } from './errors.js';\nimport { SASTokenClient, ArtifactUploader, type UploadResult, type AttachmentForUpload } from '../uploads/index.js';\nimport { CoverageMerger, extractCompactCounts, generateIstanbulHtmlReport } from '../code-coverage/index.js';\nimport type { CoverageFragment } from '../code-coverage/index.js';\nimport { createReporterLog, type ReporterLog, type RunSummaryData } from './log.js';\nimport { isDebugEnabled } from '../utils/index.js';\n\n// Maximum console chunk size (10KB in characters)\nconst MAX_CONSOLE_CHUNK_SIZE = 10_000;\n\n// Maximum number of events to buffer\nconst MAX_BUFFER_SIZE = 10;\n\n// File count threshold for coverage size warning\nconst COVERAGE_FILE_COUNT_WARNING = 500;\n\nexport class TestdinoReporter implements Reporter {\n private config: TestdinoConfig;\n private wsClient: WebSocketClient | null = null;\n private httpClient: HttpClient | null = null;\n private buffer: EventBuffer | null = null;\n private runId: string;\n private useHttpFallback = false;\n private sequenceNumber = 0;\n\n // Shard and timing info for interruption handling\n private shardInfo?: { current: number; total: number };\n private runStartTime?: number;\n\n // Signal handler management\n private sigintHandler?: () => void;\n private sigtermHandler?: () => void;\n private isShuttingDown = false;\n\n // Quota tracking\n private quotaExceeded = false;\n\n // Session ID from HTTP auth, passed to WebSocket for session reuse\n private sessionId: string | null = null;\n\n // Artifact upload\n private artifactUploader: ArtifactUploader | null = null;\n private artifactsEnabled = true; // Default: enabled\n\n // Deferred initialization - resolves true on success, false on failure\n private initPromise: Promise<boolean> | null = null;\n private initFailed = false;\n\n // Promises for onTestEnd; must be awaited in onEnd to prevent data loss\n private pendingTestEndPromises: Set<Promise<void>> = new Set();\n\n // Logger for consistent output\n private log: ReporterLog;\n\n // Coverage collection\n private coverageEnabled = false;\n private coverageMerger: CoverageMerger | null = null;\n private warnedCoverageDisconnect = false;\n private coverageThresholdFailed = false;\n\n // Test result tracking for summary\n private testCounts = { passed: 0, failed: 0, skipped: 0, timedOut: 0, interrupted: 0, flaky: 0, retried: 0 };\n private totalTests = 0;\n private lastCoverageEvent: CoverageDataEvent | null = null;\n\n // Detailed tracking for summary output\n private projectNames = new Set<string>();\n private runMetadata: RunMetadata | null = null;\n private workerCount = 0;\n\n constructor(config: TestdinoConfig = {}) {\n // Load CLI config if available\n const cliConfig = this.loadCliConfig();\n\n // Merge configs: CLI config takes precedence over constructor config\n this.config = { ...config, ...cliConfig };\n\n this.runId = randomUUID();\n\n // Initialize logger with debug setting from config\n this.log = createReporterLog({ debug: this.config.debug ?? false });\n\n // Initialize coverage merger if coverage is enabled\n this.coverageEnabled = this.config.coverage?.enabled ?? false;\n if (this.coverageEnabled) {\n this.coverageMerger = new CoverageMerger({\n include: this.config.coverage?.include,\n exclude: this.config.coverage?.exclude,\n onError: (msg) => this.log.warn(msg),\n });\n }\n\n // Create buffer early to collect events before async init; flush waits for initPromise.\n this.buffer = new EventBuffer({\n maxSize: MAX_BUFFER_SIZE,\n onFlush: async (events) => {\n if (this.initPromise) {\n const success = await this.initPromise;\n if (!success) return; // Init failed - discard events\n }\n await this.sendEvents(events);\n },\n });\n }\n\n /**\n * Load configuration from CLI temp file if available\n */\n private loadCliConfig(): TestdinoConfig {\n const cliConfigPath = process.env.TESTDINO_CLI_CONFIG_PATH;\n\n if (!cliConfigPath) {\n return {};\n }\n\n try {\n if (!existsSync(cliConfigPath)) {\n return {};\n }\n\n const configContent = readFileSync(cliConfigPath, 'utf-8');\n const cliConfig = JSON.parse(configContent) as Record<string, unknown>;\n\n const mappedConfig: TestdinoConfig = {};\n\n if (cliConfig.token !== undefined && typeof cliConfig.token === 'string') {\n mappedConfig.token = cliConfig.token;\n }\n\n if (cliConfig.serverUrl !== undefined && typeof cliConfig.serverUrl === 'string') {\n mappedConfig.serverUrl = cliConfig.serverUrl;\n }\n\n if (cliConfig.debug !== undefined && typeof cliConfig.debug === 'boolean') {\n mappedConfig.debug = cliConfig.debug;\n }\n\n // CI run ID for grouping sharded runs\n if (cliConfig.ciRunId !== undefined && typeof cliConfig.ciRunId === 'string') {\n mappedConfig.ciRunId = cliConfig.ciRunId;\n }\n\n // Handle artifacts flag\n if (cliConfig.artifacts !== undefined && typeof cliConfig.artifacts === 'boolean') {\n mappedConfig.artifacts = cliConfig.artifacts;\n }\n\n // Handle coverage config\n if (typeof cliConfig.coverage === 'object' && cliConfig.coverage !== null) {\n mappedConfig.coverage = cliConfig.coverage as import('../code-coverage/types.js').CoverageConfig;\n }\n\n return mappedConfig;\n } catch (error) {\n // Silently fail - CLI config is optional\n if (isDebugEnabled()) {\n console.warn(\n '⚠️ TestDino: Failed to load CLI config:',\n error instanceof Error ? error.message : String(error)\n );\n }\n return {};\n }\n }\n\n /**\n * Called once before running tests\n */\n async onBegin(config: FullConfig, suite: Suite): Promise<void> {\n // Check if we're a duplicate instance (CLI-injected when already in config)\n if (config && this.isDuplicateInstance(config.reporter)) {\n if (this.config.debug) {\n this.log.debug('Reporter already configured in playwright.config, skipping duplicate instance');\n }\n return;\n }\n\n const token = this.getToken();\n\n if (!token) {\n this.printConfigurationError('Token is required but not provided', [\n 'Set environment variable: export TESTDINO_TOKEN=your-token',\n 'Add to playwright.config.ts: token: \"your-token\"',\n 'Use CLI wrapper: npx tdpw test --token your-token',\n ]);\n return;\n }\n\n // Store shard info and run start time for potential interruption handling\n if (config?.shard) {\n this.shardInfo = {\n current: config.shard.current,\n total: config.shard.total,\n };\n }\n this.runStartTime = Date.now();\n\n // Kick off async initialization without awaiting - Playwright does not await onBegin. Events will be buffered and transmitted once init completes.\n this.initPromise = this.performAsyncInit(config, suite, token);\n }\n\n /**\n * Perform all async initialization: metadata, auth, WebSocket, artifacts, run:begin\n *\n * Buffer's onFlush callback awaits this promise before transmitting events,\n * ensuring no data is sent before authentication and connection are established.\n *\n * @param config - Playwright FullConfig for metadata collection\n * @param suite - Playwright Suite for skeleton building\n * @param token - Authentication token\n * @returns true on success, false on failure\n */\n private async performAsyncInit(config: FullConfig, suite: Suite, token: string): Promise<boolean> {\n const serverUrl = this.getServerUrl();\n\n try {\n // Collect metadata before initializing streaming\n const metadata = await this.collectMetadata(config, suite);\n this.runMetadata = metadata;\n this.workerCount = config.workers;\n\n // Initialize HTTP client and authenticate\n this.httpClient = new HttpClient({ token, serverUrl });\n const auth = await this.httpClient.authenticate();\n this.sessionId = auth.sessionId;\n this.log.success('Authenticated successfully');\n if (this.config.debug) {\n this.log.debug(`Session ${this.sessionId} — reusing for WebSocket`);\n }\n\n // Initialize WebSocket client\n this.wsClient = new WebSocketClient({\n token,\n sessionId: this.sessionId ?? undefined,\n serverUrl: this.getWebSocketUrl(),\n debug: this.config.debug,\n onConnected: () => {\n this.log.debug('WebSocket connected');\n },\n onDisconnected: () => {\n this.log.debug('WebSocket disconnected');\n },\n onError: (error) => {\n // Handle quota errors from WebSocket (run:begin rejection)\n if (isQuotaError(error)) {\n // Display error banner only once\n if (!this.quotaExceeded) {\n this.quotaExceeded = true;\n this.initFailed = true;\n this.printQuotaError(error);\n }\n } else {\n // Log other WebSocket errors\n this.log.error(`WebSocket error: ${error.message}`);\n }\n },\n });\n\n // Try to connect WebSocket\n try {\n await this.wsClient.connect();\n } catch {\n this.log.warn('WebSocket connection failed, using HTTP fallback');\n this.useHttpFallback = true;\n }\n\n // Initialize artifact uploader if enabled (default: true)\n this.artifactsEnabled = this.config.artifacts !== false;\n if (this.artifactsEnabled) {\n await this.initializeArtifactUploader(token, serverUrl);\n }\n\n // Send run:begin directly (not via buffer) to guarantee it arrives before\n // any buffered test events. initPromise resolves only after this send,\n // so buffer's onFlush will transmit buffered events after run:begin.\n const beginEvent: TestRunBeginEvent = {\n type: 'run:begin',\n runId: this.runId,\n metadata,\n ciRunId: this.config.ciRunId, // Include ciRunId for shard grouping\n ...this.getEventMetadata(),\n };\n\n // Debug logging for run:begin event details\n if (this.config.debug) {\n const shardInfo = config?.shard ? `${config.shard.current}/${config.shard.total}` : 'none';\n const skeletonInfo = metadata.skeleton\n ? `${metadata.skeleton.totalTests} tests, ${metadata.skeleton.suites?.length ?? 0} suites`\n : 'not available';\n this.log.debug(\n `run:begin runId=${this.runId} ciRunId=${this.config.ciRunId ?? 'none'} shard=${shardInfo} skeleton=(${skeletonInfo})`\n );\n }\n\n await this.sendEvents([beginEvent]);\n\n // Register signal handlers for graceful shutdown on interruption\n this.registerSignalHandlers();\n\n return true;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.initFailed = true;\n\n // Check if this is a quota exhausted error (402)\n if (error instanceof Error && 'code' in error && isQuotaError(error)) {\n this.quotaExceeded = true;\n this.printQuotaError(error);\n } else if (\n errorMessage.includes('Authentication failed') ||\n errorMessage.includes('401') ||\n errorMessage.includes('Unauthorized')\n ) {\n this.printConfigurationError('Authentication failed - Invalid or expired token', [\n 'Verify your token is correct',\n 'Check if the token has expired',\n 'Generate a new token from TestDino dashboard',\n `Server URL: ${serverUrl}`,\n ]);\n } else {\n // Generic initialization error\n this.printConfigurationError(`Failed to initialize TestDino reporter: ${errorMessage}`, [\n 'Check if TestDino server is running and accessible',\n `Verify server URL is correct: ${serverUrl}`,\n 'Check network connectivity',\n 'Review server logs for details',\n ]);\n }\n\n return false;\n }\n }\n\n /**\n * Called for each test before it starts\n */\n async onTestBegin(test: TestCase, result: TestResult): Promise<void> {\n if (!this.initPromise || this.initFailed) return;\n\n const event: TestBeginEvent = {\n type: 'test:begin',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Test identification\n testId: test.id,\n title: test.title,\n titlePath: test.titlePath(),\n\n // Location information\n location: {\n file: test.location.file,\n line: test.location.line,\n column: test.location.column,\n },\n\n // Test configuration\n tags: test.tags,\n expectedStatus: test.expectedStatus,\n timeout: test.timeout,\n retries: test.retries,\n annotations: this.extractAnnotations(test.annotations),\n\n // Execution context\n retry: result.retry,\n workerIndex: result.workerIndex,\n parallelIndex: result.parallelIndex,\n repeatEachIndex: test.repeatEachIndex,\n\n // Hierarchy information\n parentSuite: this.extractParentSuite(test.parent),\n\n // Timing\n startTime: result.startTime.getTime(),\n };\n\n await this.buffer!.add(event);\n }\n\n /**\n * Called when a test step begins\n */\n async onStepBegin(test: TestCase, result: TestResult, step: TestStep): Promise<void> {\n if (!this.initPromise || this.initFailed) return;\n\n const event: TestStepBeginEvent = {\n type: 'step:begin',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Step Identification\n testId: test.id,\n stepId: `${test.id}-${step.titlePath().join('-')}`,\n title: step.title,\n titlePath: step.titlePath(),\n\n // Step Classification\n category: step.category,\n\n // Location Information\n location: step.location\n ? {\n file: step.location.file,\n line: step.location.line,\n column: step.location.column,\n }\n : undefined,\n\n // Hierarchy Information\n parentStep: step.parent ? this.extractParentStep(step.parent) : undefined,\n\n // Timing\n startTime: step.startTime.getTime(),\n\n // Retry Information\n retry: result.retry,\n\n // Worker Information\n workerIndex: result.workerIndex,\n parallelIndex: result.parallelIndex,\n\n // Annotations\n annotations: this.extractAnnotations(step.annotations),\n };\n\n await this.buffer!.add(event);\n }\n\n /**\n * Called when a test step ends\n */\n async onStepEnd(test: TestCase, result: TestResult, step: TestStep): Promise<void> {\n if (!this.initPromise || this.initFailed) return;\n\n // Derive status from error presence\n const status: 'passed' | 'failed' = step.error ? 'failed' : 'passed';\n\n const event: TestStepEndEvent = {\n type: 'step:end',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Step Identification\n testId: test.id,\n stepId: `${test.id}-${step.titlePath().join('-')}`,\n title: step.title,\n titlePath: step.titlePath(),\n\n // Timing\n duration: step.duration,\n\n // Error Information\n error: this.extractError(step.error),\n\n // Status Information\n status,\n\n // Child Steps Summary\n childSteps: this.extractChildSteps(step),\n\n // Attachments Metadata\n attachments: this.extractAttachments(step),\n\n // Annotations\n annotations: this.extractAnnotations(step.annotations),\n\n // Retry Information\n retry: result.retry,\n\n // Worker Information\n workerIndex: result.workerIndex,\n parallelIndex: result.parallelIndex,\n };\n\n await this.buffer!.add(event);\n }\n\n /**\n * Called after each test.\n * Playwright does not await onTestEnd promises—pending work is awaited in onEnd.\n */\n onTestEnd(test: TestCase, result: TestResult): void {\n if (!this.initPromise || this.initFailed) return;\n\n // Warn when fixture sends coverage data but coverage is disabled in config\n if (!this.coverageEnabled && !this.warnedCoverageDisconnect) {\n const hasCoverageAttachment = result.attachments.some((a) => a.name === 'testdino-coverage');\n if (hasCoverageAttachment) {\n this.log.warn(\n 'Coverage data detected but coverage.enabled is false — ' +\n 'set coverage: { enabled: true } to collect coverage'\n );\n this.warnedCoverageDisconnect = true;\n }\n }\n\n // Extract and merge coverage fragment (if coverage enabled)\n if (this.coverageEnabled && this.coverageMerger) {\n this.extractCoverageFromResult(result, this.coverageMerger);\n }\n\n // Track project name\n const projectName = this.getProjectName(test);\n if (projectName) {\n this.projectNames.add(projectName);\n }\n\n // Track test result counts for summary table display\n if (result.retry > 0) {\n this.testCounts.retried++;\n }\n // Only count final status on the last attempt (for summary table display)\n const isFinalAttempt = result.status === 'passed' || result.retry === test.retries;\n if (isFinalAttempt) {\n this.totalTests++;\n const outcome = test.outcome();\n if (outcome === 'flaky') {\n this.testCounts.flaky++;\n } else if (result.status === 'passed') {\n this.testCounts.passed++;\n } else if (result.status === 'failed') {\n this.testCounts.failed++;\n } else if (result.status === 'skipped') {\n this.testCounts.skipped++;\n } else if (result.status === 'timedOut') {\n this.testCounts.timedOut++;\n } else if (result.status === 'interrupted') {\n this.testCounts.interrupted++;\n }\n }\n\n // Create and track the async work - Playwright does not await onTestEnd\n const workPromise = this.processTestEnd(test, result);\n this.pendingTestEndPromises.add(workPromise);\n\n // Remove from tracking when complete (success or failure)\n workPromise.finally(() => {\n this.pendingTestEndPromises.delete(workPromise);\n });\n }\n\n /**\n * Process test end event asynchronously\n * Uploads attachments and adds test:end event to buffer\n */\n private async processTestEnd(test: TestCase, result: TestResult): Promise<void> {\n try {\n // Upload attachments and get Azure URLs (if upload enabled)\n const attachmentsWithUrls = await this.uploadAttachments(result.attachments, test.id);\n\n const event: TestEndEvent = {\n type: 'test:end',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Test Identification\n testId: test.id,\n\n // Status Information\n status: result.status as 'passed' | 'failed' | 'skipped' | 'timedOut' | 'interrupted',\n outcome: test.outcome(),\n\n // Timing\n duration: result.duration,\n\n // Execution Context\n retry: result.retry,\n\n // Worker Information\n workerIndex: result.workerIndex,\n parallelIndex: result.parallelIndex,\n\n // Test Metadata\n annotations: this.extractAnnotations(result.annotations),\n\n // Error Information\n errors: result.errors\n .map((e) => this.extractError(e))\n .filter((e): e is NonNullable<typeof e> => e !== undefined),\n\n // Step Summary\n steps: this.extractTestStepsSummary(result),\n\n // Attachments Metadata (with Azure URLs when uploaded)\n attachments: attachmentsWithUrls,\n\n // Console Output\n stdout: result.stdout.length > 0 ? this.extractConsoleOutput(result.stdout) : undefined,\n stderr: result.stderr.length > 0 ? this.extractConsoleOutput(result.stderr) : undefined,\n };\n\n await this.buffer!.add(event);\n } catch (error) {\n // Log error but don't throw - one test failure shouldn't break others\n this.log.error(`Failed to process test:end event: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n /**\n * Called after all tests complete\n */\n async onEnd(result: FullResult): Promise<{ status?: FullResult['status'] } | void> {\n // If quota exceeded, skip event transmission but still cleanup\n if (this.quotaExceeded) {\n // Await pending test:end promises for graceful cleanup (even though we won't send events)\n if (this.pendingTestEndPromises.size > 0) {\n await Promise.allSettled(Array.from(this.pendingTestEndPromises));\n }\n this.log.success('Tests completed (quota limit reached; not streamed to TestDino)');\n this.wsClient?.close();\n this.removeSignalHandlers();\n return;\n }\n\n // If init was never started (no token, duplicate), nothing to do\n if (!this.initPromise) return;\n\n // Ensure initialization completes before flushing events\n const success = await this.initPromise;\n if (!success) {\n // Init failed - discard any buffered events and pending promises, cleanup\n this.buffer?.clear();\n this.pendingTestEndPromises.clear();\n this.wsClient?.close();\n this.removeSignalHandlers();\n return;\n }\n\n // Await completion of all pending test:end events before flushing final events\n if (this.pendingTestEndPromises.size > 0) {\n this.log.debug(`Waiting for ${this.pendingTestEndPromises.size} pending test:end events...`);\n // Use Promise.allSettled for error isolation - one failed test shouldn't block others\n await Promise.allSettled(Array.from(this.pendingTestEndPromises));\n }\n\n // Send coverage data before run:end (if coverage collected)\n if (this.coverageEnabled && this.coverageMerger?.hasCoverage) {\n try {\n // Build coverage event (includes summary, files, and compact counts for sharded runs)\n const coverageEvent = this.buildCoverageEvent(this.coverageMerger);\n await this.buffer!.add(coverageEvent);\n\n // Store for local console summary table\n this.lastCoverageEvent = coverageEvent;\n\n // Check coverage thresholds\n const thresholdFailures = this.checkCoverageThresholds(coverageEvent.summary);\n if (thresholdFailures.length > 0) {\n this.coverageThresholdFailed = true;\n }\n } catch (error) {\n // Log but don't fail the run\n this.log.warn(`Failed to build coverage event: ${error instanceof Error ? error.message : String(error)}`);\n }\n\n // Generate local HTML report if configured (separate try-catch to avoid\n // misleading \"Failed to build coverage event\" when only report generation fails)\n if (this.coverageMerger?.hasCoverage) {\n try {\n const outputDir = './coverage';\n const reportPath = await generateIstanbulHtmlReport(this.coverageMerger, {\n outputDir,\n });\n this.log.info(`Coverage Report: ${reportPath}`);\n } catch (error) {\n this.log.warn(\n `Failed to generate local HTML report: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n } else if (this.coverageEnabled && !this.coverageMerger?.hasCoverage) {\n this.log.warn(\n 'Coverage enabled but no data was collected. Ensure your app is instrumented ' +\n 'with Istanbul (babel-plugin-istanbul) and tests use { test } from @testdino/playwright'\n );\n }\n\n const event: TestRunEndEvent = {\n type: 'run:end',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Run Status\n status: result.status as 'passed' | 'failed' | 'timedout' | 'interrupted',\n\n // Timing\n duration: result.duration,\n startTime: result.startTime.getTime(),\n\n // Shard information\n shard: this.shardInfo,\n };\n\n // Flush any remaining buffered events first\n try {\n await this.buffer!.flush();\n } catch (error) {\n this.log.error(`Failed to flush buffered events: ${error}`);\n }\n\n // Send run:end and wait for server ACK to ensure delivery\n // This prevents data loss when shards execute very fast\n let delivered = false;\n try {\n if (this.wsClient?.isConnected()) {\n await this.wsClient.sendAndWaitForAck(event);\n this.log.success('All events delivered (server acknowledged)');\n delivered = true;\n } else if (this.httpClient) {\n // HTTP mode (no ACK confirmation available)\n await this.httpClient.sendEvents([event]);\n this.log.success('All events sent');\n delivered = true;\n } else {\n this.log.warn('No connection available to send run:end event');\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n if (errorMessage.includes('ACK timeout') || errorMessage.includes('Connection closed')) {\n // ACK timeout or connection lost - retry via HTTP as fallback\n if (this.httpClient && !delivered) {\n try {\n this.log.warn('WebSocket ACK timeout, retrying via HTTP...');\n await this.httpClient.sendEvents([event]);\n this.log.success('All events sent (HTTP fallback)');\n delivered = true;\n } catch (httpError) {\n const httpErrorMessage = httpError instanceof Error ? httpError.message : String(httpError);\n this.log.error(`HTTP fallback also failed: ${httpErrorMessage}`);\n }\n } else if (!delivered) {\n this.log.warn('Server did not acknowledge run:end in time, events may be pending');\n }\n } else {\n this.log.error(`Failed to send run:end event: ${errorMessage}`);\n }\n }\n\n // Print comprehensive run summary\n const summaryData: RunSummaryData = {\n runId: this.runId,\n runMetadata: this.runMetadata,\n testCounts: this.testCounts,\n totalTests: this.totalTests,\n workerCount: this.workerCount,\n projectNames: this.projectNames,\n shardInfo: this.shardInfo,\n useHttpFallback: this.useHttpFallback,\n lastCoverageEvent: this.lastCoverageEvent,\n };\n await this.log.printRunSummary(result, delivered, summaryData);\n\n // Check if coverage thresholds were not met\n if (this.coverageThresholdFailed && this.lastCoverageEvent) {\n const failures = this.checkCoverageThresholds(this.lastCoverageEvent.summary);\n this.log.error('Coverage thresholds not met:');\n for (const msg of failures) {\n this.log.error(` ${msg}`);\n }\n this.wsClient?.close();\n this.removeSignalHandlers();\n return { status: 'failed' };\n }\n\n // Close connections and remove signal handlers\n this.wsClient?.close();\n this.removeSignalHandlers();\n }\n\n /**\n * Called on global errors\n */\n async onError(error: TestError): Promise<void> {\n if (!this.initPromise || this.initFailed) return;\n\n const event: TestRunErrorEvent = {\n type: 'run:error',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Error Information (with cause support)\n error: this.extractGlobalError(error),\n };\n\n await this.buffer!.add(event);\n }\n\n /**\n * Called when standard output is produced in worker process\n */\n async onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult): Promise<void> {\n if (!this.initPromise || this.initFailed) return;\n\n const { text, truncated } = this.truncateChunk(chunk);\n\n const event: TestConsoleOutEvent = {\n type: 'console:out',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Console Output\n text,\n\n // Test Association (optional)\n testId: test?.id,\n retry: result?.retry,\n\n // Truncation Indicator\n truncated,\n };\n\n await this.buffer!.add(event);\n }\n\n /**\n * Called when standard error is produced in worker process\n */\n async onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult): Promise<void> {\n if (!this.initPromise || this.initFailed) return;\n\n const { text, truncated } = this.truncateChunk(chunk);\n\n const event: TestConsoleErrEvent = {\n type: 'console:err',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Console Error Output\n text,\n\n // Test Association (optional)\n testId: test?.id,\n retry: result?.retry,\n\n // Truncation Indicator\n truncated,\n };\n\n await this.buffer!.add(event);\n }\n\n /**\n * Indicates whether this reporter outputs to stdout/stderr\n * Returns false to allow Playwright to add its own terminal output\n */\n printsToStdio(): boolean {\n return false;\n }\n\n /**\n * Send events via WebSocket or HTTP fallback\n */\n private async sendEvents(events: TestEvent[]): Promise<void> {\n if (events.length === 0) return;\n\n if (this.config.debug) {\n for (const event of events) {\n if (event.type === 'test:begin') {\n const testBeginEvent = event as TestBeginEvent;\n this.log.debug(\n `Sending event type=${event.type} sequence=${event.sequence} runId=${event.runId} testId=${testBeginEvent.testId} retry=${testBeginEvent.retry} parallelIndex=${testBeginEvent.parallelIndex} title=${testBeginEvent.title}`\n );\n } else if (event.type === 'test:end') {\n const testEndEvent = event as TestEndEvent;\n this.log.debug(\n `Sending event type=${event.type} sequence=${event.sequence} runId=${event.runId} testId=${testEndEvent.testId} retry=${testEndEvent.retry} parallelIndex=${testEndEvent.parallelIndex}`\n );\n } else {\n this.log.debug(`Sending event type=${event.type} sequence=${event.sequence} runId=${event.runId}`);\n }\n }\n }\n\n // Try WebSocket first if available\n if (!this.useHttpFallback && this.wsClient?.isConnected()) {\n try {\n await this.wsClient.sendBatch(events);\n return;\n } catch {\n this.log.warn('WebSocket send failed, switching to HTTP fallback');\n this.useHttpFallback = true;\n }\n }\n\n // Use HTTP fallback\n if (this.httpClient) {\n try {\n await this.httpClient.sendEvents(events);\n } catch (error) {\n this.log.error(`Failed to send events via HTTP: ${error}`);\n throw error;\n }\n }\n }\n\n /**\n * Get token from config or environment\n */\n private getToken(): string | undefined {\n return this.config.token || process.env.TESTDINO_TOKEN;\n }\n\n /**\n * Get server URL from config or environment\n */\n private getServerUrl(): string {\n const baseUrl = this.config.serverUrl || process.env.TESTDINO_SERVER_URL || 'https://api.testdino.com';\n\n // Ensure the URL ends with /api/reporter for CLI endpoints\n return baseUrl.endsWith('/api/reporter') ? baseUrl : `${baseUrl}/api/reporter`;\n }\n\n private getWebSocketUrl(): string {\n const baseUrl = this.config.serverUrl || process.env.TESTDINO_SERVER_URL || 'https://api.testdino.com';\n\n // Remove /api/reporter suffix if present for WebSocket URL\n const wsBaseUrl = baseUrl.replace('/api/reporter', '');\n return wsBaseUrl.replace('http', 'ws');\n }\n\n /**\n * Collect metadata for the test run\n */\n private async collectMetadata(playwrightConfig: FullConfig, playwrightSuite: Suite): Promise<RunMetadata> {\n try {\n const metadataCollector = createMetadataCollector(playwrightConfig, playwrightSuite);\n const result = await metadataCollector.collectAll();\n\n if (result.failureCount > 0) {\n this.log.warn(`${result.failureCount}/${result.results.length} metadata collectors failed`);\n }\n\n // Build skeleton from suite\n const skeleton = metadataCollector.buildSkeleton(playwrightSuite);\n\n // Add skeleton to metadata\n return {\n ...result.metadata,\n skeleton,\n };\n } catch (error) {\n this.log.warn(`Metadata collection failed: ${error instanceof Error ? error.message : String(error)}`);\n return {}; // Return empty metadata as fallback\n }\n }\n\n /**\n * Get next sequence number and current timestamp\n */\n private getEventMetadata() {\n return {\n timestamp: Date.now(),\n sequence: ++this.sequenceNumber,\n };\n }\n\n /**\n * Extract annotations with optional location information\n */\n private extractAnnotations(annotations: Array<Annotation> | undefined): Array<Annotation> {\n if (!annotations || annotations.length === 0) {\n return [];\n }\n\n return annotations.map((a) => ({\n type: a.type,\n description: a.description,\n }));\n }\n\n /**\n * Extract parent suite information for skeleton mapping\n */\n private extractParentSuite(parent: Suite): TestBeginEvent['parentSuite'] {\n return {\n title: parent.title,\n type: parent.type,\n location: parent.location\n ? {\n file: parent.location.file,\n line: parent.location.line,\n column: parent.location.column,\n }\n : undefined,\n };\n }\n\n /**\n * Extract parent step information for step hierarchy mapping\n */\n private extractParentStep(parent: TestStep): TestStepBeginEvent['parentStep'] {\n return {\n title: parent.title,\n category: parent.category,\n location: parent.location\n ? {\n file: parent.location.file,\n line: parent.location.line,\n column: parent.location.column,\n }\n : undefined,\n };\n }\n\n /**\n * Extract child steps summary for step:end event\n */\n private extractChildSteps(step: TestStep): TestStepEndEvent['childSteps'] {\n return {\n count: step.steps.length,\n steps: step.steps.map((child) => ({\n title: child.title,\n status: child.error ? 'failed' : 'passed',\n })),\n };\n }\n\n /**\n * Extract error details from TestError (shared by step:end, test:end, and error events)\n */\n private extractError(error: TestError | undefined): TestStepEndEvent['error'] {\n if (!error) return undefined;\n\n return {\n message: error.message || String(error),\n stack: error.stack,\n snippet: error.snippet,\n value: error.value,\n location: error.location\n ? {\n file: error.location.file,\n line: error.location.line,\n column: error.location.column,\n }\n : undefined,\n };\n }\n\n /**\n * Extract global error details with cause support (for error events)\n */\n private extractGlobalError(error: TestError): TestRunErrorEvent['error'] {\n return {\n message: error.message,\n stack: error.stack,\n snippet: error.snippet,\n value: error.value,\n location: error.location\n ? {\n file: error.location.file,\n line: error.location.line,\n column: error.location.column,\n }\n : undefined,\n // Handle nested error cause (v1.49+)\n cause: error.cause\n ? {\n message: error.cause.message,\n stack: error.cause.stack,\n snippet: error.cause.snippet,\n value: error.cause.value,\n location: error.cause.location\n ? {\n file: error.cause.location.file,\n line: error.cause.location.line,\n column: error.cause.location.column,\n }\n : undefined,\n }\n : undefined,\n };\n }\n\n /**\n * Print a prominent configuration error banner\n */\n private printConfigurationError(message: string, solutions: string[]): void {\n const border = '═'.repeat(70);\n console.error('');\n console.error(border);\n console.error(' ❌ TestDino Reporter Configuration Error');\n console.error(border);\n console.error(` ${message}`);\n console.error('');\n console.error(' Solutions:');\n solutions.forEach((solution, index) => {\n console.error(` ${index + 1}. ${solution}`);\n });\n console.error(border);\n console.error('');\n }\n\n /**\n * Print quota error with plan details and upgrade information\n * @param error - Quota error (QUOTA_EXHAUSTED or QUOTA_EXCEEDED)\n */\n private printQuotaError(error: unknown): void {\n const border = '═'.repeat(70);\n const errorData = error as Record<string, unknown>;\n const details = errorData.details as Record<string, unknown>;\n const planName = (details?.planName as string) || 'Unknown';\n const resetDate = details?.resetDate;\n\n // Build detailed message with plan information\n let message = 'Execution quota exceeded';\n message += `\\n\\n Current Plan: ${planName}`;\n\n if (errorData.code === 'QUOTA_EXHAUSTED') {\n message += `\\n Monthly Limit: ${details.totalLimit || 'Unknown'} executions`;\n message += `\\n Used: ${details.used || 'Unknown'} executions`;\n if (resetDate) {\n message += `\\n Limit Resets: ${new Date(resetDate as string).toLocaleDateString()}`;\n }\n } else if (errorData.code === 'QUOTA_EXCEEDED') {\n const exceeded = details as {\n total?: number;\n used?: number;\n remaining?: number;\n totalTests?: number;\n };\n message += `\\n Monthly Limit: ${exceeded.total || 'Unknown'} executions`;\n message += `\\n Used: ${exceeded.used || 'Unknown'} executions`;\n\n // Always calculate remaining from total - used (don't trust server's remaining field)\n const remaining = (exceeded.total ?? 0) - (exceeded.used ?? 0);\n\n message += `\\n Remaining: ${remaining} executions`;\n message += `\\n Tests in this run: ${exceeded.totalTests || 'Unknown'}`;\n if (resetDate) {\n message += `\\n Limit Resets: ${new Date(resetDate as string).toLocaleDateString()}`;\n }\n }\n\n console.error('');\n console.error(border);\n console.error(' ❌ TestDino Execution Limit Reached');\n console.error(border);\n console.error(` ${message}`);\n console.error('');\n console.error(' Solutions:');\n console.error(' 1. Upgrade your plan to increase monthly limit');\n console.error(' 2. Wait for monthly limit reset');\n console.error(' 3. Visit https://testdino.com/pricing for plan options');\n console.error(border);\n console.error('');\n }\n\n /**\n * Truncate console chunk to max size and convert Buffer to string\n */\n private truncateChunk(chunk: string | Buffer): { text: string; truncated?: boolean } {\n // Convert Buffer to string (UTF-8)\n let convertedText: string;\n if (Buffer.isBuffer(chunk)) {\n convertedText = chunk.toString('utf-8');\n } else {\n convertedText = chunk;\n }\n\n // Check if truncation is needed\n if (convertedText.length > MAX_CONSOLE_CHUNK_SIZE) {\n return {\n text: convertedText.substring(0, MAX_CONSOLE_CHUNK_SIZE) + '\\n[truncated]',\n truncated: true,\n };\n }\n\n return { text: convertedText };\n }\n\n /**\n * Extract attachment metadata for step:end event\n */\n private extractAttachments(step: TestStep): TestStepEndEvent['attachments'] {\n return step.attachments.map((a) => ({\n name: a.name,\n contentType: a.contentType,\n path: a.path, // undefined for in-memory attachments\n }));\n }\n\n /**\n * Extract steps summary for test:end event\n */\n private extractTestStepsSummary(result: TestResult): TestEndEvent['steps'] {\n return {\n total: result.steps.length,\n passed: result.steps.filter((s) => !s.error).length,\n failed: result.steps.filter((s) => s.error).length,\n };\n }\n\n /**\n * Extract console output for test:end event\n */\n private extractConsoleOutput(output: Array<string | Buffer>): string[] {\n return output.map((item) => (typeof item === 'string' ? item : item.toString()));\n }\n\n /**\n * Check if this is a duplicate instance of TestdinoReporter\n * This happens when the CLI injects our reporter via --reporter flag,\n * but the user already has TestdinoReporter configured in playwright.config\n *\n * @param reporters - The resolved reporters array from FullConfig\n * @returns true if there are multiple TestdinoReporter instances, false otherwise\n */\n private isDuplicateInstance(reporters: unknown[]): boolean {\n const count = this.countTestdinoReporters(reporters);\n\n // If count > 1, it means we have both:\n // - One from playwright.config (user's manual config)\n // - One from CLI injection (our --reporter flag)\n // The config-based instance should handle reporting, so we exit\n return count > 1;\n }\n\n /**\n * Count how many TestdinoReporter instances are in the reporters array\n *\n * @param reporters - The resolved reporters array from FullConfig\n * @returns Number of TestdinoReporter instances found\n */\n private countTestdinoReporters(reporters: unknown): number {\n // Guard against undefined or non-array reporters\n if (!reporters || !Array.isArray(reporters)) {\n return 0;\n }\n\n let count = 0;\n\n for (const reporter of reporters) {\n // Reporters can be in multiple formats:\n // - ['@testdino/playwright'] - string in array\n // - ['@testdino/playwright', options] - string with options\n // - Class instance (when instantiated with new)\n\n if (Array.isArray(reporter) && reporter.length > 0) {\n const reporterName = reporter[0];\n if (this.isTestdinoReporter(reporterName)) {\n count++;\n }\n }\n }\n\n return count;\n }\n\n /**\n * Check if a reporter name/path matches TestdinoReporter\n *\n * @param value - Reporter name, path, or class reference\n * @returns true if this is our reporter, false otherwise\n */\n private isTestdinoReporter(value: unknown): boolean {\n if (typeof value !== 'string') {\n return false;\n }\n\n // Check various forms our reporter might appear as:\n // - '@testdino/playwright' (npm package name)\n // - 'TestdinoReporter' (class name)\n // - Path ending with 'testdino-playwright'\n return (\n value.includes('@testdino/playwright') ||\n value.includes('TestdinoReporter') ||\n value.endsWith('testdino-playwright')\n );\n }\n\n /**\n * Register signal handlers for graceful shutdown on interruption\n */\n private registerSignalHandlers(): void {\n // Use a wrapper that properly handles async operations\n // Node.js signal handlers don't wait for async operations,\n // so we need to prevent immediate exit and handle manually\n this.sigintHandler = () => {\n // Remove handler immediately to prevent duplicate signals during shutdown\n if (this.sigintHandler) {\n process.removeListener('SIGINT', this.sigintHandler);\n }\n if (this.sigtermHandler) {\n process.removeListener('SIGTERM', this.sigtermHandler);\n }\n\n this.handleInterruption('SIGINT', 130);\n };\n\n this.sigtermHandler = () => {\n // Remove handler immediately to prevent duplicate signals during shutdown\n if (this.sigintHandler) {\n process.removeListener('SIGINT', this.sigintHandler);\n }\n if (this.sigtermHandler) {\n process.removeListener('SIGTERM', this.sigtermHandler);\n }\n\n this.handleInterruption('SIGTERM', 143);\n };\n\n process.on('SIGINT', this.sigintHandler);\n process.on('SIGTERM', this.sigtermHandler);\n }\n\n /**\n * Remove signal handlers (called on normal completion or after handling interruption)\n */\n private removeSignalHandlers(): void {\n if (this.sigintHandler) {\n process.removeListener('SIGINT', this.sigintHandler);\n }\n if (this.sigtermHandler) {\n process.removeListener('SIGTERM', this.sigtermHandler);\n }\n }\n\n /**\n * Handle process interruption by sending run:end event with interrupted status\n * @param signal - The signal that triggered the interruption (SIGINT or SIGTERM)\n * @param exitCode - The exit code to use when exiting\n */\n private handleInterruption(signal: string, exitCode: number): void {\n // Prevent duplicate handling\n if (this.isShuttingDown) return;\n this.isShuttingDown = true;\n\n this.log.warn(`Received ${signal}, sending interruption event...`);\n\n if (!this.initPromise) {\n // Init never started, just exit\n process.exit(exitCode);\n }\n\n // Wait up to 2000ms for pending test:end events to finish (matches send timeout)\n // This allows time for in-progress uploads to complete during graceful shutdown\n const waitForPending = async () => {\n if (this.pendingTestEndPromises.size > 0) {\n try {\n await Promise.race([\n Promise.allSettled(Array.from(this.pendingTestEndPromises)),\n this.timeoutPromise(2000, 'Pending events timeout'),\n ]);\n } catch {\n // Timeout expected, continue with shutdown\n }\n }\n };\n\n // Create interruption event\n const event: TestRunEndEvent = {\n type: 'run:end',\n runId: this.runId,\n ...this.getEventMetadata(),\n status: 'interrupted',\n duration: this.runStartTime ? Date.now() - this.runStartTime : 0,\n startTime: this.runStartTime ?? Date.now(),\n shard: this.shardInfo,\n };\n\n // Keep event loop alive with interval - CRITICAL for ensuring async operations complete\n const keepAlive = setInterval(() => {\n // Empty interval to keep process alive\n }, 100);\n\n // Hard timeout to force exit if send hangs\n const forceExitTimer = setTimeout(() => {\n clearInterval(keepAlive);\n this.log.error('Force exit - send timeout exceeded');\n this.wsClient?.close();\n process.exit(exitCode);\n }, 3000); // 3 second hard limit\n\n // Send interruption event directly (bypass buffer to avoid flush delays)\n const sendAndExit = async () => {\n try {\n // Wait briefly for pending test:end events (best effort)\n await waitForPending();\n\n // Send directly without buffering for speed\n await Promise.race([this.sendInterruptionEvent(event), this.timeoutPromise(2500, 'Send timeout')]);\n\n this.log.success('Interruption event sent');\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n this.log.error(`Failed to send interruption event: ${errorMsg}`);\n } finally {\n // Clean up\n clearTimeout(forceExitTimer);\n clearInterval(keepAlive);\n this.wsClient?.close();\n process.exit(exitCode);\n }\n };\n\n // Execute\n sendAndExit().catch(() => {\n clearTimeout(forceExitTimer);\n clearInterval(keepAlive);\n process.exit(exitCode);\n });\n }\n\n /**\n * Send interruption event directly without buffering\n * Uses optimized path for speed during shutdown\n */\n private async sendInterruptionEvent(event: TestRunEndEvent): Promise<void> {\n // Try WebSocket first if connected\n if (!this.useHttpFallback && this.wsClient?.isConnected()) {\n try {\n await this.wsClient.send(event);\n return;\n } catch {\n // Fall through to HTTP\n this.log.warn('WebSocket send failed, trying HTTP');\n }\n }\n\n // Use HTTP with reduced retries for speed\n if (this.httpClient) {\n try {\n // Single attempt, no retries for interruption (speed is critical)\n await this.httpClient.sendEvent(event);\n } catch (error) {\n throw new Error(`HTTP send failed: ${error}`);\n }\n } else {\n throw new Error('No client available');\n }\n }\n\n /**\n * Create a promise that rejects after a timeout\n * @param ms - Timeout in milliseconds\n * @param message - Error message for timeout\n */\n private timeoutPromise(ms: number, message: string): Promise<never> {\n return new Promise((_, reject) => setTimeout(() => reject(new Error(message)), ms));\n }\n\n /**\n * Initialize artifact uploader by requesting SAS token\n * Gracefully handles failures - uploads disabled if SAS token request fails\n */\n private async initializeArtifactUploader(token: string, serverUrl: string): Promise<void> {\n try {\n // Get base server URL (without /api/reporter)\n const baseServerUrl = this.getBaseServerUrl(serverUrl);\n\n const sasTokenClient = new SASTokenClient({\n token,\n serverUrl: baseServerUrl,\n });\n\n const sasToken = await sasTokenClient.requestToken();\n\n this.artifactUploader = new ArtifactUploader(sasToken, {\n debug: this.config.debug,\n });\n\n this.log.debug('Artifact uploads enabled');\n } catch (error) {\n // Graceful degradation - disable uploads but continue\n this.log.warn(`Artifact uploads disabled - ${error instanceof Error ? error.message : String(error)}`);\n this.artifactsEnabled = false;\n this.artifactUploader = null;\n }\n }\n\n /**\n * Upload attachments and return with Azure URLs\n * If uploads disabled or failed, returns attachments with local paths\n */\n private async uploadAttachments(\n attachments: Array<{ name: string; contentType: string; path?: string; body?: Buffer }>,\n testId: string\n ): Promise<Array<{ name: string; contentType: string; path?: string }>> {\n // If uploads not enabled or no uploader, return original attachments\n if (!this.artifactsEnabled || !this.artifactUploader) {\n return attachments.map((a) => ({\n name: a.name,\n contentType: a.contentType,\n path: a.path,\n }));\n }\n\n // Filter to file-based attachments only (with path)\n const fileAttachments: AttachmentForUpload[] = attachments\n .filter((a): a is { name: string; contentType: string; path: string } => !!a.path)\n .map((a) => ({\n name: a.name,\n contentType: a.contentType,\n path: a.path,\n }));\n\n // If no file attachments, return original\n if (fileAttachments.length === 0) {\n return attachments.map((a) => ({\n name: a.name,\n contentType: a.contentType,\n path: a.path,\n }));\n }\n\n // Upload files in parallel\n const uploadResults = await this.artifactUploader.uploadAll(fileAttachments, testId);\n\n // Create a map of upload results by name\n const uploadMap = new Map<string, UploadResult>();\n for (const result of uploadResults) {\n uploadMap.set(result.name, result);\n }\n\n // Map attachments with Azure URLs where upload succeeded\n return attachments.map((a) => {\n const uploadResult = uploadMap.get(a.name);\n if (uploadResult?.success && uploadResult.uploadUrl) {\n // Replace local path with Azure URL\n return {\n name: a.name,\n contentType: a.contentType,\n path: uploadResult.uploadUrl,\n };\n }\n // Keep original path if upload failed or not applicable\n return {\n name: a.name,\n contentType: a.contentType,\n path: a.path,\n };\n });\n }\n\n /**\n * Get base server URL without /api/reporter suffix\n */\n private getBaseServerUrl(serverUrl: string): string {\n // Remove /api/reporter suffix if present\n return serverUrl.replace(/\\/api\\/reporter$/, '');\n }\n\n /**\n * Walk up the suite hierarchy to find the project name for a test.\n */\n private getProjectName(test: TestCase): string | undefined {\n let suite: Suite | undefined = test.parent;\n while (suite) {\n const project = suite.project();\n if (project) {\n return project.name || project.use?.defaultBrowserType || 'default';\n }\n suite = suite.parent;\n }\n return undefined;\n }\n\n /**\n * Extract coverage fragment from test result attachments and merge incrementally.\n * The fixture attaches coverage as an in-memory JSON attachment named 'testdino-coverage'.\n */\n private extractCoverageFromResult(result: TestResult, merger: CoverageMerger): void {\n const coverageAttachment = result.attachments.find((a) => a.name === 'testdino-coverage');\n\n if (!coverageAttachment?.body) return;\n\n try {\n const fragment: CoverageFragment = JSON.parse(coverageAttachment.body.toString());\n merger.addFragment(fragment);\n } catch (error) {\n this.log.debug(\n `Malformed coverage attachment, skipping: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n /**\n * Build a coverage:data event from the merged coverage data.\n *\n * Non-sharded runs: summary + per-file metrics only (small payload).\n * Sharded runs: also includes compact hit counts for server-side cross-shard merging.\n */\n private buildCoverageEvent(merger: CoverageMerger): CoverageDataEvent {\n const rootDir = this.runMetadata?.playwright?.rootDir || process.cwd();\n const summary = merger.computeSummary();\n const files = merger.computeFileCoverage(rootDir);\n const isSharded = !!this.shardInfo;\n\n if (files.length > COVERAGE_FILE_COUNT_WARNING) {\n this.log.warn(\n `Coverage includes ${files.length} files — consider using coverage.include/exclude to reduce scope`\n );\n }\n\n const event: CoverageDataEvent = {\n type: 'coverage:data',\n runId: this.runId,\n ...this.getEventMetadata(),\n summary,\n files,\n metadata: {\n instrumentationType: 'istanbul',\n fileCount: files.length,\n sharded: isSharded,\n },\n };\n\n // For sharded runs, include compact hit counts for server-side merging\n if (isSharded) {\n const coverageMapJSON = merger.toJSON();\n event.compactCounts = extractCompactCounts(coverageMapJSON, rootDir);\n event.shard = this.shardInfo;\n }\n\n return event;\n }\n\n /**\n * Check coverage against configured thresholds.\n * Returns an array of failure messages (empty if all thresholds pass).\n */\n private checkCoverageThresholds(summary: import('../types/index.js').CoverageSummary): string[] {\n const thresholds = this.config.coverage?.thresholds;\n if (!thresholds) return [];\n\n const failures: string[] = [];\n const check = (name: string, actual: number, threshold: number | undefined) => {\n if (threshold !== undefined && actual < threshold) {\n failures.push(`${name}: ${actual.toFixed(2)}% < ${threshold}%`);\n }\n };\n\n check('Statements', summary.statements.pct, thresholds.statements);\n check('Branches', summary.branches.pct, thresholds.branches);\n check('Functions', summary.functions.pct, thresholds.functions);\n check('Lines', summary.lines.pct, thresholds.lines);\n\n return failures;\n }\n}\n","/**\n * WebSocket client for real-time event streaming\n */\n\nimport WebSocket from 'ws';\nimport type { TestEvent, NackMessage } from '../types/index.js';\nimport { isServerError, QuotaExceededError, QuotaExhaustedError } from '../reporter/errors.js';\n\nconst HANDSHAKE_TIMEOUT_MS = 10_000;\n\nexport interface WebSocketClientOptions {\n token: string;\n serverUrl: string;\n sessionId?: string;\n maxRetries?: number;\n retryDelay?: number;\n debug?: boolean;\n onConnected?: () => void;\n onDisconnected?: () => void;\n onError?: (error: Error) => void;\n}\n\n// ACK timeout for waiting on server acknowledgment\nconst DEFAULT_ACK_TIMEOUT_MS = 5_000;\n\n// Pending ACK tracking\ninterface PendingAck {\n resolve: () => void;\n reject: (error: Error) => void;\n timer: NodeJS.Timeout;\n}\n\nexport class WebSocketClient {\n private ws: WebSocket | null = null;\n private options: Required<WebSocketClientOptions>;\n private reconnectAttempts = 0;\n private reconnectTimer: NodeJS.Timeout | null = null;\n private isConnecting = false;\n private isClosed = false;\n private pingInterval: NodeJS.Timeout | null = null;\n private pendingAcks: Map<number, PendingAck> = new Map();\n private ackTimeout: number = DEFAULT_ACK_TIMEOUT_MS;\n private debug: boolean;\n\n constructor(options: WebSocketClientOptions) {\n this.debug = options.debug ?? false;\n this.options = {\n sessionId: '',\n maxRetries: 5,\n retryDelay: 1000,\n debug: false,\n onConnected: () => {},\n onDisconnected: () => {},\n onError: () => {},\n ...options,\n };\n }\n\n /**\n * Establish WebSocket connection.\n * Resolves only after the server sends its 'connected' handshake message,\n * guaranteeing the server's message handler is registered before events are sent.\n * Passes sessionId from HTTP auth so the server reuses the existing session.\n */\n async connect(): Promise<void> {\n if (this.isConnecting || this.ws?.readyState === WebSocket.OPEN) {\n return;\n }\n\n this.isConnecting = true;\n\n return new Promise((resolve, reject) => {\n try {\n // Include sessionId in URL if available from prior HTTP auth\n let wsUrl = `${this.options.serverUrl}/stream?token=${this.options.token}`;\n if (this.options.sessionId) {\n wsUrl += `&sessionId=${this.options.sessionId}`;\n }\n this.ws = new WebSocket(wsUrl);\n\n // Track whether we've received the server's 'connected' handshake\n let serverReady = false;\n\n const handshakeTimeout = setTimeout(() => {\n if (!serverReady) {\n // Server didn't send 'connected' in time — resolve anyway to avoid blocking\n // the test run indefinitely. The early message buffer on the server will\n // catch any messages sent before the handler is ready.\n if (this.debug) {\n console.warn(\n `⚠️ TestDino: WebSocket handshake timeout — server did not send 'connected' within ${HANDSHAKE_TIMEOUT_MS}ms. Resolving anyway.`\n );\n }\n serverReady = true;\n this.isConnecting = false;\n this.options.onConnected();\n resolve();\n }\n }, HANDSHAKE_TIMEOUT_MS);\n\n this.ws.on('open', () => {\n this.reconnectAttempts = 0;\n this.startPing();\n });\n\n this.ws.on('message', (data) => {\n const raw = data.toString();\n\n // Intercept the 'connected' handshake message\n if (!serverReady) {\n try {\n const msg = JSON.parse(raw) as Record<string, unknown>;\n if (msg.type === 'connected') {\n serverReady = true;\n clearTimeout(handshakeTimeout);\n this.isConnecting = false;\n this.options.onConnected();\n resolve();\n return;\n }\n } catch {\n // Not JSON — fall through to normal handling\n }\n }\n\n this.handleMessage(raw);\n });\n\n this.ws.on('close', (code, reason) => {\n clearTimeout(handshakeTimeout);\n if (!serverReady) {\n this.isConnecting = false;\n reject(new Error(`WebSocket closed before server ready: code=${code} reason=${reason.toString()}`));\n return;\n }\n this.handleClose(code, reason.toString());\n });\n\n this.ws.on('error', (error) => {\n clearTimeout(handshakeTimeout);\n this.isConnecting = false;\n this.options.onError(error);\n reject(error);\n });\n\n this.ws.on('pong', () => {\n // Keep-alive response received\n });\n } catch (error) {\n this.isConnecting = false;\n reject(error);\n }\n });\n }\n\n /**\n * Send event through WebSocket\n */\n async send(event: TestEvent): Promise<void> {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('WebSocket is not connected');\n }\n\n return new Promise((resolve, reject) => {\n this.ws!.send(JSON.stringify(event), (error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n\n /**\n * Send multiple events in batch (parallel for speed)\n * Note: Uses Promise.all intentionally - if one send fails, connection is broken\n */\n async sendBatch(events: TestEvent[]): Promise<void> {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('WebSocket is not connected');\n }\n\n // Send all events in parallel for better performance\n await Promise.all(events.map((event) => this.send(event)));\n }\n\n /**\n * Send event and wait for server ACK\n * Use this for critical events like run:end where delivery must be confirmed\n */\n async sendAndWaitForAck(event: TestEvent, timeout: number = this.ackTimeout): Promise<void> {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('WebSocket is not connected');\n }\n\n const sequence = event.sequence;\n\n return new Promise((resolve, reject) => {\n // Set up ACK timeout\n const timer = setTimeout(() => {\n this.pendingAcks.delete(sequence);\n reject(new Error(`ACK timeout for sequence ${sequence} after ${timeout}ms`));\n }, timeout);\n\n // Register pending ACK\n this.pendingAcks.set(sequence, { resolve, reject, timer });\n\n // Send the event\n this.ws!.send(JSON.stringify(event), (error) => {\n if (error) {\n clearTimeout(timer);\n this.pendingAcks.delete(sequence);\n reject(error);\n }\n // Don't resolve here - wait for ACK from server\n });\n });\n }\n\n /**\n * Set ACK timeout for sendAndWaitForAck\n */\n setAckTimeout(timeout: number): void {\n this.ackTimeout = timeout;\n }\n\n /**\n * Check if WebSocket is connected\n */\n isConnected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n /**\n * Close WebSocket connection\n */\n close(): void {\n this.isClosed = true;\n this.stopPing();\n this.clearReconnectTimer();\n this.clearPendingAcks();\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n }\n\n /**\n * Clear all pending ACKs (reject them with connection closed error)\n */\n private clearPendingAcks(): void {\n for (const [sequence, pending] of this.pendingAcks) {\n clearTimeout(pending.timer);\n pending.reject(new Error(`Connection closed while waiting for ACK on sequence ${sequence}`));\n }\n this.pendingAcks.clear();\n }\n\n /**\n * Handle incoming messages\n */\n private handleMessage(data: string): void {\n try {\n const message = JSON.parse(data) as Record<string, unknown>;\n\n if (message.type === 'connected') {\n // Server acknowledged connection\n } else if (message.type === 'ack') {\n // Server acknowledged event receipt - resolve pending ACK promise\n // Validate sequence is actually a number (server might send string)\n const sequence = typeof message.sequence === 'number' ? message.sequence : undefined;\n if (sequence === undefined) {\n // ACK without valid sequence - can't match to pending request\n return;\n }\n if (this.pendingAcks.has(sequence)) {\n const pending = this.pendingAcks.get(sequence)!;\n clearTimeout(pending.timer);\n this.pendingAcks.delete(sequence);\n pending.resolve();\n }\n } else if (message.type === 'nack') {\n // Server rejected event - convert to Error and notify via onError\n const nack = message as unknown as NackMessage;\n\n // Convert NACK to Error and notify via lifecycle callback\n // Reporter will handle all error display (quota errors with special banner)\n if (isServerError(nack.error)) {\n // Pass the server error directly (it's already an Error instance)\n this.options.onError(nack.error);\n } else if (typeof nack.error === 'object' && nack.error !== null) {\n // Check if this is a structured quota error object\n const errorObj = nack.error as Record<string, unknown>;\n const errorCode = errorObj.code || errorObj.error;\n\n if (errorCode === 'QUOTA_EXCEEDED' && errorObj.details && typeof errorObj.details === 'object') {\n // Construct QuotaExceededError instance\n const details = errorObj.details as Record<string, unknown>;\n this.options.onError(\n new QuotaExceededError(errorObj.message?.toString() || 'Quota exceeded', {\n planName: details.planName?.toString() || 'Unknown',\n totalTests: Number(details.totalTests) || 0,\n remaining: Number(details.remaining) || 0,\n used: Number(details.used) || 0,\n total: Number(details.total) || 0,\n resetDate: details.resetDate?.toString(),\n canPartialSubmit: Boolean(details.canPartialSubmit),\n allowedCount: Number(details.allowedCount) || 0,\n })\n );\n } else if (errorCode === 'QUOTA_EXHAUSTED' && errorObj.details && typeof errorObj.details === 'object') {\n // Construct QuotaExhaustedError instance\n const details = errorObj.details as Record<string, unknown>;\n this.options.onError(\n new QuotaExhaustedError(errorObj.message?.toString() || 'Quota exhausted', {\n planName: details.planName?.toString() || 'Unknown',\n totalLimit: Number(details.totalLimit) || 0,\n used: Number(details.used) || 0,\n resetDate: details.resetDate?.toString(),\n })\n );\n } else {\n // Generic structured error\n const errorMessage =\n errorObj.message?.toString() || errorObj.error?.toString() || JSON.stringify(nack.error);\n this.options.onError(new Error(`Event rejected: ${errorMessage}`));\n }\n } else {\n // String or primitive error\n const errorMessage = typeof nack.error === 'string' ? nack.error : String(nack.error);\n this.options.onError(new Error(`Event rejected: ${errorMessage}`));\n }\n }\n } catch (error) {\n if (this.debug) {\n console.error('Failed to parse WebSocket message:', error);\n }\n }\n }\n\n /**\n * Handle connection close\n */\n private handleClose(_code: number, _reason: string): void {\n this.stopPing();\n this.clearPendingAcks();\n this.options.onDisconnected();\n\n // Don't reconnect if explicitly closed\n if (this.isClosed) {\n return;\n }\n\n // Attempt reconnection with exponential backoff\n if (this.reconnectAttempts < this.options.maxRetries) {\n this.scheduleReconnect();\n } else {\n this.options.onError(new Error(`WebSocket connection failed after ${this.options.maxRetries} attempts`));\n }\n }\n\n /**\n * Schedule reconnection with exponential backoff\n */\n private scheduleReconnect(): void {\n this.clearReconnectTimer();\n\n const delay = this.options.retryDelay * Math.pow(2, this.reconnectAttempts);\n this.reconnectAttempts++;\n\n this.reconnectTimer = setTimeout(() => {\n this.connect().catch((error) => {\n if (this.debug) {\n console.error('Reconnection failed:', error);\n }\n });\n }, delay);\n }\n\n /**\n * Clear reconnection timer\n */\n private clearReconnectTimer(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n /**\n * Start ping interval for keep-alive\n */\n private startPing(): void {\n this.stopPing();\n this.pingInterval = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.ping();\n }\n }, 30000); // Ping every 30 seconds\n }\n\n /**\n * Stop ping interval\n */\n private stopPing(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n}\n","/**\n * Server error classes for quota and infrastructure errors\n * These are received from the TestDino server and handled gracefully in the reporter\n * Follows the same Error class pattern as cli/errors.ts\n */\n\n/**\n * Base class for all TestDino server errors\n * Extends Error to work with standard error handling\n */\nexport class TestDinoServerError extends Error {\n public readonly code: string;\n\n constructor(code: string, message: string) {\n super(message);\n this.name = 'TestDinoServerError';\n this.code = code;\n Error.captureStackTrace(this, this.constructor);\n }\n}\n\n/**\n * Quota exhausted error (session creation)\n * HTTP 402 response when organization pool is exhausted\n */\nexport class QuotaExhaustedError extends TestDinoServerError {\n public readonly details: {\n planName: string;\n totalLimit: number;\n used: number;\n resetDate?: string;\n };\n\n constructor(\n message: string,\n details: {\n planName: string;\n totalLimit: number;\n used: number;\n resetDate?: string;\n }\n ) {\n super('QUOTA_EXHAUSTED', message);\n this.name = 'QuotaExhaustedError';\n this.details = details;\n }\n}\n\n/**\n * Quota exceeded error (run:begin)\n * WebSocket NACK when test count exceeds available quota\n */\nexport class QuotaExceededError extends TestDinoServerError {\n public readonly details: {\n planName: string;\n totalTests: number;\n remaining: number;\n used: number;\n total: number;\n resetDate?: string;\n canPartialSubmit: boolean;\n allowedCount: number;\n };\n\n constructor(\n message: string,\n details: {\n planName: string;\n totalTests: number;\n remaining: number;\n used: number;\n total: number;\n resetDate?: string;\n canPartialSubmit: boolean;\n allowedCount: number;\n }\n ) {\n super('QUOTA_EXCEEDED', message);\n this.name = 'QuotaExceededError';\n this.details = details;\n }\n}\n\n/**\n * Queue full error (backpressure)\n * WebSocket NACK when event queue is full\n */\nexport class QueueFullError extends TestDinoServerError {\n constructor(message: string) {\n super('QUEUE_FULL', message);\n this.name = 'QueueFullError';\n }\n}\n\n/**\n * Circuit breaker error (Redis unavailable)\n * WebSocket NACK when circuit breaker is open\n */\nexport class CircuitBreakerError extends TestDinoServerError {\n public readonly retryAfter?: number;\n\n constructor(message: string, retryAfter?: number) {\n super('CIRCUIT_BREAKER_OPEN', message);\n this.name = 'CircuitBreakerError';\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Union type for all server error classes\n */\nexport type ServerError = QuotaExhaustedError | QuotaExceededError | QueueFullError | CircuitBreakerError;\n\n/**\n * Type guard to check if error is a server error instance\n * @param error - Unknown error value to check\n * @returns true if error is an instance of TestDinoServerError\n */\nexport function isServerError(error: unknown): error is ServerError {\n return error instanceof TestDinoServerError;\n}\n\n/**\n * Type guard to check if error is a quota-related error\n * @param error - Unknown error value to check\n * @returns true if error is QuotaExhaustedError or QuotaExceededError\n */\nexport function isQuotaError(error: unknown): error is QuotaExhaustedError | QuotaExceededError {\n return error instanceof QuotaExhaustedError || error instanceof QuotaExceededError;\n}\n\n/**\n * Type guard to check if error is a retryable infrastructure error\n * @param error - Unknown error value to check\n * @returns true if error is QueueFullError or CircuitBreakerError\n */\nexport function isRetryableError(error: unknown): error is QueueFullError | CircuitBreakerError {\n return error instanceof QueueFullError || error instanceof CircuitBreakerError;\n}\n","/**\n * HTTP client for fallback event transmission\n */\n\nimport axios, { type AxiosInstance } from 'axios';\nimport type { TestEvent, SessionCreationQuotaResponse } from '../types/index.js';\nimport { QuotaExhaustedError } from '../reporter/errors.js';\nimport { sleep } from '../utils/index.js';\n\nexport interface HttpClientOptions {\n token: string;\n serverUrl: string;\n maxRetries?: number;\n retryDelay?: number;\n}\n\nexport class HttpClient {\n private client: AxiosInstance;\n private options: Required<HttpClientOptions>;\n\n constructor(options: HttpClientOptions) {\n this.options = {\n maxRetries: 3,\n retryDelay: 1000,\n ...options,\n };\n\n this.client = axios.create({\n baseURL: this.options.serverUrl,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.options.token}`,\n },\n timeout: 10000,\n });\n }\n\n /**\n * Authenticate with server\n */\n async authenticate(): Promise<{ sessionId: string; wsUrl: string }> {\n try {\n const response = await this.client.post('/auth');\n return response.data;\n } catch (error) {\n // Handle quota exhausted error (402)\n if (axios.isAxiosError(error) && error.response?.status === 402) {\n const quotaError = error.response.data as SessionCreationQuotaResponse;\n\n // Throw proper Error class instance\n throw new QuotaExhaustedError(quotaError.message, quotaError.details);\n }\n\n throw new Error(`Authentication failed: ${this.getErrorMessage(error)}`);\n }\n }\n\n /**\n * Send events via HTTP (fallback)\n */\n async sendEvents(events: TestEvent[]): Promise<void> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < this.options.maxRetries; attempt++) {\n try {\n await this.client.post('/events', { events });\n return;\n } catch (error) {\n lastError = new Error(this.getErrorMessage(error));\n\n if (attempt < this.options.maxRetries - 1) {\n // Wait before retry with exponential backoff\n const delay = this.options.retryDelay * Math.pow(2, attempt);\n await sleep(delay);\n }\n }\n }\n\n throw lastError || new Error('Failed to send events via HTTP');\n }\n\n /**\n * Send single event via HTTP\n */\n async sendEvent(event: TestEvent): Promise<void> {\n await this.sendEvents([event]);\n }\n\n /**\n * Extract error message from various error types\n */\n private getErrorMessage(error: unknown): string {\n if (axios.isAxiosError(error)) {\n return error.response?.data?.message || error.message;\n }\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n }\n}\n","/**\n * Shared utility functions\n */\n\nimport { relative } from 'path';\n\n/**\n * Normalize file path to repo-root-relative.\n * Ensures consistent paths across different machines and shards.\n */\nexport function normalizePath(filePath: string, rootDir?: string): string {\n if (rootDir && filePath.startsWith(rootDir)) {\n return relative(rootDir, filePath);\n }\n return filePath;\n}\n\n/**\n * Sleep utility for retry delays\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Check if debug mode is enabled (standardized check)\n */\nexport function isDebugEnabled(): boolean {\n return process.env.TESTDINO_DEBUG === 'true' || process.env.TESTDINO_DEBUG === '1' || process.env.DEBUG === 'true';\n}\n","/**\n * Event buffer for batching and managing event transmission\n */\n\nimport type { TestEvent } from '../types/index.js';\n\nexport interface EventBufferOptions {\n maxSize?: number;\n onFlush?: (events: TestEvent[]) => Promise<void>;\n}\n\nexport class EventBuffer {\n private events: TestEvent[] = [];\n private maxSize: number;\n private onFlush: (events: TestEvent[]) => Promise<void>;\n private flushPromise: Promise<void> | null = null;\n\n constructor(options: EventBufferOptions) {\n this.maxSize = options.maxSize || 10;\n this.onFlush = options.onFlush || (async () => {});\n }\n\n /**\n * Add event to buffer\n * Automatically flushes if buffer reaches max size\n */\n async add(event: TestEvent): Promise<void> {\n this.events.push(event);\n\n if (this.events.length >= this.maxSize) {\n await this.flush();\n }\n }\n\n /**\n * Flush all buffered events\n * If a flush is already in progress, waits for it to complete then flushes any new events\n */\n async flush(): Promise<void> {\n // If a flush is in progress, wait for it to complete\n if (this.flushPromise) {\n await this.flushPromise;\n // After waiting, check if there are more events to flush (added during wait)\n // Recursively call flush to handle them\n if (this.events.length > 0) {\n return this.flush();\n }\n return;\n }\n\n // Nothing to flush\n if (this.events.length === 0) {\n return;\n }\n\n // Start the flush operation and track it\n this.flushPromise = this.doFlush();\n\n try {\n await this.flushPromise;\n } finally {\n this.flushPromise = null;\n }\n }\n\n /**\n * Internal flush implementation\n */\n private async doFlush(): Promise<void> {\n try {\n const eventsToFlush = [...this.events];\n this.events = [];\n await this.onFlush(eventsToFlush);\n } catch (error) {\n // If flush fails, log error but don't restore events (they were already sent partially)\n console.error('Failed to flush events:', error);\n throw error;\n }\n }\n\n /**\n * Get current buffer size\n */\n size(): number {\n return this.events.length;\n }\n\n /**\n * Check if buffer is empty\n */\n isEmpty(): boolean {\n return this.events.length === 0;\n }\n\n /**\n * Clear buffer without flushing\n */\n clear(): void {\n this.events = [];\n }\n\n /**\n * Get all events without removing them\n */\n getEvents(): TestEvent[] {\n return [...this.events];\n }\n}\n","/**\n * Git metadata collector\n * Collects git repository information using git commands\n */\n\nimport { readFile } from 'fs/promises';\nimport { BaseMetadataCollector } from './base.js';\nimport type { GitMetadata, PRMetadata } from './types.js';\n\n// Dynamic import for execa (ESM-only package, needs dynamic import in CJS context)\nlet execaPromise: Promise<typeof import('execa').execa> | null = null;\n\n/**\n * Dynamically import execa (ESM-only package) for CJS compatibility only once per process using memoization.\n *\n * @returns Promise resolving to the execa function\n * @throws Error if execa cannot be imported\n */\nasync function getExeca(): Promise<typeof import('execa').execa> {\n if (!execaPromise) {\n execaPromise = import('execa')\n .then((m) => m.execa)\n .catch((error) => {\n console.error('Failed to import execa:', error.message);\n throw new Error('Failed to load execa. Ensure execa is installed: npm install execa');\n });\n }\n return execaPromise;\n}\n\n/**\n * Minimal GitHub event data interface for type safety\n */\ninterface GitHubEventData {\n pull_request?: {\n title?: unknown;\n number?: unknown;\n state?: unknown;\n head?: { ref?: unknown; sha?: unknown };\n base?: { ref?: unknown };\n user?: { login?: unknown };\n labels?: Array<{ name?: unknown }>;\n merged?: unknown;\n mergeable?: unknown;\n merge_commit_sha?: unknown;\n };\n}\n\n/**\n * Resolved GitHub author information for deduplication and canonical naming\n */\ninterface GitHubAuthorInfo {\n authorId: string;\n authorLogin?: string;\n}\n\nexport interface GitCollectorOptions {\n /**\n * Timeout for git commands in milliseconds\n * @default 3000\n */\n timeout?: number;\n\n /**\n * Working directory for git commands\n * @default process.cwd()\n */\n cwd?: string;\n}\n\n/**\n * Collects git repository metadata\n */\nexport class GitMetadataCollector extends BaseMetadataCollector<GitMetadata> {\n private options: Required<GitCollectorOptions>;\n\n constructor(options: GitCollectorOptions = {}) {\n super('git');\n this.options = {\n timeout: options.timeout || 3000,\n cwd: options.cwd || process.cwd(),\n };\n }\n\n /**\n * Collect git metadata\n */\n protected async collectMetadata(): Promise<GitMetadata> {\n // Fix git ownership issues in CI containers\n await this.configureGitForCI();\n\n // Check if we're in a git repository\n const isGitRepo = await this.isGitRepository();\n if (!isGitRepo) {\n return this.getEmptyMetadata();\n }\n\n // Collect all git metadata in parallel using Promise.allSettled for error isolation\n const results = await Promise.allSettled([\n this.getBranch(),\n this.getCommitHash(),\n this.getCommitMessage(),\n this.getAuthorName(),\n this.getAuthorEmail(),\n this.getCommitTimestamp(),\n this.getRepoUrl(),\n this.isDirtyWorkingTree(),\n ]);\n\n // Extract values, using undefined for rejected promises\n const extractedValues = results.map((r) => (r.status === 'fulfilled' ? r.value : undefined));\n\n // Variables that may be reassigned in PR context below\n let branch = extractedValues[0] as string | undefined;\n let hash = extractedValues[1] as string | undefined;\n let message = extractedValues[2] as string | undefined;\n let author = extractedValues[3] as string | undefined;\n let email = extractedValues[4] as string | undefined;\n let timestamp = extractedValues[5] as string | undefined;\n\n // Variables that are never reassigned\n const repoUrl = extractedValues[6] as string | undefined;\n const isDirty = extractedValues[7] as boolean | undefined;\n\n // Handle PR context — override merge commit data with real PR data\n let prMetadata: PRMetadata | undefined;\n\n if (process.env.GITHUB_EVENT_NAME === 'pull_request') {\n const eventData = await this.readGitHubEventFile();\n\n if (eventData?.pull_request) {\n prMetadata = this.extractPRMetadata(eventData);\n\n // Override branch with real source branch\n const headRef = process.env.GITHUB_HEAD_REF;\n if (this.isNonEmptyString(headRef)) {\n branch = headRef;\n }\n\n // Override commit data with real PR head commit\n const headSha = eventData.pull_request.head?.sha;\n if (this.isNonEmptyString(headSha)) {\n hash = headSha;\n const realCommit = await this.getCommitInfoFromSha(headSha);\n if (realCommit) {\n message = realCommit.message ?? message;\n author = realCommit.author ?? author;\n email = realCommit.email ?? email;\n timestamp = realCommit.timestamp ?? timestamp;\n }\n }\n }\n }\n\n // Resolve GitHub author info: numeric ID for deduplication, login for canonical name\n const githubAuthor = await this.resolveGitHubAuthor(hash);\n\n return {\n branch,\n commit: {\n hash,\n message,\n author: githubAuthor.authorLogin || author,\n authorId: githubAuthor.authorId,\n email,\n timestamp,\n isDirty,\n },\n repository: {\n name: this.extractRepoName(repoUrl),\n url: repoUrl,\n },\n ...(prMetadata ? { pr: prMetadata } : {}),\n };\n }\n\n /**\n * Get empty metadata\n */\n protected getEmptyMetadata(): GitMetadata {\n return {};\n }\n\n /**\n * Configure git for CI environments\n * Fixes \"dubious ownership\" errors when workspace is mounted with different ownership\n */\n private async configureGitForCI(): Promise<void> {\n const isCI = process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true';\n if (!isCI) return;\n\n try {\n const execa = await getExeca();\n await execa('git', ['config', '--global', '--add', 'safe.directory', this.options.cwd], {\n timeout: this.options.timeout,\n reject: true,\n });\n } catch {\n // Silently continue — this is a best-effort fix\n }\n }\n\n /**\n * Read and parse the GitHub Actions event file\n */\n private async readGitHubEventFile(): Promise<GitHubEventData | undefined> {\n const eventPath = process.env.GITHUB_EVENT_PATH;\n if (!eventPath) return undefined;\n\n try {\n const content = await this.withTimeout(\n readFile(eventPath, 'utf-8'),\n this.options.timeout,\n 'GitHub event file read'\n );\n return this.safeJsonParse<GitHubEventData>(content, {});\n } catch (error) {\n console.warn(\n '⚠️ TestDino: Failed to read GitHub event data:',\n error instanceof Error ? error.message : String(error)\n );\n return undefined;\n }\n }\n\n /**\n * Extract PR metadata from GitHub event data\n */\n private extractPRMetadata(eventData: GitHubEventData): PRMetadata | undefined {\n const pullRequest = eventData?.pull_request;\n if (!pullRequest) return undefined;\n\n const prMetadata: PRMetadata = {};\n\n // Basic PR information\n if (this.isNonEmptyString(pullRequest.title)) {\n prMetadata.title = pullRequest.title;\n }\n\n if (typeof pullRequest.number === 'number') {\n prMetadata.number = pullRequest.number;\n }\n\n if (this.isNonEmptyString(pullRequest.state)) {\n prMetadata.status = pullRequest.state;\n }\n\n // Build PR URL\n const serverUrl = process.env.GITHUB_SERVER_URL;\n const repository = process.env.GITHUB_REPOSITORY;\n if (prMetadata.number && serverUrl && repository) {\n prMetadata.url = `${serverUrl}/${repository}/pull/${prMetadata.number}`;\n }\n\n // Branch information\n if (pullRequest.head?.ref && this.isNonEmptyString(pullRequest.head.ref)) {\n prMetadata.branch = pullRequest.head.ref;\n }\n\n if (pullRequest.base?.ref && this.isNonEmptyString(pullRequest.base.ref)) {\n prMetadata.targetBranch = pullRequest.base.ref;\n }\n\n // Author information\n if (pullRequest.user?.login && this.isNonEmptyString(pullRequest.user.login)) {\n prMetadata.author = pullRequest.user.login;\n }\n\n // Labels\n if (Array.isArray(pullRequest.labels) && pullRequest.labels.length > 0) {\n const labels = pullRequest.labels\n .map((label) => label?.name)\n .filter((name): name is string => this.isNonEmptyString(name));\n\n if (labels.length > 0) {\n prMetadata.labels = labels;\n }\n }\n\n // Merge status\n if (typeof pullRequest.merged === 'boolean') {\n prMetadata.merged = pullRequest.merged;\n }\n\n if (typeof pullRequest.mergeable === 'boolean') {\n prMetadata.mergeable = pullRequest.mergeable;\n }\n\n if (this.isNonEmptyString(pullRequest.merge_commit_sha)) {\n prMetadata.mergeCommitSha = pullRequest.merge_commit_sha;\n }\n\n // Return undefined if no meaningful data was extracted\n const hasData = Object.keys(prMetadata).length > 0;\n return hasData ? prMetadata : undefined;\n }\n\n /**\n * Get commit details for a specific SHA using git show\n * Used to resolve real commit data in PR context (instead of merge commit)\n */\n private async getCommitInfoFromSha(sha: string): Promise<\n | {\n message?: string;\n author?: string;\n email?: string;\n timestamp?: string;\n }\n | undefined\n > {\n try {\n const result = await this.execGit(['show', '-s', '--format=%s%n%an%n%ae%n%aI', sha]);\n const lines = result.split('\\n');\n if (lines.length < 4) return undefined;\n\n return {\n message: this.isNonEmptyString(lines[0]) ? lines[0] : undefined,\n author: this.isNonEmptyString(lines[1]) ? lines[1] : undefined,\n email: this.isNonEmptyString(lines[2]) ? lines[2] : undefined,\n timestamp: this.isNonEmptyString(lines[3]) ? lines[3] : undefined,\n };\n } catch (error) {\n console.warn(\n '⚠️ TestDino: Failed to get commit info from SHA:',\n error instanceof Error ? error.message : String(error)\n );\n return undefined;\n }\n }\n\n /**\n * Resolve GitHub author info via the Commits API\n * Only runs on GitHub Actions where GITHUB_REPOSITORY is available\n */\n private async resolveGitHubAuthor(commitHash: string | undefined): Promise<GitHubAuthorInfo> {\n const empty: GitHubAuthorInfo = { authorId: '' };\n\n if (process.env.GITHUB_ACTIONS !== 'true') {\n return empty;\n }\n\n if (!commitHash) {\n return empty;\n }\n\n const repository = process.env.GITHUB_REPOSITORY;\n if (!repository) {\n return empty;\n }\n\n const url = `https://api.github.com/repos/${repository}/commits/${commitHash}`;\n\n try {\n const headers: Record<string, string> = {\n Accept: 'application/vnd.github.v3+json',\n 'User-Agent': 'testdino-playwright',\n };\n\n const token = process.env.GITHUB_TOKEN;\n if (token) {\n headers.Authorization = `token ${token}`;\n }\n\n const response = await this.withTimeout(fetch(url, { headers }), this.options.timeout, 'GitHub Commits API');\n\n if (!response.ok) {\n return empty;\n }\n\n const data = (await response.json()) as {\n author?: { id?: number; login?: string } | null;\n };\n\n if (data?.author?.id) {\n const authorId = String(data.author.id);\n const authorLogin = this.isNonEmptyString(data.author.login) ? data.author.login : undefined;\n return { authorId, authorLogin };\n }\n\n return this.resolveGitHubAuthorFromActor();\n } catch (error) {\n console.warn(\n '⚠️ TestDino: Failed to resolve GitHub author from Commits API:',\n error instanceof Error ? error.message : String(error)\n );\n return this.resolveGitHubAuthorFromActor();\n }\n }\n\n /**\n * Fallback: resolve GitHub author info from GITHUB_ACTOR via the Users API\n */\n private async resolveGitHubAuthorFromActor(): Promise<GitHubAuthorInfo> {\n const actor = process.env.GITHUB_ACTOR;\n if (!actor) {\n return { authorId: '' };\n }\n\n const url = `https://api.github.com/users/${actor}`;\n\n try {\n const response = await this.withTimeout(\n fetch(url, {\n headers: {\n Accept: 'application/vnd.github.v3+json',\n 'User-Agent': 'testdino-playwright',\n },\n }),\n this.options.timeout,\n 'GitHub Users API'\n );\n\n if (!response.ok) {\n return { authorId: '' };\n }\n\n const data = (await response.json()) as {\n id?: number;\n login?: string;\n };\n\n const authorId = data?.id ? String(data.id) : '';\n const authorLogin = this.isNonEmptyString(data?.login) ? data.login : actor;\n\n return { authorId, authorLogin };\n } catch (error) {\n console.warn(\n '⚠️ TestDino: Failed to resolve GitHub author from GITHUB_ACTOR:',\n error instanceof Error ? error.message : String(error)\n );\n return { authorId: '' };\n }\n }\n\n /**\n * Check if current directory is a git repository\n */\n private async isGitRepository(): Promise<boolean> {\n try {\n await this.execGit(['rev-parse', '--git-dir']);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get current branch name\n * Uses: git rev-parse --abbrev-ref HEAD\n */\n private async getBranch(): Promise<string | undefined> {\n try {\n const result = await this.execGit(['rev-parse', '--abbrev-ref', 'HEAD']);\n return this.isNonEmptyString(result) ? result : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Get current commit hash (full SHA)\n * Uses: git rev-parse HEAD\n */\n private async getCommitHash(): Promise<string | undefined> {\n try {\n const result = await this.execGit(['rev-parse', 'HEAD']);\n return this.isNonEmptyString(result) ? result : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Get commit message of current HEAD\n * Uses: git log -1 --pretty=format:%s\n */\n private async getCommitMessage(): Promise<string | undefined> {\n try {\n const result = await this.execGit(['log', '-1', '--pretty=format:%s']);\n return this.isNonEmptyString(result) ? result : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Get commit author name\n * Uses: git log -1 --pretty=format:%an\n */\n private async getAuthorName(): Promise<string | undefined> {\n try {\n const result = await this.execGit(['log', '-1', '--pretty=format:%an']);\n return this.isNonEmptyString(result) ? result : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Get commit author email\n * Uses: git log -1 --pretty=format:%ae\n */\n private async getAuthorEmail(): Promise<string | undefined> {\n try {\n const result = await this.execGit(['log', '-1', '--pretty=format:%ae']);\n return this.isNonEmptyString(result) ? result : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Get commit timestamp (ISO format)\n * Uses: git log -1 --pretty=format:%aI\n */\n private async getCommitTimestamp(): Promise<string | undefined> {\n try {\n const result = await this.execGit(['log', '-1', '--pretty=format:%aI']);\n return this.isNonEmptyString(result) ? result : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Get remote origin URL\n * Uses: git config --get remote.origin.url\n */\n private async getRepoUrl(): Promise<string | undefined> {\n try {\n const result = await this.execGit(['config', '--get', 'remote.origin.url']);\n return this.isNonEmptyString(result) ? result : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Check if working tree has uncommitted changes\n * Uses: git status --porcelain\n * Returns true if there are any changes (staged, unstaged, or untracked)\n */\n private async isDirtyWorkingTree(): Promise<boolean | undefined> {\n try {\n const result = await this.execGit(['status', '--porcelain']);\n // If output is non-empty, there are changes\n return result.trim().length > 0;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Extract repository name from remote URL\n * e.g., \"https://github.com/user/repo.git\" → \"repo\"\n */\n private extractRepoName(repoUrl: string | undefined): string | undefined {\n if (!repoUrl) return undefined;\n return repoUrl.split('/').pop()?.replace('.git', '') || undefined;\n }\n\n /**\n * Execute git command with timeout\n */\n private async execGit(args: string[]): Promise<string> {\n const execa = await getExeca();\n const { stdout } = await this.withTimeout(\n execa('git', args, {\n cwd: this.options.cwd,\n timeout: this.options.timeout,\n reject: true,\n }),\n this.options.timeout,\n `git ${args.join(' ')}`\n );\n\n return stdout.trim();\n }\n}\n","/**\n * Base metadata collector implementation\n */\n\nimport type { MetadataCollector, MetadataCollectionResult } from './types.js';\n\n/**\n * Abstract base class for metadata collectors\n * Provides common functionality and error handling patterns\n */\nexport abstract class BaseMetadataCollector<T = Record<string, unknown>> implements MetadataCollector<T> {\n protected name: string;\n\n constructor(name: string) {\n this.name = name;\n }\n\n /**\n * Get collector name\n */\n getName(): string {\n return this.name;\n }\n\n /**\n * Collect metadata with error handling and timing\n */\n async collect(): Promise<T> {\n try {\n const data = await this.collectMetadata();\n return data;\n } catch (error) {\n // Log error but don't throw - graceful degradation\n console.warn(\n `⚠️ TestDino: ${this.name} metadata collection failed:`,\n error instanceof Error ? error.message : String(error)\n );\n return this.getEmptyMetadata();\n }\n }\n\n /**\n * Collect metadata with result tracking\n */\n async collectWithResult(): Promise<MetadataCollectionResult<T>> {\n const startTime = Date.now();\n const collector = this.getName();\n\n try {\n const data = await this.collectMetadata();\n const duration = Date.now() - startTime;\n\n return {\n data,\n success: true,\n duration,\n collector,\n };\n } catch (error) {\n const duration = Date.now() - startTime;\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n // Log error but return empty data for graceful degradation\n console.warn(`⚠️ TestDino: ${collector} metadata collection failed:`, errorMessage);\n\n return {\n data: this.getEmptyMetadata(),\n success: false,\n error: errorMessage,\n duration,\n collector,\n };\n }\n }\n\n /**\n * Abstract method to be implemented by concrete collectors\n */\n protected abstract collectMetadata(): Promise<T>;\n\n /**\n * Abstract method to provide empty metadata on errors\n */\n protected abstract getEmptyMetadata(): T;\n\n /**\n * Utility method to run command with timeout\n */\n protected async withTimeout<R>(promise: Promise<R>, timeoutMs: number, operation: string): Promise<R> {\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error(`${operation} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n return Promise.race([promise, timeoutPromise]);\n }\n\n /**\n * Utility method to safely parse JSON\n */\n protected safeJsonParse<R>(jsonString: string, fallback: R): R {\n try {\n return JSON.parse(jsonString);\n } catch {\n return fallback;\n }\n }\n\n /**\n * Utility method to check if a value is non-empty string\n */\n protected isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n }\n}\n","/**\n * CI metadata collector\n * Collects CI environment information, focusing on GitHub Actions\n */\n\nimport { type as osType, release as osRelease } from 'os';\nimport { BaseMetadataCollector } from './base.js';\nimport type { CIMetadata } from './types.js';\n\nexport interface CICollectorOptions {\n /**\n * Timeout for CI metadata collection in milliseconds\n * @default 3000\n */\n timeout?: number;\n}\n\n/**\n * Collects CI environment metadata\n */\nexport class CIMetadataCollector extends BaseMetadataCollector<CIMetadata> {\n constructor(_options: CICollectorOptions = {}) {\n super('ci');\n }\n\n /**\n * Collect CI metadata\n */\n protected async collectMetadata(): Promise<CIMetadata> {\n const provider = this.detectCIProvider();\n\n if (!provider) {\n return {\n provider: 'local',\n environment: this.collectEnvironment(),\n };\n }\n\n if (provider === 'github-actions') {\n return this.collectGitHubActionsMetadata();\n }\n\n // Fallback for unknown CI providers\n return {\n provider,\n environment: this.collectEnvironment(),\n };\n }\n\n /**\n * Get empty metadata\n */\n protected getEmptyMetadata(): CIMetadata {\n return {};\n }\n\n /**\n * Detect CI provider from environment variables\n */\n private detectCIProvider(): string | undefined {\n const { env } = process;\n\n // GitHub Actions detection\n if (env.GITHUB_ACTIONS === 'true') {\n return 'github-actions';\n }\n\n // Could add other CI providers here in the future\n // if (env.GITLAB_CI) return 'gitlab';\n // if (env.CIRCLECI) return 'circleci';\n\n return undefined;\n }\n\n /**\n * Collect GitHub Actions specific metadata\n */\n private collectGitHubActionsMetadata(): CIMetadata {\n const { env } = process;\n\n return {\n provider: 'github-actions',\n pipeline: {\n id: env.GITHUB_RUN_ID,\n name: env.GITHUB_WORKFLOW,\n url: this.buildPipelineUrl(),\n },\n build: {\n number: env.GITHUB_RUN_NUMBER,\n trigger: env.GITHUB_EVENT_NAME,\n },\n environment: this.collectEnvironment(),\n };\n }\n\n /**\n * Build the pipeline URL from GitHub Actions environment variables\n */\n private buildPipelineUrl(): string | undefined {\n const { env } = process;\n const serverUrl = env.GITHUB_SERVER_URL;\n const repository = env.GITHUB_REPOSITORY;\n const runId = env.GITHUB_RUN_ID;\n\n if (serverUrl && repository && runId) {\n return `${serverUrl}/${repository}/actions/runs/${runId}`;\n }\n\n return undefined;\n }\n\n /**\n * Collect runner environment information\n */\n private collectEnvironment(): CIMetadata['environment'] {\n try {\n return {\n name: osType(),\n type: process.platform,\n os: `${osType()} ${osRelease()}`,\n node: process.version,\n };\n } catch {\n return {};\n }\n }\n}\n","/**\n * System metadata collector\n * Collects system information using Node.js built-in modules\n */\n\nimport { platform, release, cpus, totalmem, hostname } from 'os';\nimport { version } from 'process';\nimport { BaseMetadataCollector } from './base.js';\nimport type { SystemMetadata } from './types.js';\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface SystemCollectorOptions {\n // Reserved for future options\n}\n\n/**\n * Collects system metadata using Node.js built-in modules\n */\nexport class SystemMetadataCollector extends BaseMetadataCollector<SystemMetadata> {\n constructor(_options: SystemCollectorOptions = {}) {\n super('system');\n // System metadata collection is synchronous and doesn't need configuration\n }\n\n /**\n * Collect system metadata\n */\n protected async collectMetadata(): Promise<SystemMetadata> {\n // System metadata collection is synchronous and fast\n // but we wrap it in a promise for consistency with other collectors\n return new Promise((resolve) => {\n const metadata: SystemMetadata = {\n os: this.getOperatingSystem(),\n cpu: this.getCpuInfo(),\n memory: this.getMemoryInfo(),\n nodeVersion: this.getNodeVersion(),\n platform: this.getPlatform(),\n hostname: this.getHostname(),\n };\n\n resolve(metadata);\n });\n }\n\n /**\n * Get empty metadata\n */\n protected getEmptyMetadata(): SystemMetadata {\n return {};\n }\n\n /**\n * Get operating system information\n * Format: \"platform release\" (e.g., \"darwin 23.1.0\", \"linux 5.4.0\")\n */\n private getOperatingSystem(): string {\n let platformName = 'unknown';\n let releaseVersion = 'unknown';\n\n try {\n platformName = platform();\n } catch {\n // Keep default 'unknown'\n }\n\n try {\n releaseVersion = release();\n } catch {\n // Keep default 'unknown'\n }\n\n return `${platformName} ${releaseVersion}`;\n }\n\n /**\n * Get CPU information\n * Format: \"model (X cores)\" (e.g., \"Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz (12 cores)\")\n */\n private getCpuInfo(): string {\n try {\n const cpuList = cpus();\n if (cpuList.length === 0) {\n return 'unknown';\n }\n\n const model = cpuList[0].model.trim();\n const coreCount = cpuList.length;\n\n return `${model} (${coreCount} cores)`;\n } catch {\n return 'unknown';\n }\n }\n\n /**\n * Get memory information\n * Format: \"X.X GB\" (e.g., \"16.0 GB\", \"8.5 GB\")\n */\n private getMemoryInfo(): string {\n try {\n const totalBytes = totalmem();\n const totalGB = totalBytes / (1024 * 1024 * 1024);\n\n // Round to 1 decimal place\n return `${totalGB.toFixed(1)} GB`;\n } catch {\n return 'unknown';\n }\n }\n\n /**\n * Get Node.js version\n * Returns process.version (e.g., \"v18.17.0\")\n */\n private getNodeVersion(): string {\n try {\n return version;\n } catch {\n return 'unknown';\n }\n }\n\n /**\n * Get platform identifier\n * Returns os.platform() (e.g., \"darwin\", \"linux\", \"win32\")\n */\n private getPlatform(): string {\n try {\n return platform();\n } catch {\n return 'unknown';\n }\n }\n\n /**\n * Get hostname\n * Returns os.hostname() (e.g., \"MacBook-Pro.local\")\n */\n private getHostname(): string {\n try {\n return hostname();\n } catch {\n return 'unknown';\n }\n }\n}\n","/**\n * Playwright metadata collector\n * Collects Playwright configuration and version information\n */\n\nimport { readFile } from 'fs/promises';\nimport { BaseMetadataCollector } from './base.js';\nimport type {\n PlaywrightMetadata,\n ProjectConfig,\n ProjectUseOptions,\n RunSkeleton,\n SkeletonSuite,\n SkeletonTest,\n} from './types.js';\n\n/**\n * Playwright FullProject interface (per-project config)\n * @see docs/PLAYWRIGHT-TYPES-REFERENCE.md\n */\ninterface PlaywrightFullProject {\n name?: string;\n testDir?: string;\n timeout?: number;\n retries?: number;\n repeatEach?: number;\n dependencies?: string[];\n grep?: RegExp | RegExp[];\n use?: {\n browserName?: 'chromium' | 'firefox' | 'webkit';\n defaultBrowserType?: 'chromium' | 'firefox' | 'webkit'; // From device presets\n channel?: string;\n headless?: boolean;\n viewport?: { width: number; height: number } | null;\n baseURL?: string;\n trace?: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | { mode: string };\n screenshot?: 'off' | 'on' | 'only-on-failure' | { mode: string };\n video?: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | { mode: string };\n isMobile?: boolean;\n locale?: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Playwright FullConfig interface (global config)\n * Note: testDir, timeout, retries are on FullProject, not FullConfig\n * @see docs/PLAYWRIGHT-TYPES-REFERENCE.md\n */\ninterface PlaywrightFullConfig {\n fullyParallel?: boolean;\n workers?: number;\n shard?: {\n current: number;\n total: number;\n } | null;\n projects?: PlaywrightFullProject[];\n version?: string;\n configFile?: string;\n forbidOnly?: boolean;\n globalTimeout?: number;\n grep?: RegExp | RegExp[];\n maxFailures?: number;\n metadata?: Record<string, unknown>;\n reportSlowTests?: {\n max: number;\n threshold: number;\n } | null;\n rootDir?: string;\n tags?: string[];\n webServer?: Record<string, unknown> | null;\n}\n\n/**\n * Playwright Suite interface (minimal)\n */\ninterface PlaywrightSuite {\n title: string;\n type: 'root' | 'project' | 'file' | 'describe';\n location?: {\n file: string;\n line: number;\n column: number;\n };\n suites: PlaywrightSuite[];\n tests: PlaywrightTestCase[];\n allTests(): PlaywrightTestCase[];\n}\n\n/**\n * Playwright TestCase interface (minimal)\n */\ninterface PlaywrightTestCase {\n id: string;\n title: string;\n location: {\n file: string;\n line: number;\n column: number;\n };\n tags: string[];\n expectedStatus: 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted';\n annotations: Array<{\n type: string;\n description?: string;\n }>;\n}\n\nexport interface PlaywrightCollectorOptions {\n /**\n * Timeout for Playwright metadata collection in milliseconds\n * @default 3000\n */\n timeout?: number;\n\n /**\n * Playwright FullConfig object (when available from reporter context)\n */\n config?: PlaywrightFullConfig;\n\n /**\n * Playwright Suite object (when available from reporter context)\n */\n suite?: PlaywrightSuite;\n\n /**\n * Custom path to @playwright/test package.json for testing\n */\n packageJsonPath?: string;\n}\n\n/**\n * Collects Playwright metadata including version and configuration\n */\nexport class PlaywrightMetadataCollector extends BaseMetadataCollector<PlaywrightMetadata> {\n private options: Required<Omit<PlaywrightCollectorOptions, 'config' | 'suite' | 'packageJsonPath'>> &\n Pick<PlaywrightCollectorOptions, 'config' | 'suite' | 'packageJsonPath'>;\n\n constructor(options: PlaywrightCollectorOptions = {}) {\n super('playwright');\n this.options = {\n timeout: options.timeout || 3000,\n config: options.config,\n suite: options.suite,\n packageJsonPath: options.packageJsonPath,\n };\n }\n\n /**\n * Collect Playwright metadata\n */\n protected async collectMetadata(): Promise<PlaywrightMetadata> {\n const metadata: PlaywrightMetadata = {};\n\n // Collect version (always attempt this)\n const version = await this.getPlaywrightVersion();\n if (version) {\n metadata.version = version;\n }\n\n // Collect config metadata if FullConfig is available\n if (this.options.config) {\n const configMetadata = this.extractConfigMetadata(this.options.config);\n Object.assign(metadata, configMetadata);\n }\n\n return metadata;\n }\n\n /**\n * Build skeleton from Suite and include it in CompleteMetadata\n */\n buildSkeletonMetadata(suite: PlaywrightSuite): { skeleton: RunSkeleton } {\n const skeleton = this.buildSkeleton(suite);\n return { skeleton };\n }\n\n /**\n * Get empty metadata\n */\n protected getEmptyMetadata(): PlaywrightMetadata {\n return {};\n }\n\n /**\n * Get Playwright version from @playwright/test package.json\n */\n private async getPlaywrightVersion(): Promise<string | undefined> {\n try {\n const packageJsonPath = this.options.packageJsonPath || this.resolvePlaywrightPackageJson();\n\n if (!packageJsonPath) {\n return undefined;\n }\n\n const packageJsonContent = await this.withTimeout(\n readFile(packageJsonPath, 'utf-8'),\n this.options.timeout,\n 'Playwright package.json read'\n );\n\n const packageJson = this.safeJsonParse<{ version?: string }>(packageJsonContent, {});\n\n return this.isNonEmptyString(packageJson.version) ? packageJson.version : undefined;\n } catch (error) {\n // Log error but don't fail the entire collection\n console.warn(\n '⚠️ TestDino: Failed to read Playwright version:',\n error instanceof Error ? error.message : String(error)\n );\n return undefined;\n }\n }\n\n /**\n * Resolve @playwright/test package.json path\n */\n private resolvePlaywrightPackageJson(): string | undefined {\n try {\n // Try to resolve @playwright/test package.json\n return require.resolve('@playwright/test/package.json');\n } catch {\n // @playwright/test not found\n return undefined;\n }\n }\n\n /**\n * Extract metadata from Playwright FullConfig\n */\n private extractConfigMetadata(config: PlaywrightFullConfig): Partial<PlaywrightMetadata> {\n const metadata: Partial<PlaywrightMetadata> = {};\n\n // Config file path\n if (this.isNonEmptyString(config.configFile)) {\n metadata.configFile = config.configFile;\n }\n\n // Forbid only\n if (typeof config.forbidOnly === 'boolean') {\n metadata.forbidOnly = config.forbidOnly;\n }\n\n // Fully parallel mode\n if (typeof config.fullyParallel === 'boolean') {\n metadata.fullyParallel = config.fullyParallel;\n metadata.parallel = config.fullyParallel; // Keep for backward compatibility\n }\n\n // Global timeout\n if (typeof config.globalTimeout === 'number') {\n metadata.globalTimeout = config.globalTimeout;\n }\n\n // Grep pattern(s)\n if (config.grep) {\n const patterns = Array.isArray(config.grep) ? config.grep : [config.grep];\n metadata.grep = patterns.map((p) => p.source);\n }\n\n // Max failures\n if (typeof config.maxFailures === 'number') {\n metadata.maxFailures = config.maxFailures;\n }\n\n // Custom metadata\n if (config.metadata && typeof config.metadata === 'object') {\n metadata.metadata = config.metadata;\n }\n\n // Workers\n if (typeof config.workers === 'number') {\n metadata.workers = config.workers;\n }\n\n // Projects (full configuration from FullProject)\n if (Array.isArray(config.projects) && config.projects.length > 0) {\n const projectConfigs = config.projects\n .filter((project) => this.isNonEmptyString(project.name))\n .map((project) => this.extractProjectConfig(project));\n\n if (projectConfigs.length > 0) {\n metadata.projects = projectConfigs;\n\n // Extract unique browsers from all projects\n const browsers = this.extractBrowsersFromProjects(config.projects);\n if (browsers.length > 0) {\n metadata.browsers = browsers;\n }\n }\n }\n\n // Report slow tests\n if (config.reportSlowTests && typeof config.reportSlowTests === 'object') {\n metadata.reportSlowTests = {\n max: config.reportSlowTests.max,\n threshold: config.reportSlowTests.threshold,\n };\n }\n\n // Root directory\n if (this.isNonEmptyString(config.rootDir)) {\n metadata.rootDir = config.rootDir;\n }\n\n // Shard information\n if (config.shard && typeof config.shard.current === 'number' && typeof config.shard.total === 'number') {\n metadata.shard = {\n current: config.shard.current,\n total: config.shard.total,\n };\n }\n\n // Tags\n if (Array.isArray(config.tags) && config.tags.length > 0) {\n metadata.tags = config.tags;\n }\n\n // Web server\n if (config.webServer && typeof config.webServer === 'object') {\n metadata.webServer = config.webServer;\n }\n\n return metadata;\n }\n\n /**\n * Extract project configuration from FullProject\n */\n private extractProjectConfig(project: PlaywrightFullProject): ProjectConfig {\n const config: ProjectConfig = {\n name: project.name || '',\n };\n\n // Project-level settings (these exist on FullProject, not FullConfig)\n if (this.isNonEmptyString(project.testDir)) {\n config.testDir = project.testDir;\n }\n\n if (typeof project.timeout === 'number') {\n config.timeout = project.timeout;\n }\n\n if (typeof project.retries === 'number') {\n config.retries = project.retries;\n }\n\n if (typeof project.repeatEach === 'number' && project.repeatEach > 1) {\n config.repeatEach = project.repeatEach;\n }\n\n // Dependencies (projects that must run first)\n if (Array.isArray(project.dependencies) && project.dependencies.length > 0) {\n config.dependencies = project.dependencies;\n }\n\n // Project-specific grep patterns\n if (project.grep) {\n const patterns = Array.isArray(project.grep) ? project.grep : [project.grep];\n const grepStrings = patterns.map((p) => p.source);\n if (grepStrings.length > 0) {\n config.grep = grepStrings;\n }\n }\n\n // Extract use options (browser, viewport, etc.)\n if (project.use) {\n const useOptions = this.extractUseOptions(project.use);\n if (Object.keys(useOptions).length > 0) {\n config.use = useOptions;\n }\n }\n\n return config;\n }\n\n /**\n * Extract use options from project.use\n */\n private extractUseOptions(use: PlaywrightFullProject['use']): ProjectUseOptions {\n const options: ProjectUseOptions = {};\n\n if (!use) return options;\n\n // Extract channel first (needed for browser inference)\n if (this.isNonEmptyString(use.channel)) {\n options.channel = use.channel;\n }\n\n // Determine browserName - explicit or inferred from channel\n const browserName = this.resolveBrowserName(use.browserName, use.defaultBrowserType, use.channel);\n if (browserName) {\n options.browserName = browserName;\n }\n\n if (typeof use.headless === 'boolean') {\n options.headless = use.headless;\n }\n\n if (use.viewport && typeof use.viewport === 'object') {\n options.viewport = {\n width: use.viewport.width,\n height: use.viewport.height,\n };\n } else if (use.viewport === null) {\n options.viewport = null;\n }\n\n if (this.isNonEmptyString(use.baseURL)) {\n options.baseURL = use.baseURL;\n }\n\n // Artifact capture settings\n const trace = this.normalizeArtifactMode(use.trace);\n if (trace) {\n options.trace = trace;\n }\n\n const screenshot = this.normalizeArtifactMode(use.screenshot);\n if (screenshot) {\n options.screenshot = screenshot;\n }\n\n const video = this.normalizeArtifactMode(use.video);\n if (video) {\n options.video = video;\n }\n\n // Device emulation\n if (typeof use.isMobile === 'boolean') {\n options.isMobile = use.isMobile;\n }\n\n // Locale\n if (this.isNonEmptyString(use.locale)) {\n options.locale = use.locale;\n }\n\n return options;\n }\n\n /**\n * Normalize artifact mode (trace/screenshot/video can be string or { mode: string })\n */\n private normalizeArtifactMode(value: unknown): string | undefined {\n if (!value) return undefined;\n if (typeof value === 'string' && value !== 'off') {\n return value;\n }\n if (typeof value === 'object' && value !== null && 'mode' in value) {\n const mode = (value as { mode: string }).mode;\n if (mode && mode !== 'off') {\n return mode;\n }\n }\n return undefined;\n }\n\n /**\n * Resolve browserName from explicit value, defaultBrowserType (device presets), or channel\n * Priority: browserName > defaultBrowserType > channel inference\n */\n private resolveBrowserName(\n browserName: string | undefined,\n defaultBrowserType: string | undefined,\n channel: string | undefined\n ): 'chromium' | 'firefox' | 'webkit' | undefined {\n const validBrowsers = ['chromium', 'firefox', 'webkit'];\n\n // 1. Use explicit browserName if valid\n if (browserName && validBrowsers.includes(browserName)) {\n return browserName as 'chromium' | 'firefox' | 'webkit';\n }\n\n // 2. Use defaultBrowserType from device presets (e.g., devices['Desktop Chrome'])\n if (defaultBrowserType && validBrowsers.includes(defaultBrowserType)) {\n return defaultBrowserType as 'chromium' | 'firefox' | 'webkit';\n }\n\n // 3. Infer from channel\n if (channel) {\n // Chrome-based channels use chromium engine\n if (\n [\n 'chrome',\n 'chrome-beta',\n 'chrome-dev',\n 'chrome-canary',\n 'msedge',\n 'msedge-beta',\n 'msedge-dev',\n 'msedge-canary',\n ].includes(channel)\n ) {\n return 'chromium';\n }\n }\n\n return undefined;\n }\n\n /**\n * Extract unique browsers from all projects\n */\n private extractBrowsersFromProjects(projects: PlaywrightFullProject[]): string[] {\n const browsers = new Set<string>();\n\n for (const project of projects) {\n const browserName = this.resolveBrowserName(\n project.use?.browserName,\n project.use?.defaultBrowserType,\n project.use?.channel\n );\n if (browserName) {\n browsers.add(browserName);\n }\n }\n\n return Array.from(browsers);\n }\n\n /**\n * Build skeleton from Suite\n */\n buildSkeleton(suite: PlaywrightSuite): RunSkeleton {\n const totalTests = suite.allTests().length;\n const suites = this.buildSuiteTree(suite);\n\n return {\n totalTests,\n suites,\n };\n }\n\n /**\n * Recursively build suite tree\n */\n private buildSuiteTree(suite: PlaywrightSuite): SkeletonSuite[] {\n const suites: SkeletonSuite[] = [];\n\n // Process child suites\n for (const childSuite of suite.suites) {\n const skeletonSuite: SkeletonSuite = {\n title: childSuite.title,\n type: childSuite.type === 'file' ? 'file' : 'describe',\n tests: childSuite.tests.map((test) => this.buildSkeletonTest(test)),\n };\n\n // Add file for file-type suites\n if (childSuite.type === 'file' && childSuite.location) {\n skeletonSuite.file = childSuite.location.file;\n }\n\n // Add location if available\n if (childSuite.location) {\n skeletonSuite.location = {\n file: childSuite.location.file,\n line: childSuite.location.line,\n column: childSuite.location.column,\n };\n }\n\n // Recursively build nested suites\n if (childSuite.suites.length > 0) {\n skeletonSuite.suites = this.buildSuiteTree(childSuite);\n }\n\n suites.push(skeletonSuite);\n }\n\n return suites;\n }\n\n /**\n * Build skeleton test from TestCase\n */\n private buildSkeletonTest(test: PlaywrightTestCase): SkeletonTest {\n const skeletonTest: SkeletonTest = {\n testId: test.id,\n title: test.title,\n location: {\n file: test.location.file,\n line: test.location.line,\n column: test.location.column,\n },\n };\n\n // Add tags if present\n if (test.tags && test.tags.length > 0) {\n skeletonTest.tags = test.tags;\n }\n\n // Add expected status\n if (test.expectedStatus) {\n skeletonTest.expectedStatus = test.expectedStatus;\n }\n\n // Add annotations if present\n if (test.annotations && test.annotations.length > 0) {\n skeletonTest.annotations = test.annotations.map((ann) => ({\n type: ann.type,\n description: ann.description,\n }));\n }\n\n return skeletonTest;\n }\n}\n","/**\n * Metadata collection main exports and aggregator\n */\n\nimport type {\n MetadataCollectionOptions,\n MetadataCollectionResult,\n MetadataCollectionSummary,\n CompleteMetadata,\n GitMetadata,\n CIMetadata,\n SystemMetadata,\n PlaywrightMetadata,\n} from './types.js';\nimport { GitMetadataCollector } from './git.js';\nimport { CIMetadataCollector } from './ci.js';\nimport { SystemMetadataCollector } from './system.js';\nimport { PlaywrightMetadataCollector } from './playwright.js';\n\n/**\n * Default metadata collection options\n * @internal\n */\nconst DEFAULT_METADATA_OPTIONS: Required<MetadataCollectionOptions> = {\n timeout: 5000,\n debug: false,\n};\n\n/**\n * Playwright FullConfig interface (minimal subset for metadata collection)\n */\ninterface PlaywrightFullConfig {\n fullyParallel?: boolean;\n workers?: number;\n shard?: {\n current: number;\n total: number;\n } | null;\n projects?: Array<{\n name?: string;\n }>;\n}\n\n/**\n * Playwright Suite interface (minimal subset for skeleton building)\n */\ninterface PlaywrightSuite {\n title: string;\n type: 'root' | 'project' | 'file' | 'describe';\n location?: {\n file: string;\n line: number;\n column: number;\n };\n suites: PlaywrightSuite[];\n tests: PlaywrightTestCase[];\n allTests(): PlaywrightTestCase[];\n}\n\n/**\n * Playwright TestCase interface (minimal subset for skeleton building)\n */\ninterface PlaywrightTestCase {\n id: string;\n title: string;\n location: {\n file: string;\n line: number;\n column: number;\n };\n tags: string[];\n expectedStatus: 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted';\n annotations: Array<{\n type: string;\n description?: string;\n }>;\n}\n\n/**\n * Main metadata aggregator class\n * Orchestrates collection from all metadata collectors\n */\nexport class MetadataAggregator {\n private options: Required<MetadataCollectionOptions>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private collectors: Array<{ collectWithResult: () => Promise<MetadataCollectionResult<any>> }> = [];\n private playwrightCollector?: PlaywrightMetadataCollector;\n\n constructor(options: MetadataCollectionOptions = {}) {\n this.options = { ...DEFAULT_METADATA_OPTIONS, ...options };\n }\n\n /**\n * Register a metadata collector\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n registerCollector(collector: { collectWithResult: () => Promise<MetadataCollectionResult<any>> }): void {\n this.collectors.push(collector);\n\n // Keep reference to PlaywrightMetadataCollector for skeleton building\n if (collector instanceof PlaywrightMetadataCollector) {\n this.playwrightCollector = collector;\n }\n }\n\n /**\n * Build skeleton from Suite (if PlaywrightMetadataCollector is registered with a suite)\n */\n buildSkeleton(suite: PlaywrightSuite): CompleteMetadata['skeleton'] {\n if (!this.playwrightCollector) {\n return undefined;\n }\n\n return this.playwrightCollector.buildSkeleton(suite);\n }\n\n /**\n * Collect metadata from all registered collectors\n * Uses Promise.allSettled for error isolation\n */\n async collectAll(): Promise<MetadataCollectionSummary> {\n const startTime = Date.now();\n\n // Collect from all collectors in parallel with error isolation\n const settledResults = await Promise.allSettled(\n this.collectors.map((collector) =>\n this.withTimeout(collector.collectWithResult(), this.options.timeout, 'Metadata collection')\n )\n );\n\n // Process results\n const results: MetadataCollectionResult[] = [];\n const metadata: CompleteMetadata = {};\n\n for (const settledResult of settledResults) {\n if (settledResult.status === 'fulfilled') {\n const result = settledResult.value;\n results.push(result);\n\n // Aggregate metadata by collector type\n this.aggregateMetadata(metadata, result);\n } else {\n // Handle rejected promises (shouldn't happen due to error handling in collectors)\n const error = settledResult.reason;\n console.warn('⚠️ TestDino: Metadata collector promise rejected:', error);\n\n results.push({\n data: {},\n success: false,\n error: error instanceof Error ? error.message : String(error),\n duration: 0,\n collector: 'unknown',\n });\n }\n }\n\n const totalDuration = Date.now() - startTime;\n const successCount = results.filter((r) => r.success).length;\n const failureCount = results.length - successCount;\n\n return {\n metadata,\n results,\n totalDuration,\n successCount,\n failureCount,\n };\n }\n\n /**\n * Aggregate individual collector results into complete metadata\n */\n private aggregateMetadata(metadata: CompleteMetadata, result: MetadataCollectionResult): void {\n const { collector, data } = result;\n\n switch (collector) {\n case 'git':\n metadata.git = data as GitMetadata;\n break;\n case 'ci':\n metadata.ci = data as CIMetadata;\n break;\n case 'system':\n metadata.system = data as SystemMetadata;\n break;\n case 'playwright':\n metadata.playwright = data as PlaywrightMetadata;\n break;\n default:\n if (this.options.debug) {\n console.warn(`⚠️ TestDino: Unknown metadata collector: ${collector}`);\n }\n }\n }\n\n /**\n * Utility method to run operation with timeout\n */\n private async withTimeout<T>(promise: Promise<T>, timeoutMs: number, operation: string): Promise<T> {\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error(`${operation} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n return Promise.race([promise, timeoutPromise]);\n }\n}\n\n// Re-export types\nexport type {\n MetadataCollectionOptions,\n MetadataCollectionResult,\n MetadataCollectionSummary,\n CompleteMetadata,\n GitMetadata,\n CIMetadata,\n SystemMetadata,\n PlaywrightMetadata,\n PRMetadata,\n ShardMetadata,\n MetadataCollector,\n RunSkeleton,\n SkeletonSuite,\n SkeletonTest,\n} from './types.js';\n\n// Re-export base class\nexport { BaseMetadataCollector } from './base.js';\n\n// Re-export collectors\nexport { GitMetadataCollector } from './git.js';\nexport { CIMetadataCollector } from './ci.js';\nexport { SystemMetadataCollector } from './system.js';\nexport { PlaywrightMetadataCollector } from './playwright.js';\n\n/**\n * Factory function to create a metadata aggregator with all collectors registered\n *\n * @param playwrightConfig Optional Playwright FullConfig for enhanced metadata collection\n * @param playwrightSuite Optional Playwright Suite for skeleton building\n * @returns MetadataAggregator instance with all collectors registered\n */\nexport function createMetadataCollector(\n playwrightConfig?: PlaywrightFullConfig,\n playwrightSuite?: PlaywrightSuite\n): MetadataAggregator {\n const aggregator = new MetadataAggregator();\n\n // Register all collectors\n aggregator.registerCollector(new GitMetadataCollector());\n aggregator.registerCollector(new CIMetadataCollector());\n aggregator.registerCollector(new SystemMetadataCollector());\n aggregator.registerCollector(\n new PlaywrightMetadataCollector({\n config: playwrightConfig,\n suite: playwrightSuite,\n })\n );\n\n return aggregator;\n}\n","/**\n * SAS Token client for requesting Azure Blob Storage upload tokens\n */\n\nimport axios, { type AxiosInstance } from 'axios';\nimport type { SASTokenResponse, SASTokenServerResponse } from './types.js';\nimport { sleep } from '../utils/index.js';\n\nexport interface SASTokenClientOptions {\n token: string;\n serverUrl: string;\n maxRetries?: number;\n retryDelay?: number;\n}\n\n/**\n * Client for requesting SAS tokens from TestDino server\n */\nexport class SASTokenClient {\n private client: AxiosInstance;\n private options: Required<SASTokenClientOptions>;\n\n constructor(options: SASTokenClientOptions) {\n this.options = {\n maxRetries: 2,\n retryDelay: 1000,\n ...options,\n };\n\n // Use x-api-key header for storage endpoint (different from Bearer token)\n this.client = axios.create({\n baseURL: this.options.serverUrl,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.options.token,\n },\n timeout: 10000,\n });\n }\n\n /**\n * Request SAS token for artifact uploads\n * @param expiryHours - Token validity duration (1-48 hours, default 48)\n * @returns SAS token response with upload instructions\n */\n async requestToken(expiryHours: number = 48): Promise<SASTokenResponse> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < this.options.maxRetries; attempt++) {\n try {\n const response = await this.client.post<SASTokenServerResponse>('/api/storage/token', undefined, {\n params: {\n expiryHours,\n permissions: 'write,create',\n },\n });\n\n if (!response.data.success) {\n throw new Error(response.data.message || 'SAS token request failed');\n }\n\n return response.data.data;\n } catch (error) {\n lastError = new Error(this.getErrorMessage(error));\n\n if (attempt < this.options.maxRetries - 1) {\n // Wait before retry with exponential backoff\n const delay = this.options.retryDelay * Math.pow(2, attempt);\n await sleep(delay);\n }\n }\n }\n\n throw lastError || new Error('Failed to request SAS token');\n }\n\n /**\n * Extract error message from various error types\n */\n private getErrorMessage(error: unknown): string {\n if (axios.isAxiosError(error)) {\n // Handle specific HTTP status codes\n if (error.response?.status === 401) {\n return 'Invalid API key for artifact uploads';\n }\n if (error.response?.status === 403) {\n return 'API key does not have write permission for uploads';\n }\n if (error.response?.status === 429) {\n return 'Rate limit exceeded for SAS token requests';\n }\n return error.response?.data?.message || error.message;\n }\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n }\n}\n","/**\n * Artifact uploader for Azure Blob Storage\n * Uploads test artifacts (screenshots, videos, traces) using SAS tokens\n */\n\nimport { createReadStream, statSync } from 'fs';\nimport { basename, extname } from 'path';\nimport axios from 'axios';\nimport type { SASTokenResponse, UploadResult, AttachmentForUpload } from './types.js';\nimport { sleep } from '../utils/index.js';\n\nexport interface ArtifactUploaderOptions {\n /** Upload timeout per file in milliseconds */\n timeout?: number;\n /** Maximum retries per file */\n maxRetries?: number;\n /** Debug mode for logging */\n debug?: boolean;\n}\n\n/**\n * Uploads artifacts to Azure Blob Storage using SAS tokens\n */\nexport class ArtifactUploader {\n private sasToken: SASTokenResponse;\n private options: Required<ArtifactUploaderOptions>;\n\n constructor(sasToken: SASTokenResponse, options: ArtifactUploaderOptions = {}) {\n this.sasToken = sasToken;\n this.options = {\n timeout: 60000,\n maxRetries: 2,\n debug: false,\n ...options,\n };\n }\n\n /**\n * Upload a single file to Azure Blob Storage\n * @param attachment - Attachment with path and content type\n * @param testId - Test identifier for organizing uploads\n * @returns Upload result with Azure URL or error\n */\n async uploadFile(attachment: AttachmentForUpload, testId: string): Promise<UploadResult> {\n const startTime = Date.now();\n const fileName = basename(attachment.path);\n\n try {\n // Validate file exists and get size\n const stats = statSync(attachment.path);\n const fileSize = stats.size;\n\n // Check file size limit\n if (fileSize > this.sasToken.maxSize) {\n return {\n name: attachment.name,\n success: false,\n error: `File size ${fileSize} bytes exceeds maximum ${this.sasToken.maxSize} bytes`,\n fileSize,\n duration: Date.now() - startTime,\n };\n }\n\n // Check file type is allowed\n const extension = extname(fileName).slice(1).toLowerCase();\n const allowedTypes = this.sasToken.allowedFileTypes;\n if (allowedTypes.length > 0 && !allowedTypes.includes(extension)) {\n return {\n name: attachment.name,\n success: false,\n error: `File extension '.${extension}' not in allowed types: ${allowedTypes.join(', ')}`,\n fileSize,\n duration: Date.now() - startTime,\n };\n }\n\n // Build upload URL: baseUrl/pathPrefix/testId/fileName\n const uploadUrl = this.buildUploadUrl(testId, fileName);\n\n // Upload with retries\n await this.uploadWithRetry(attachment.path, uploadUrl, attachment.contentType);\n\n // Build public URL (without SAS token query params)\n const publicUrl = this.buildPublicUrl(testId, fileName);\n\n if (this.options.debug) {\n console.log(`📤 Uploaded: ${attachment.name} → ${publicUrl}`);\n }\n\n return {\n name: attachment.name,\n success: true,\n uploadUrl: publicUrl,\n fileSize,\n duration: Date.now() - startTime,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n if (this.options.debug) {\n console.warn(`⚠️ Upload failed: ${attachment.name} - ${errorMessage}`);\n }\n\n return {\n name: attachment.name,\n success: false,\n error: errorMessage,\n duration: Date.now() - startTime,\n };\n }\n }\n\n /**\n * Upload multiple attachments in parallel\n * @param attachments - Array of attachments to upload\n * @param testId - Test identifier for organizing uploads\n * @returns Array of upload results\n */\n async uploadAll(attachments: AttachmentForUpload[], testId: string): Promise<UploadResult[]> {\n if (attachments.length === 0) {\n return [];\n }\n\n // Filter to only attachments with valid paths\n const validAttachments = attachments.filter((a) => a.path);\n\n if (validAttachments.length === 0) {\n return [];\n }\n\n // Upload all in parallel using Promise.allSettled\n const results = await Promise.allSettled(validAttachments.map((attachment) => this.uploadFile(attachment, testId)));\n\n // Convert settled results to upload results\n return results.map((result, index) => {\n if (result.status === 'fulfilled') {\n return result.value;\n }\n // Promise rejection (shouldn't happen as uploadFile catches errors)\n return {\n name: validAttachments[index].name,\n success: false,\n error: result.reason?.message || 'Upload failed',\n };\n });\n }\n\n /**\n * Build the full upload URL with SAS token\n * Format: containerUrl/blobPath/uniqueId/testId/fileName?sasToken\n */\n private buildUploadUrl(testId: string, fileName: string): string {\n const { containerUrl, blobPath, uniqueId, sasToken } = this.sasToken;\n const path = `${blobPath}/${uniqueId}/${this.sanitizeTestId(testId)}/${fileName}`;\n return `${containerUrl}/${path}?${sasToken}`;\n }\n\n /**\n * Build public URL without SAS token (for storage in events)\n * Format: containerUrl/blobPath/uniqueId/testId/fileName\n */\n private buildPublicUrl(testId: string, fileName: string): string {\n const { containerUrl, blobPath, uniqueId } = this.sasToken;\n const path = `${blobPath}/${uniqueId}/${this.sanitizeTestId(testId)}/${fileName}`;\n return `${containerUrl}/${path}`;\n }\n\n /**\n * Sanitize test ID for use in URL path\n */\n private sanitizeTestId(testId: string): string {\n // Replace characters that are problematic in URLs\n return testId.replace(/[^a-zA-Z0-9-_]/g, '_');\n }\n\n /**\n * Upload file with retry logic\n */\n private async uploadWithRetry(filePath: string, uploadUrl: string, contentType: string): Promise<void> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < this.options.maxRetries; attempt++) {\n try {\n await this.doUpload(filePath, uploadUrl, contentType);\n return;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n if (attempt < this.options.maxRetries - 1) {\n // Wait before retry with exponential backoff\n const delay = 1000 * Math.pow(2, attempt);\n await sleep(delay);\n }\n }\n }\n\n throw lastError || new Error('Upload failed after retries');\n }\n\n /**\n * Perform actual file upload to Azure\n */\n private async doUpload(filePath: string, uploadUrl: string, contentType: string): Promise<void> {\n const fileStream = createReadStream(filePath);\n const stats = statSync(filePath);\n\n await axios.put(uploadUrl, fileStream, {\n headers: {\n 'Content-Type': contentType,\n 'Content-Length': stats.size,\n 'x-ms-blob-type': 'BlockBlob',\n },\n timeout: this.options.timeout,\n maxContentLength: Infinity,\n maxBodyLength: Infinity,\n });\n }\n\n /**\n * Check if SAS token is still valid\n */\n isTokenValid(): boolean {\n const expiresAt = new Date(this.sasToken.expiresAt).getTime();\n const now = Date.now();\n // Consider token invalid if it expires within 5 minutes\n return expiresAt > now + 5 * 60 * 1000;\n }\n\n /**\n * Get the unique ID for this upload session\n */\n getSessionId(): string {\n return this.sasToken.uniqueId;\n }\n}\n","/**\n * Incremental coverage merger using istanbul-lib-coverage.\n * Merges per-test coverage fragments as tests complete.\n * See docs/CODE_COVERAGE.md for design decisions and algorithms.\n */\n\nimport istanbulCoverage from 'istanbul-lib-coverage';\nimport picomatch from 'picomatch';\nimport { normalizePath } from '../utils/index.js';\nimport type { CoverageFragment, CoverageSummary, FileCoverage, CoverageMetric, IstanbulCoverageMap } from './types.js';\n\n/**\n * Type adapter: our IstanbulCoverageMap → istanbul-lib-coverage CoverageMapData.\n * Both represent the same JSON shape; the double cast is isolated here.\n */\nfunction toIstanbulMapData(map: IstanbulCoverageMap): istanbulCoverage.CoverageMapData {\n return map as unknown as istanbulCoverage.CoverageMapData;\n}\n\n/**\n * Type adapter: istanbul-lib-coverage CoverageMapData → our IstanbulCoverageMap.\n */\nfunction fromIstanbulMapData(data: istanbulCoverage.CoverageMapData): IstanbulCoverageMap {\n return data as unknown as IstanbulCoverageMap;\n}\n\n/**\n * Options for filtering coverage files during merge.\n */\nexport interface CoverageMergerOptions {\n /** Glob patterns — only files whose path matches a pattern are included */\n include?: string[];\n /** Glob patterns — files whose path matches a pattern are excluded */\n exclude?: string[];\n /** Error callback for merge failures. Falls back to console.warn if not provided. */\n onError?: (message: string) => void;\n}\n\n/**\n * Incremental coverage merger.\n * Merges per-test Istanbul coverage fragments into a single coverage map.\n */\nexport class CoverageMerger {\n private coverageMap = istanbulCoverage.createCoverageMap({});\n private hasData = false;\n private includePatterns?: string[];\n private excludePatterns?: string[];\n private onError?: (message: string) => void;\n\n constructor(options?: CoverageMergerOptions) {\n this.includePatterns = options?.include;\n this.excludePatterns = options?.exclude;\n this.onError = options?.onError;\n }\n\n /**\n * Merge a coverage fragment from a completed test.\n * The fragment is merged directly and can be GC'd immediately.\n */\n addFragment(fragment: CoverageFragment): void {\n if (!fragment.istanbul) return;\n\n try {\n const filtered = this.filterCoverageMap(fragment.istanbul);\n this.coverageMap.merge(toIstanbulMapData(filtered));\n if (this.coverageMap.files().length > 0) {\n this.hasData = true;\n }\n } catch (error) {\n // Malformed coverage data — report and skip\n const msg = `[TestDino] Failed to merge coverage fragment: ${error instanceof Error ? error.message : String(error)}`;\n if (this.onError) {\n this.onError(msg);\n } else {\n console.warn(msg);\n }\n }\n }\n\n /**\n * Filter files from a coverage map based on include/exclude glob patterns.\n */\n private filterCoverageMap(coverageMap: IstanbulCoverageMap): IstanbulCoverageMap {\n const hasInclude = this.includePatterns && this.includePatterns.length > 0;\n const hasExclude = this.excludePatterns && this.excludePatterns.length > 0;\n\n if (!hasInclude && !hasExclude) return coverageMap;\n\n const isExcluded = hasExclude ? picomatch(this.excludePatterns!) : undefined;\n const isIncluded = hasInclude ? picomatch(this.includePatterns!) : undefined;\n\n const filtered: IstanbulCoverageMap = {};\n\n for (const [filePath, fileCoverage] of Object.entries(coverageMap)) {\n if (isExcluded && isExcluded(filePath)) {\n continue;\n }\n\n if (isIncluded && !isIncluded(filePath)) {\n continue;\n }\n\n filtered[filePath] = fileCoverage;\n }\n\n return filtered;\n }\n\n /**\n * Whether any coverage data has been collected.\n */\n get hasCoverage(): boolean {\n return this.hasData;\n }\n\n /**\n * Compute aggregate summary metrics from the merged coverage map.\n */\n computeSummary(): CoverageSummary {\n const globalSummary = this.coverageMap.getCoverageSummary();\n return {\n lines: extractMetric(globalSummary.lines),\n branches: extractMetric(globalSummary.branches),\n functions: extractMetric(globalSummary.functions),\n statements: extractMetric(globalSummary.statements),\n };\n }\n\n /**\n * Compute per-file coverage metrics.\n * Normalizes file paths to be relative to git root.\n */\n computeFileCoverage(gitRoot?: string): FileCoverage[] {\n const root = gitRoot || process.cwd();\n\n return this.coverageMap.files().map((filePath: string) => {\n const fileCoverage = this.coverageMap.fileCoverageFor(filePath);\n const fileSummary = fileCoverage.toSummary();\n\n // Normalize path to be relative to git root\n const normalizedPath = normalizePath(filePath, root);\n\n return {\n path: normalizedPath,\n lines: extractMetric(fileSummary.lines),\n branches: extractMetric(fileSummary.branches),\n functions: extractMetric(fileSummary.functions),\n statements: extractMetric(fileSummary.statements),\n };\n });\n }\n\n /**\n * Get the raw merged coverage map (for local report generation or compact extraction).\n */\n getRawCoverageMap(): istanbulCoverage.CoverageMap {\n return this.coverageMap;\n }\n\n /**\n * Get the merged coverage map as a plain JSON object.\n */\n toJSON(): IstanbulCoverageMap {\n return fromIstanbulMapData(this.coverageMap.toJSON());\n }\n}\n\ninterface IstanbulSummaryMetric {\n total: number;\n covered: number;\n skipped: number;\n pct: number;\n}\n\nfunction extractMetric(metric: IstanbulSummaryMetric): CoverageMetric {\n return {\n total: metric.total,\n covered: metric.covered,\n pct: metric.pct,\n };\n}\n","/**\n * Compact count extractor for cross-shard coverage merging.\n * Extracts minimal hit count data from merged coverage maps.\n * See docs/CODE_COVERAGE.md for design decisions and algorithms.\n */\n\nimport { createHash } from 'crypto';\nimport { normalizePath } from '../utils/index.js';\nimport type { CompactCoverageData, CompactFileCounts, IstanbulFileCoverage } from './types.js';\n\n/**\n * Extract compact hit counts from a merged coverage map JSON.\n *\n * @param coverageMapJSON - Raw coverage map data from CoverageMerger.toJSON()\n * @param gitRoot - Git repository root for path normalization (optional)\n * @returns Compact coverage data suitable for cross-shard upload\n */\nexport function extractCompactCounts(\n coverageMapJSON: Record<string, IstanbulFileCoverage>,\n gitRoot?: string\n): CompactCoverageData {\n const files: Record<string, CompactFileCounts> = {};\n let fileCount = 0;\n\n for (const [filePath, fileCoverage] of Object.entries(coverageMapJSON)) {\n const normalizedPath = normalizePath(filePath, gitRoot);\n\n files[normalizedPath] = {\n s: fileCoverage.s,\n f: fileCoverage.f,\n b: fileCoverage.b,\n totals: {\n s: Object.keys(fileCoverage.statementMap || {}).length,\n f: Object.keys(fileCoverage.fnMap || {}).length,\n b: countBranchPaths(fileCoverage.branchMap || {}),\n },\n shapeHash: computeShapeHash(fileCoverage),\n };\n fileCount++;\n }\n\n return { files, fileCount };\n}\n\n/**\n * Count total branch paths across all branches in a file.\n * Each branch (if/else, ternary, switch) can have multiple paths.\n */\nfunction countBranchPaths(branchMap: IstanbulFileCoverage['branchMap']): number {\n let total = 0;\n for (const branch of Object.values(branchMap)) {\n total += (branch.locations || []).length;\n }\n return total;\n}\n\n/**\n * Compute a shape hash from the structural maps of a file.\n * Used to detect build mismatches across shards — if two shards produce\n * different shape hashes for the same file, the builds were different.\n *\n * The hash encodes the count of statements, functions, and branch paths,\n * plus the per-branch path counts for granularity.\n */\nfunction computeShapeHash(fileCoverage: IstanbulFileCoverage): string {\n const branchMap = fileCoverage.branchMap || {};\n const shape = {\n s: Object.keys(fileCoverage.statementMap || {}).length,\n f: Object.keys(fileCoverage.fnMap || {}).length,\n b: countBranchPaths(branchMap),\n bp: Object.values(branchMap).map((b) => (b.locations || []).length),\n };\n return createHash('sha256').update(JSON.stringify(shape)).digest('hex').slice(0, 12);\n}\n","/**\n * Istanbul HTML report generator.\n * Uses the standard istanbul-reports package to generate a full HTML report\n * with source code display, line-by-line coverage, and drill-down navigation.\n */\n\nimport { createContext } from 'istanbul-lib-report';\nimport { create as createReporter } from 'istanbul-reports';\nimport { mkdir, rm } from 'fs/promises';\nimport type { CoverageMerger } from './merger.js';\n\nexport interface IstanbulHtmlReportOptions {\n outputDir: string;\n}\n\n/**\n * Generate a full Istanbul HTML coverage report in the specified output directory.\n * Returns the path to the generated index.html entry point.\n */\nexport async function generateIstanbulHtmlReport(\n coverageMerger: CoverageMerger,\n options: IstanbulHtmlReportOptions\n): Promise<string> {\n // Clean output directory to remove stale files from previous runs\n await rm(options.outputDir, { recursive: true, force: true }).catch(() => {});\n await mkdir(options.outputDir, { recursive: true });\n\n // Get the raw coverage map from the merger\n const coverageMap = coverageMerger.getRawCoverageMap();\n\n // Create Istanbul report context\n const context = createContext({\n dir: options.outputDir,\n watermarks: {\n statements: [50, 80],\n functions: [50, 80],\n branches: [50, 80],\n lines: [50, 80],\n },\n coverageMap,\n });\n\n // Create HTML reporter\n const reporter = createReporter('html', {\n skipEmpty: false,\n subdir: '',\n });\n\n // Generate the report\n reporter.execute(context);\n\n // Return the path to the main index file\n return `${options.outputDir}/index.html`;\n}\n","/**\n * Coverage fixtures for automatic Istanbul coverage collection.\n *\n * The fixture runs automatically (auto: true) for every test:\n * 1. Test executes normally\n * 2. After test: extracts window.__coverage__ from the page\n * 3. Attaches coverage JSON to testInfo for the reporter to pick up\n *\n * Gracefully no-ops when the app is not instrumented (window.__coverage__ absent).\n */\n\nimport { test as base, expect, type Page, type TestInfo } from '@playwright/test';\nimport type { IstanbulCoverageMap, CoverageFragment } from './types.js';\n\n// Timeout for extracting Istanbul coverage from the page (30 seconds)\nconst COVERAGE_EXTRACT_TIMEOUT_MS = 30_000;\n\ntype CoverageFixtures = {\n _testdinoCoverage: void;\n};\n\n/**\n * Extract Istanbul coverage from a Playwright Page.\n * Returns null if window.__coverage__ does not exist or if extraction times out.\n */\nexport async function extractCoverageFromPage(\n page: Page,\n timeoutMs: number = COVERAGE_EXTRACT_TIMEOUT_MS\n): Promise<IstanbulCoverageMap | null> {\n return Promise.race([\n page.evaluate(() => (globalThis as unknown as { __coverage__?: IstanbulCoverageMap }).__coverage__ ?? null),\n new Promise<null>((resolve) => setTimeout(() => resolve(null), timeoutMs)),\n ]).catch(() => null);\n}\n\n/**\n * Attach an Istanbul coverage map to a TestInfo as a JSON attachment.\n * The reporter picks up the attachment by name ('testdino-coverage').\n */\nexport async function attachCoverageToTestInfo(testInfo: TestInfo, coverage: IstanbulCoverageMap): Promise<void> {\n const fragment: CoverageFragment = {\n istanbul: coverage,\n };\n\n await testInfo.attach('testdino-coverage', {\n body: JSON.stringify(fragment),\n contentType: 'application/json',\n });\n}\n\nexport const coverageFixtures = {\n _testdinoCoverage: [\n async ({ page }: { page: Page }, use: () => Promise<void>, testInfo: TestInfo) => {\n // Test executes here\n await use();\n\n // Extract Istanbul coverage (any browser, if app is instrumented).\n const istanbulCoverage = await extractCoverageFromPage(page);\n\n // Attach coverage data to test result for the reporter to pick up\n if (istanbulCoverage) {\n await attachCoverageToTestInfo(testInfo, istanbulCoverage);\n }\n },\n { auto: true },\n ],\n};\n\n/**\n * Pre-extended test with coverage fixtures for direct import.\n *\n * Usage:\n * ```typescript\n * import { test, expect } from '@testdino/playwright';\n *\n * test('my test', async ({ page }) => {\n * // coverage collected automatically\n * });\n * ```\n */\nexport const test = base.extend<CoverageFixtures>(\n coverageFixtures as Parameters<typeof base.extend<CoverageFixtures>>[0]\n);\nexport { expect };\n","/**\n * Reporter logging and summary rendering utilities\n */\n\nimport type { FullResult } from '@playwright/test/reporter';\nimport type { CoverageDataEvent, RunMetadata } from '../types/index.js';\n\n// Dynamic import for chalk (ESM-only package)\nlet chalkPromise: Promise<typeof import('chalk').default> | null = null;\n\n/**\n * Dynamically import chalk (ESM-only package) for CJS compatibility only once per process using memoization.\n *\n * @returns Promise resolving to the chalk default export\n * @throws Never - returns plain text fallback on import failure\n */\nasync function getChalk(): Promise<typeof import('chalk').default> {\n if (!chalkPromise) {\n chalkPromise = import('chalk')\n .then((m) => m.default)\n .catch((error) => {\n console.warn('⚠️ TestDino: Failed to load chalk, using plain text output');\n console.debug('Chalk import error:', error.message);\n // Return no-op chalk that returns strings unchanged\n return {\n green: (s: string) => s,\n yellow: (s: string) => s,\n red: (s: string) => s,\n blue: (s: string) => s,\n cyan: (s: string) => s,\n dim: (s: string) => s,\n bold: (s: string) => s,\n } as unknown as typeof import('chalk').default;\n });\n }\n return chalkPromise;\n}\n\nexport interface ReporterLogOptions {\n debug: boolean;\n}\n\nexport interface RunSummaryData {\n runId: string;\n runMetadata: RunMetadata | null;\n testCounts: {\n passed: number;\n failed: number;\n skipped: number;\n timedOut: number;\n interrupted: number;\n flaky: number;\n retried: number;\n };\n totalTests: number;\n workerCount: number;\n projectNames: Set<string>;\n shardInfo?: { current: number; total: number };\n useHttpFallback: boolean;\n lastCoverageEvent: CoverageDataEvent | null;\n}\n\n// ── Formatting helpers (exported for testing) ──\n\nfunction stripAnsi(str: string): string {\n // eslint-disable-next-line no-control-regex\n return str.replace(/\\u001b\\[[0-9;]*m/g, '');\n}\n\nexport function pad(str: string, len: number): string {\n const vLen = stripAnsi(str).length;\n return vLen >= len ? str : str + ' '.repeat(len - vLen);\n}\n\nexport function padStart(str: string, len: number): string {\n const vLen = stripAnsi(str).length;\n return vLen >= len ? str : ' '.repeat(len - vLen) + str;\n}\n\nexport function formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n const seconds = ms / 1000;\n if (seconds < 60) return `${seconds.toFixed(1)}s`;\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = (seconds % 60).toFixed(0);\n return `${minutes}m ${remainingSeconds}s`;\n}\n\nexport function shortenPath(filePath: string): string {\n const markers = ['/src/', '/lib/', '/app/'];\n for (const marker of markers) {\n const idx = filePath.indexOf(marker);\n if (idx !== -1) return filePath.slice(idx + 1);\n }\n const parts = filePath.split('/');\n return parts.length > 2 ? parts.slice(-2).join('/') : filePath;\n}\n\ntype ChalkInstance = typeof import('chalk').default;\n\nexport function colorPct(pct: number, chalk: ChalkInstance): string {\n const color = pct >= 80 ? chalk.green : pct >= 50 ? chalk.yellow : chalk.red;\n return color(pct === 100 ? '100%' : `${pct.toFixed(1)}%`);\n}\n\n// ── Summary rendering ──\n\nfunction printCoverageTableWithChalk(event: CoverageDataEvent, chalk: ChalkInstance): void {\n const { summary, files } = event;\n const row = (content: string) => ` ${chalk.dim('│')} ${content}`;\n const nameW = 30;\n const colW = 10;\n\n console.log(row(`${chalk.bold('Coverage')} ${chalk.dim(`${files.length} files`)}`));\n console.log(row(''));\n\n // Header\n console.log(\n row(\n ` ${chalk.dim(`${pad('File', nameW)}${padStart('Stmts', colW)}${padStart('Branch', colW)}${padStart('Funcs', colW)}${padStart('Lines', colW)}`)}`\n )\n );\n\n // Per-file rows\n for (const file of files) {\n const name = shortenPath(file.path);\n const short = name.length > nameW ? name.slice(0, nameW - 1) + '~' : name;\n console.log(\n row(\n ` ${pad(short, nameW)}` +\n padStart(colorPct(file.statements.pct, chalk), colW) +\n padStart(colorPct(file.branches.pct, chalk), colW) +\n padStart(colorPct(file.functions.pct, chalk), colW) +\n padStart(colorPct(file.lines.pct, chalk), colW)\n )\n );\n }\n\n // Separator + totals\n console.log(row(` ${chalk.dim('─'.repeat(nameW + colW * 4))}`));\n console.log(\n row(\n ` ${chalk.bold(pad('All files', nameW))}` +\n padStart(colorPct(summary.statements.pct, chalk), colW) +\n padStart(colorPct(summary.branches.pct, chalk), colW) +\n padStart(colorPct(summary.functions.pct, chalk), colW) +\n padStart(colorPct(summary.lines.pct, chalk), colW)\n )\n );\n}\n\nfunction printRunSummaryWithChalk(\n result: FullResult,\n streamingSuccess: boolean,\n data: RunSummaryData,\n chalk: ChalkInstance\n): void {\n const W = 72;\n const topBorder = ` ${chalk.dim(`┌${'─'.repeat(W)}┐`)}`;\n const bottomBorder = ` ${chalk.dim(`└${'─'.repeat(W)}┘`)}`;\n const divider = ` ${chalk.dim(`├${'─'.repeat(W)}┤`)}`;\n const row = (content: string) => ` ${chalk.dim('│')} ${content}`;\n\n console.log('');\n console.log(topBorder);\n console.log(row(chalk.bold('TestDino Run Summary')));\n console.log(divider);\n\n // Run & Git\n console.log(row(`${chalk.dim('Run')} ${data.runId}`));\n const git = data.runMetadata?.git;\n if (git?.branch || git?.commit?.hash) {\n const branch = git.branch ? chalk.cyan(git.branch) : '';\n const sha = git.commit?.hash ? chalk.dim(git.commit.hash.slice(0, 7)) : '';\n const sep = branch && sha ? ` ${chalk.dim('@')} ` : '';\n const msg = git.commit?.message ? ` ${chalk.dim(git.commit.message.split('\\n')[0].slice(0, 50))}` : '';\n console.log(row(`${chalk.dim('Git')} ${branch}${sep}${sha}${msg}`));\n }\n\n console.log(divider);\n\n // Test Results\n const statusColor = result.status === 'passed' ? chalk.green : result.status === 'failed' ? chalk.red : chalk.yellow;\n const statusLabel = result.status === 'timedout' ? 'timed out' : result.status;\n console.log(\n row(\n `${chalk.bold('Tests')} ${statusColor(statusLabel.toUpperCase())} ${chalk.dim(formatDuration(result.duration))}`\n )\n );\n\n const counts: string[] = [];\n if (data.testCounts.passed > 0) counts.push(chalk.green(`${data.testCounts.passed} passed`));\n if (data.testCounts.failed > 0) counts.push(chalk.red(`${data.testCounts.failed} failed`));\n if (data.testCounts.flaky > 0) counts.push(chalk.yellow(`${data.testCounts.flaky} flaky`));\n if (data.testCounts.skipped > 0) counts.push(chalk.dim(`${data.testCounts.skipped} skipped`));\n if (data.testCounts.timedOut > 0) counts.push(chalk.red(`${data.testCounts.timedOut} timed out`));\n if (data.testCounts.interrupted > 0) counts.push(chalk.yellow(`${data.testCounts.interrupted} interrupted`));\n const retriedStr = data.testCounts.retried > 0 ? ` ${chalk.dim(`(${data.testCounts.retried} retries)`)}` : '';\n console.log(row(` ${counts.join(chalk.dim(' · '))} ${chalk.dim(`of ${data.totalTests}`)}${retriedStr}`));\n\n // Workers & Shards\n console.log(divider);\n const shardStr = data.shardInfo ? `${data.shardInfo.current}/${data.shardInfo.total}` : '—';\n console.log(\n row(\n `${pad(`${chalk.dim('Workers')} ${data.workerCount > 0 ? data.workerCount : '—'}`, 28)}` +\n `${pad(`${chalk.dim('Shard')} ${shardStr}`, 28)}` +\n `${chalk.dim('Projects')} ${data.projectNames.size > 0 ? Array.from(data.projectNames).join(', ') : '—'}`\n )\n );\n\n // Streaming\n console.log(divider);\n const transport = data.useHttpFallback ? 'HTTP' : 'WebSocket';\n const streamIcon = streamingSuccess ? chalk.green('sent') : chalk.red('failed');\n console.log(row(`${chalk.bold('Stream')} ${streamIcon} ${chalk.dim(`via ${transport}`)}`));\n\n // Coverage\n if (data.lastCoverageEvent) {\n console.log(divider);\n printCoverageTableWithChalk(data.lastCoverageEvent, chalk);\n }\n\n console.log(bottomBorder);\n console.log('');\n}\n\n/**\n * Create a reporter log helper with consistent prefixes.\n * printRunSummary and printCoverageTable use dynamic chalk import (async).\n */\nexport const createReporterLog = (options: ReporterLogOptions) => ({\n success: (msg: string) => console.log(`✅ TestDino: ${msg}`),\n warn: (msg: string) => console.warn(`⚠️ TestDino: ${msg}`),\n error: (msg: string) => console.error(`❌ TestDino: ${msg}`),\n info: (msg: string) => console.log(`ℹ️ TestDino: ${msg}`),\n debug: (msg: string) => {\n if (options.debug) {\n console.log(`🔍 TestDino: ${msg}`);\n }\n },\n printRunSummary: async (result: FullResult, streamingSuccess: boolean, data: RunSummaryData): Promise<void> => {\n const chalk = await getChalk();\n printRunSummaryWithChalk(result, streamingSuccess, data, chalk);\n },\n printCoverageTable: async (event: CoverageDataEvent): Promise<void> => {\n const chalk = await getChalk();\n printCoverageTableWithChalk(event, chalk);\n },\n});\n\nexport type ReporterLog = ReturnType<typeof createReporterLog>;\n"],"mappings":"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","names":["readFile","version","readFile","test","axios","axios","axios","axios","istanbulCoverage","test"]}
1
+ {"version":3,"sources":["../src/reporter/index.ts","../src/streaming/websocket.ts","../src/reporter/errors.ts","../src/streaming/http.ts","../src/utils/index.ts","../src/streaming/buffer.ts","../src/metadata/git.ts","../src/metadata/base.ts","../src/metadata/ci.ts","../src/metadata/system.ts","../src/metadata/playwright.ts","../src/metadata/index.ts","../src/uploads/sas-token-client.ts","../src/uploads/artifact-uploader.ts","../src/code-coverage/merger.ts","../src/code-coverage/compact.ts","../src/code-coverage/html-report.ts","../src/code-coverage/fixtures.ts","../src/reporter/log.ts"],"sourcesContent":["/**\n * TestDino Playwright Reporter\n * Streams test execution data in real-time\n */\n\nimport type {\n Reporter,\n FullConfig,\n Suite,\n TestCase,\n TestResult,\n TestStep,\n FullResult,\n TestError,\n} from '@playwright/test/reporter';\nimport { randomUUID } from 'crypto';\nimport { readFileSync, existsSync } from 'fs';\nimport { WebSocketClient } from '../streaming/websocket.js';\nimport { HttpClient } from '../streaming/http.js';\nimport { EventBuffer } from '../streaming/buffer.js';\nimport { createMetadataCollector } from '../metadata/index.js';\nimport type {\n TestdinoConfig,\n TestEvent,\n TestRunBeginEvent,\n TestBeginEvent,\n TestStepBeginEvent,\n TestStepEndEvent,\n TestEndEvent,\n TestRunEndEvent,\n TestRunErrorEvent,\n TestConsoleOutEvent,\n TestConsoleErrEvent,\n CoverageDataEvent,\n RunMetadata,\n Annotation,\n} from '../types/index.js';\nimport { isQuotaError } from './errors.js';\nimport { SASTokenClient, ArtifactUploader, type UploadResult, type AttachmentForUpload } from '../uploads/index.js';\nimport { CoverageMerger, extractCompactCounts, generateIstanbulHtmlReport } from '../code-coverage/index.js';\nimport type { CoverageFragment } from '../code-coverage/index.js';\nimport { createReporterLog, type ReporterLog, type RunSummaryData } from './log.js';\nimport { isDebugEnabled } from '../utils/index.js';\n\n// Maximum console chunk size (10KB in characters)\nconst MAX_CONSOLE_CHUNK_SIZE = 10_000;\n\n// Maximum number of events to buffer\nconst MAX_BUFFER_SIZE = 10;\n\n// File count threshold for coverage size warning\nconst COVERAGE_FILE_COUNT_WARNING = 500;\n\nexport class TestdinoReporter implements Reporter {\n private config: TestdinoConfig;\n private wsClient: WebSocketClient | null = null;\n private httpClient: HttpClient | null = null;\n private buffer: EventBuffer | null = null;\n private runId: string;\n private useHttpFallback = false;\n private sequenceNumber = 0;\n\n // Shard and timing info for interruption handling\n private shardInfo?: { current: number; total: number };\n private runStartTime?: number;\n\n // Signal handler management\n private sigintHandler?: () => void;\n private sigtermHandler?: () => void;\n private isShuttingDown = false;\n\n // Quota tracking\n private quotaExceeded = false;\n private pendingQuotaError: unknown = null;\n\n // Session ID from HTTP auth, passed to WebSocket for session reuse\n private sessionId: string | null = null;\n\n // Artifact upload\n private artifactUploader: ArtifactUploader | null = null;\n private artifactsEnabled = true; // Default: enabled\n\n // Deferred initialization - resolves true on success, false on failure\n private initPromise: Promise<boolean> | null = null;\n private initFailed = false;\n\n // Promises for onTestEnd; must be awaited in onEnd to prevent data loss\n private pendingTestEndPromises: Set<Promise<void>> = new Set();\n\n // Logger for consistent output\n private log: ReporterLog;\n\n // Coverage collection\n private coverageEnabled = false;\n private coverageMerger: CoverageMerger | null = null;\n private warnedCoverageDisconnect = false;\n private coverageThresholdFailed = false;\n\n // Test result tracking for summary\n private testCounts = { passed: 0, failed: 0, skipped: 0, timedOut: 0, interrupted: 0, flaky: 0, retried: 0 };\n private totalTests = 0;\n private lastCoverageEvent: CoverageDataEvent | null = null;\n\n // Detailed tracking for summary output\n private projectNames = new Set<string>();\n private runMetadata: RunMetadata | null = null;\n private workerCount = 0;\n\n constructor(config: TestdinoConfig = {}) {\n // Load CLI config if available\n const cliConfig = this.loadCliConfig();\n\n // Merge configs: CLI config takes precedence over constructor config\n this.config = { ...config, ...cliConfig };\n\n this.runId = randomUUID();\n\n // Initialize logger with debug setting from config\n this.log = createReporterLog({ debug: this.config.debug ?? false });\n\n // Initialize coverage merger if coverage is enabled\n this.coverageEnabled = this.config.coverage?.enabled ?? false;\n if (this.coverageEnabled) {\n this.coverageMerger = new CoverageMerger({\n include: this.config.coverage?.include,\n exclude: this.config.coverage?.exclude,\n onError: (msg) => this.log.warn(msg),\n });\n }\n\n // Create buffer early to collect events before async init; flush waits for initPromise.\n this.buffer = new EventBuffer({\n maxSize: MAX_BUFFER_SIZE,\n onFlush: async (events) => {\n if (this.initPromise) {\n const success = await this.initPromise;\n if (!success) return; // Init failed - discard events\n }\n await this.sendEvents(events);\n },\n });\n }\n\n /**\n * Load configuration from CLI temp file if available\n */\n private loadCliConfig(): TestdinoConfig {\n const cliConfigPath = process.env.TESTDINO_CLI_CONFIG_PATH;\n\n if (!cliConfigPath) {\n return {};\n }\n\n try {\n if (!existsSync(cliConfigPath)) {\n return {};\n }\n\n const configContent = readFileSync(cliConfigPath, 'utf-8');\n const cliConfig = JSON.parse(configContent) as Record<string, unknown>;\n\n const mappedConfig: TestdinoConfig = {};\n\n if (cliConfig.token !== undefined && typeof cliConfig.token === 'string') {\n mappedConfig.token = cliConfig.token;\n }\n\n if (cliConfig.serverUrl !== undefined && typeof cliConfig.serverUrl === 'string') {\n mappedConfig.serverUrl = cliConfig.serverUrl;\n }\n\n if (cliConfig.debug !== undefined && typeof cliConfig.debug === 'boolean') {\n mappedConfig.debug = cliConfig.debug;\n }\n\n // CI run ID for grouping sharded runs\n if (cliConfig.ciRunId !== undefined && typeof cliConfig.ciRunId === 'string') {\n mappedConfig.ciRunId = cliConfig.ciRunId;\n }\n\n // Handle artifacts flag\n if (cliConfig.artifacts !== undefined && typeof cliConfig.artifacts === 'boolean') {\n mappedConfig.artifacts = cliConfig.artifacts;\n }\n\n // Handle coverage config\n if (typeof cliConfig.coverage === 'object' && cliConfig.coverage !== null) {\n mappedConfig.coverage = cliConfig.coverage as import('../code-coverage/types.js').CoverageConfig;\n }\n\n return mappedConfig;\n } catch (error) {\n // Silently fail - CLI config is optional\n if (isDebugEnabled()) {\n console.warn(\n '⚠️ TestDino: Failed to load CLI config:',\n error instanceof Error ? error.message : String(error)\n );\n }\n return {};\n }\n }\n\n /**\n * Called once before running tests\n */\n async onBegin(config: FullConfig, suite: Suite): Promise<void> {\n // Check if we're a duplicate instance (CLI-injected when already in config)\n if (config && this.isDuplicateInstance(config.reporter)) {\n if (this.config.debug) {\n this.log.debug('Reporter already configured in playwright.config, skipping duplicate instance');\n }\n return;\n }\n\n const token = this.getToken();\n\n if (!token) {\n this.printConfigurationError('Token is required but not provided', [\n 'Set environment variable: export TESTDINO_TOKEN=your-token',\n 'Add to playwright.config.ts: token: \"your-token\"',\n 'Use CLI wrapper: npx tdpw test --token your-token',\n ]);\n return;\n }\n\n // Store shard info and run start time for potential interruption handling\n if (config?.shard) {\n this.shardInfo = {\n current: config.shard.current,\n total: config.shard.total,\n };\n }\n this.runStartTime = Date.now();\n\n // Kick off async initialization without awaiting - Playwright does not await onBegin. Events will be buffered and transmitted once init completes.\n this.initPromise = this.performAsyncInit(config, suite, token);\n }\n\n /**\n * Perform all async initialization: metadata, auth, WebSocket, artifacts, run:begin\n *\n * Buffer's onFlush callback awaits this promise before transmitting events,\n * ensuring no data is sent before authentication and connection are established.\n *\n * @param config - Playwright FullConfig for metadata collection\n * @param suite - Playwright Suite for skeleton building\n * @param token - Authentication token\n * @returns true on success, false on failure\n */\n private async performAsyncInit(config: FullConfig, suite: Suite, token: string): Promise<boolean> {\n const serverUrl = this.getServerUrl();\n\n try {\n // Collect metadata before initializing streaming\n const metadata = await this.collectMetadata(config, suite);\n this.runMetadata = metadata;\n this.workerCount = config.workers;\n\n // Initialize HTTP client and authenticate\n this.httpClient = new HttpClient({ token, serverUrl });\n const auth = await this.httpClient.authenticate();\n this.sessionId = auth.sessionId;\n this.log.success('Authenticated successfully');\n if (this.config.debug) {\n this.log.debug(`Session ${this.sessionId} — reusing for WebSocket`);\n }\n\n // Initialize WebSocket client\n this.wsClient = new WebSocketClient({\n token,\n sessionId: this.sessionId ?? undefined,\n serverUrl: this.getWebSocketUrl(),\n debug: this.config.debug,\n onConnected: () => {\n this.log.debug('WebSocket connected');\n },\n onDisconnected: () => {\n this.log.debug('WebSocket disconnected');\n },\n onError: (error) => {\n // Handle quota errors from WebSocket (run:begin rejection)\n if (isQuotaError(error)) {\n // Store error and defer banner to onEnd() so it prints after test output\n if (!this.quotaExceeded) {\n this.quotaExceeded = true;\n this.initFailed = true;\n this.pendingQuotaError = error;\n }\n } else {\n // Log other WebSocket errors\n this.log.error(`WebSocket error: ${error.message}`);\n }\n },\n });\n\n // Try to connect WebSocket\n try {\n await this.wsClient.connect();\n } catch {\n this.log.warn('WebSocket connection failed, using HTTP fallback');\n this.useHttpFallback = true;\n }\n\n // Initialize artifact uploader if enabled (default: true)\n this.artifactsEnabled = this.config.artifacts !== false;\n if (this.artifactsEnabled) {\n await this.initializeArtifactUploader(token, serverUrl);\n }\n\n // Send run:begin directly (not via buffer) to guarantee it arrives before\n // any buffered test events. initPromise resolves only after this send,\n // so buffer's onFlush will transmit buffered events after run:begin.\n const beginEvent: TestRunBeginEvent = {\n type: 'run:begin',\n runId: this.runId,\n metadata,\n ciRunId: this.config.ciRunId, // Include ciRunId for shard grouping\n ...this.getEventMetadata(),\n };\n\n // Debug logging for run:begin event details\n if (this.config.debug) {\n const shardInfo = config?.shard ? `${config.shard.current}/${config.shard.total}` : 'none';\n const skeletonInfo = metadata.skeleton\n ? `${metadata.skeleton.totalTests} tests, ${metadata.skeleton.suites?.length ?? 0} suites`\n : 'not available';\n this.log.debug(\n `run:begin runId=${this.runId} ciRunId=${this.config.ciRunId ?? 'none'} shard=${shardInfo} skeleton=(${skeletonInfo})`\n );\n }\n\n await this.sendEvents([beginEvent]);\n\n // Register signal handlers for graceful shutdown on interruption\n this.registerSignalHandlers();\n\n return true;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n this.initFailed = true;\n\n // Check if this is a quota exhausted error (402)\n if (error instanceof Error && 'code' in error && isQuotaError(error)) {\n this.quotaExceeded = true;\n this.pendingQuotaError = error;\n } else if (\n errorMessage.includes('Authentication failed') ||\n errorMessage.includes('401') ||\n errorMessage.includes('Unauthorized')\n ) {\n this.printConfigurationError('Authentication failed - Invalid or expired token', [\n 'Verify your token is correct',\n 'Check if the token has expired',\n 'Generate a new token from TestDino dashboard',\n `Server URL: ${serverUrl}`,\n ]);\n } else {\n // Generic initialization error\n this.printConfigurationError(`Failed to initialize TestDino reporter: ${errorMessage}`, [\n 'Check if TestDino server is running and accessible',\n `Verify server URL is correct: ${serverUrl}`,\n 'Check network connectivity',\n 'Review server logs for details',\n ]);\n }\n\n return false;\n }\n }\n\n /**\n * Called for each test before it starts\n */\n async onTestBegin(test: TestCase, result: TestResult): Promise<void> {\n if (!this.initPromise || this.initFailed) return;\n\n const event: TestBeginEvent = {\n type: 'test:begin',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Test identification\n testId: test.id,\n title: test.title,\n titlePath: test.titlePath(),\n\n // Location information\n location: {\n file: test.location.file,\n line: test.location.line,\n column: test.location.column,\n },\n\n // Test configuration\n tags: test.tags,\n expectedStatus: test.expectedStatus,\n timeout: test.timeout,\n retries: test.retries,\n annotations: this.extractAnnotations(test.annotations),\n\n // Execution context\n retry: result.retry,\n workerIndex: result.workerIndex,\n parallelIndex: result.parallelIndex,\n repeatEachIndex: test.repeatEachIndex,\n\n // Hierarchy information\n parentSuite: this.extractParentSuite(test.parent),\n\n // Timing\n startTime: result.startTime.getTime(),\n };\n\n await this.buffer!.add(event);\n }\n\n /**\n * Called when a test step begins\n */\n async onStepBegin(test: TestCase, result: TestResult, step: TestStep): Promise<void> {\n if (!this.initPromise || this.initFailed) return;\n\n const event: TestStepBeginEvent = {\n type: 'step:begin',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Step Identification\n testId: test.id,\n stepId: `${test.id}-${step.titlePath().join('-')}`,\n title: step.title,\n titlePath: step.titlePath(),\n\n // Step Classification\n category: step.category,\n\n // Location Information\n location: step.location\n ? {\n file: step.location.file,\n line: step.location.line,\n column: step.location.column,\n }\n : undefined,\n\n // Hierarchy Information\n parentStep: step.parent ? this.extractParentStep(step.parent) : undefined,\n\n // Timing\n startTime: step.startTime.getTime(),\n\n // Retry Information\n retry: result.retry,\n\n // Worker Information\n workerIndex: result.workerIndex,\n parallelIndex: result.parallelIndex,\n\n // Annotations\n annotations: this.extractAnnotations(step.annotations),\n };\n\n await this.buffer!.add(event);\n }\n\n /**\n * Called when a test step ends\n */\n async onStepEnd(test: TestCase, result: TestResult, step: TestStep): Promise<void> {\n if (!this.initPromise || this.initFailed) return;\n\n // Derive status from error presence\n const status: 'passed' | 'failed' = step.error ? 'failed' : 'passed';\n\n const event: TestStepEndEvent = {\n type: 'step:end',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Step Identification\n testId: test.id,\n stepId: `${test.id}-${step.titlePath().join('-')}`,\n title: step.title,\n titlePath: step.titlePath(),\n\n // Timing\n duration: step.duration,\n\n // Error Information\n error: this.extractError(step.error),\n\n // Status Information\n status,\n\n // Child Steps Summary\n childSteps: this.extractChildSteps(step),\n\n // Attachments Metadata\n attachments: this.extractAttachments(step),\n\n // Annotations\n annotations: this.extractAnnotations(step.annotations),\n\n // Retry Information\n retry: result.retry,\n\n // Worker Information\n workerIndex: result.workerIndex,\n parallelIndex: result.parallelIndex,\n };\n\n await this.buffer!.add(event);\n }\n\n /**\n * Called after each test.\n * Playwright does not await onTestEnd promises—pending work is awaited in onEnd.\n */\n onTestEnd(test: TestCase, result: TestResult): void {\n if (!this.initPromise || this.initFailed) return;\n\n // Warn when fixture sends coverage data but coverage is disabled in config\n if (!this.coverageEnabled && !this.warnedCoverageDisconnect) {\n const hasCoverageAttachment = result.attachments.some((a) => a.name === 'testdino-coverage');\n if (hasCoverageAttachment) {\n this.log.warn(\n 'Coverage data detected but coverage.enabled is false — ' +\n 'set coverage: { enabled: true } to collect coverage'\n );\n this.warnedCoverageDisconnect = true;\n }\n }\n\n // Extract and merge coverage fragment (if coverage enabled)\n if (this.coverageEnabled && this.coverageMerger) {\n this.extractCoverageFromResult(result, this.coverageMerger);\n }\n\n // Track project name\n const projectName = this.getProjectName(test);\n if (projectName) {\n this.projectNames.add(projectName);\n }\n\n // Track test result counts for summary table display\n if (result.retry > 0) {\n this.testCounts.retried++;\n }\n // Only count final status on the last attempt (for summary table display)\n const isFinalAttempt = result.status === 'passed' || result.retry === test.retries;\n if (isFinalAttempt) {\n this.totalTests++;\n const outcome = test.outcome();\n if (outcome === 'flaky') {\n this.testCounts.flaky++;\n } else if (result.status === 'passed') {\n this.testCounts.passed++;\n } else if (result.status === 'failed') {\n this.testCounts.failed++;\n } else if (result.status === 'skipped') {\n this.testCounts.skipped++;\n } else if (result.status === 'timedOut') {\n this.testCounts.timedOut++;\n } else if (result.status === 'interrupted') {\n this.testCounts.interrupted++;\n }\n }\n\n // Create and track the async work - Playwright does not await onTestEnd\n const workPromise = this.processTestEnd(test, result);\n this.pendingTestEndPromises.add(workPromise);\n\n // Remove from tracking when complete (success or failure)\n workPromise.finally(() => {\n this.pendingTestEndPromises.delete(workPromise);\n });\n }\n\n /**\n * Process test end event asynchronously\n * Uploads attachments and adds test:end event to buffer\n */\n private async processTestEnd(test: TestCase, result: TestResult): Promise<void> {\n try {\n // Upload attachments and get Azure URLs (if upload enabled)\n const attachmentsWithUrls = await this.uploadAttachments(result.attachments, test.id);\n\n const event: TestEndEvent = {\n type: 'test:end',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Test Identification\n testId: test.id,\n\n // Status Information\n status: result.status as 'passed' | 'failed' | 'skipped' | 'timedOut' | 'interrupted',\n outcome: test.outcome(),\n\n // Timing\n duration: result.duration,\n\n // Execution Context\n retry: result.retry,\n\n // Worker Information\n workerIndex: result.workerIndex,\n parallelIndex: result.parallelIndex,\n\n // Test Metadata\n annotations: this.extractAnnotations(result.annotations),\n\n // Error Information\n errors: result.errors\n .map((e) => this.extractError(e))\n .filter((e): e is NonNullable<typeof e> => e !== undefined),\n\n // Step Summary\n steps: this.extractTestStepsSummary(result),\n\n // Attachments Metadata (with Azure URLs when uploaded)\n attachments: attachmentsWithUrls,\n\n // Console Output\n stdout: result.stdout.length > 0 ? this.extractConsoleOutput(result.stdout) : undefined,\n stderr: result.stderr.length > 0 ? this.extractConsoleOutput(result.stderr) : undefined,\n };\n\n await this.buffer!.add(event);\n } catch (error) {\n // Log error but don't throw - one test failure shouldn't break others\n this.log.error(`Failed to process test:end event: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n /**\n * Called after all tests complete\n */\n async onEnd(result: FullResult): Promise<{ status?: FullResult['status'] } | void> {\n // If quota exceeded, skip event transmission but still cleanup\n if (this.quotaExceeded) {\n // Await pending test:end promises for graceful cleanup (even though we won't send events)\n if (this.pendingTestEndPromises.size > 0) {\n await Promise.allSettled(Array.from(this.pendingTestEndPromises));\n }\n // Print quota banner after all test output is done\n if (this.pendingQuotaError) {\n this.printQuotaError(this.pendingQuotaError);\n }\n this.log.success('Tests completed (quota limit reached; not streamed to TestDino)');\n this.wsClient?.close();\n this.removeSignalHandlers();\n return;\n }\n\n // If init was never started (no token, duplicate), nothing to do\n if (!this.initPromise) return;\n\n // Ensure initialization completes before flushing events\n const success = await this.initPromise;\n if (!success) {\n // Init failed - discard any buffered events and pending promises, cleanup\n this.buffer?.clear();\n this.pendingTestEndPromises.clear();\n this.wsClient?.close();\n this.removeSignalHandlers();\n return;\n }\n\n // Await completion of all pending test:end events before flushing final events\n if (this.pendingTestEndPromises.size > 0) {\n this.log.debug(`Waiting for ${this.pendingTestEndPromises.size} pending test:end events...`);\n // Use Promise.allSettled for error isolation - one failed test shouldn't block others\n await Promise.allSettled(Array.from(this.pendingTestEndPromises));\n }\n\n // Send coverage data before run:end (if coverage collected)\n if (this.coverageEnabled && this.coverageMerger?.hasCoverage) {\n try {\n // Build coverage event (includes summary, files, and compact counts for sharded runs)\n const coverageEvent = this.buildCoverageEvent(this.coverageMerger);\n await this.buffer!.add(coverageEvent);\n\n // Store for local console summary table\n this.lastCoverageEvent = coverageEvent;\n\n // Check coverage thresholds\n const thresholdFailures = this.checkCoverageThresholds(coverageEvent.summary);\n if (thresholdFailures.length > 0) {\n this.coverageThresholdFailed = true;\n }\n } catch (error) {\n // Log but don't fail the run\n this.log.warn(`Failed to build coverage event: ${error instanceof Error ? error.message : String(error)}`);\n }\n\n // Generate local HTML report if configured (separate try-catch to avoid\n // misleading \"Failed to build coverage event\" when only report generation fails)\n if (this.coverageMerger?.hasCoverage) {\n try {\n const outputDir = './coverage';\n const reportPath = await generateIstanbulHtmlReport(this.coverageMerger, {\n outputDir,\n });\n this.log.info(`Coverage Report: ${reportPath}`);\n } catch (error) {\n this.log.warn(\n `Failed to generate local HTML report: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n } else if (this.coverageEnabled && !this.coverageMerger?.hasCoverage) {\n this.log.warn(\n 'Coverage enabled but no data was collected. Ensure your app is instrumented ' +\n 'with Istanbul (babel-plugin-istanbul) and tests use { test } from @testdino/playwright'\n );\n }\n\n const event: TestRunEndEvent = {\n type: 'run:end',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Run Status\n status: result.status as 'passed' | 'failed' | 'timedout' | 'interrupted',\n\n // Timing\n duration: result.duration,\n startTime: result.startTime.getTime(),\n\n // Shard information\n shard: this.shardInfo,\n };\n\n // Flush any remaining buffered events first\n try {\n await this.buffer!.flush();\n } catch (error) {\n this.log.error(`Failed to flush buffered events: ${error}`);\n }\n\n // Send run:end and wait for server ACK to ensure delivery\n // This prevents data loss when shards execute very fast\n let delivered = false;\n try {\n if (this.wsClient?.isConnected()) {\n await this.wsClient.sendAndWaitForAck(event);\n this.log.success('All events delivered (server acknowledged)');\n delivered = true;\n } else if (this.httpClient) {\n // HTTP mode (no ACK confirmation available)\n await this.httpClient.sendEvents([event]);\n this.log.success('All events sent');\n delivered = true;\n } else {\n this.log.warn('No connection available to send run:end event');\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n if (errorMessage.includes('ACK timeout') || errorMessage.includes('Connection closed')) {\n // ACK timeout or connection lost - retry via HTTP as fallback\n if (this.httpClient && !delivered) {\n try {\n this.log.warn('WebSocket ACK timeout, retrying via HTTP...');\n await this.httpClient.sendEvents([event]);\n this.log.success('All events sent (HTTP fallback)');\n delivered = true;\n } catch (httpError) {\n const isQuota =\n isQuotaError(httpError) ||\n (httpError instanceof Error &&\n 'code' in httpError &&\n ((httpError as Record<string, unknown>).code === 'QUOTA_EXCEEDED' ||\n (httpError as Record<string, unknown>).code === 'QUOTA_EXHAUSTED'));\n if (isQuota) {\n this.quotaExceeded = true;\n this.pendingQuotaError = httpError;\n this.printQuotaError(httpError);\n } else {\n const httpErrorMessage = httpError instanceof Error ? httpError.message : String(httpError);\n this.log.error(`HTTP fallback also failed: ${httpErrorMessage}`);\n }\n }\n } else if (!delivered) {\n this.log.warn('Server did not acknowledge run:end in time, events may be pending');\n }\n } else {\n this.log.error(`Failed to send run:end event: ${errorMessage}`);\n }\n }\n\n // Print comprehensive run summary\n const summaryData: RunSummaryData = {\n runId: this.runId,\n runMetadata: this.runMetadata,\n testCounts: this.testCounts,\n totalTests: this.totalTests,\n workerCount: this.workerCount,\n projectNames: this.projectNames,\n shardInfo: this.shardInfo,\n useHttpFallback: this.useHttpFallback,\n lastCoverageEvent: this.lastCoverageEvent,\n };\n await this.log.printRunSummary(result, delivered, summaryData);\n\n // Check if coverage thresholds were not met\n if (this.coverageThresholdFailed && this.lastCoverageEvent) {\n const failures = this.checkCoverageThresholds(this.lastCoverageEvent.summary);\n this.log.error('Coverage thresholds not met:');\n for (const msg of failures) {\n this.log.error(` ${msg}`);\n }\n this.wsClient?.close();\n this.removeSignalHandlers();\n return { status: 'failed' };\n }\n\n // Close connections and remove signal handlers\n this.wsClient?.close();\n this.removeSignalHandlers();\n }\n\n /**\n * Called on global errors\n */\n async onError(error: TestError): Promise<void> {\n if (!this.initPromise || this.initFailed) return;\n\n const event: TestRunErrorEvent = {\n type: 'run:error',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Error Information (with cause support)\n error: this.extractGlobalError(error),\n };\n\n await this.buffer!.add(event);\n }\n\n /**\n * Called when standard output is produced in worker process\n */\n async onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult): Promise<void> {\n if (!this.initPromise || this.initFailed) return;\n\n const { text, truncated } = this.truncateChunk(chunk);\n\n const event: TestConsoleOutEvent = {\n type: 'console:out',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Console Output\n text,\n\n // Test Association (optional)\n testId: test?.id,\n retry: result?.retry,\n\n // Truncation Indicator\n truncated,\n };\n\n await this.buffer!.add(event);\n }\n\n /**\n * Called when standard error is produced in worker process\n */\n async onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult): Promise<void> {\n if (!this.initPromise || this.initFailed) return;\n\n const { text, truncated } = this.truncateChunk(chunk);\n\n const event: TestConsoleErrEvent = {\n type: 'console:err',\n runId: this.runId,\n ...this.getEventMetadata(),\n\n // Console Error Output\n text,\n\n // Test Association (optional)\n testId: test?.id,\n retry: result?.retry,\n\n // Truncation Indicator\n truncated,\n };\n\n await this.buffer!.add(event);\n }\n\n /**\n * Indicates whether this reporter outputs to stdout/stderr\n * Returns false to allow Playwright to add its own terminal output\n */\n printsToStdio(): boolean {\n return false;\n }\n\n /**\n * Send events via WebSocket or HTTP fallback\n */\n private async sendEvents(events: TestEvent[]): Promise<void> {\n if (events.length === 0) return;\n\n if (this.config.debug) {\n for (const event of events) {\n if (event.type === 'test:begin') {\n const testBeginEvent = event as TestBeginEvent;\n this.log.debug(\n `Sending event type=${event.type} sequence=${event.sequence} runId=${event.runId} testId=${testBeginEvent.testId} retry=${testBeginEvent.retry} parallelIndex=${testBeginEvent.parallelIndex} title=${testBeginEvent.title}`\n );\n } else if (event.type === 'test:end') {\n const testEndEvent = event as TestEndEvent;\n this.log.debug(\n `Sending event type=${event.type} sequence=${event.sequence} runId=${event.runId} testId=${testEndEvent.testId} retry=${testEndEvent.retry} parallelIndex=${testEndEvent.parallelIndex}`\n );\n } else {\n this.log.debug(`Sending event type=${event.type} sequence=${event.sequence} runId=${event.runId}`);\n }\n }\n }\n\n // Try WebSocket first if available\n if (!this.useHttpFallback && this.wsClient?.isConnected()) {\n try {\n await this.wsClient.sendBatch(events);\n return;\n } catch {\n this.log.warn('WebSocket send failed, switching to HTTP fallback');\n this.useHttpFallback = true;\n }\n }\n\n // Use HTTP fallback\n if (this.httpClient) {\n try {\n await this.httpClient.sendEvents(events);\n } catch (error) {\n this.log.error(`Failed to send events via HTTP: ${error}`);\n throw error;\n }\n }\n }\n\n /**\n * Get token from config or environment\n */\n private getToken(): string | undefined {\n return this.config.token || process.env.TESTDINO_TOKEN;\n }\n\n /**\n * Get server URL from config or environment\n */\n private getServerUrl(): string {\n const baseUrl = this.config.serverUrl || process.env.TESTDINO_SERVER_URL || 'https://api.testdino.com';\n\n // Ensure the URL ends with /api/reporter for CLI endpoints\n return baseUrl.endsWith('/api/reporter') ? baseUrl : `${baseUrl}/api/reporter`;\n }\n\n private getWebSocketUrl(): string {\n const baseUrl = this.config.serverUrl || process.env.TESTDINO_SERVER_URL || 'https://api.testdino.com';\n\n // Remove /api/reporter suffix if present for WebSocket URL\n const wsBaseUrl = baseUrl.replace('/api/reporter', '');\n return wsBaseUrl.replace('http', 'ws');\n }\n\n /**\n * Collect metadata for the test run\n */\n private async collectMetadata(playwrightConfig: FullConfig, playwrightSuite: Suite): Promise<RunMetadata> {\n try {\n const metadataCollector = createMetadataCollector(playwrightConfig, playwrightSuite);\n const result = await metadataCollector.collectAll();\n\n if (result.failureCount > 0) {\n this.log.warn(`${result.failureCount}/${result.results.length} metadata collectors failed`);\n }\n\n // Build skeleton from suite\n const skeleton = metadataCollector.buildSkeleton(playwrightSuite);\n\n // Add skeleton to metadata\n return {\n ...result.metadata,\n skeleton,\n };\n } catch (error) {\n this.log.warn(`Metadata collection failed: ${error instanceof Error ? error.message : String(error)}`);\n return {}; // Return empty metadata as fallback\n }\n }\n\n /**\n * Get next sequence number and current timestamp\n */\n private getEventMetadata() {\n return {\n timestamp: Date.now(),\n sequence: ++this.sequenceNumber,\n };\n }\n\n /**\n * Extract annotations with optional location information\n */\n private extractAnnotations(annotations: Array<Annotation> | undefined): Array<Annotation> {\n if (!annotations || annotations.length === 0) {\n return [];\n }\n\n return annotations.map((a) => ({\n type: a.type,\n description: a.description,\n }));\n }\n\n /**\n * Extract parent suite information for skeleton mapping\n */\n private extractParentSuite(parent: Suite): TestBeginEvent['parentSuite'] {\n return {\n title: parent.title,\n type: parent.type,\n location: parent.location\n ? {\n file: parent.location.file,\n line: parent.location.line,\n column: parent.location.column,\n }\n : undefined,\n };\n }\n\n /**\n * Extract parent step information for step hierarchy mapping\n */\n private extractParentStep(parent: TestStep): TestStepBeginEvent['parentStep'] {\n return {\n title: parent.title,\n category: parent.category,\n location: parent.location\n ? {\n file: parent.location.file,\n line: parent.location.line,\n column: parent.location.column,\n }\n : undefined,\n };\n }\n\n /**\n * Extract child steps summary for step:end event\n */\n private extractChildSteps(step: TestStep): TestStepEndEvent['childSteps'] {\n return {\n count: step.steps.length,\n steps: step.steps.map((child) => ({\n title: child.title,\n status: child.error ? 'failed' : 'passed',\n })),\n };\n }\n\n /**\n * Extract error details from TestError (shared by step:end, test:end, and error events)\n */\n private extractError(error: TestError | undefined): TestStepEndEvent['error'] {\n if (!error) return undefined;\n\n return {\n message: error.message || String(error),\n stack: error.stack,\n snippet: error.snippet,\n value: error.value,\n location: error.location\n ? {\n file: error.location.file,\n line: error.location.line,\n column: error.location.column,\n }\n : undefined,\n };\n }\n\n /**\n * Extract global error details with cause support (for error events)\n */\n private extractGlobalError(error: TestError): TestRunErrorEvent['error'] {\n return {\n message: error.message,\n stack: error.stack,\n snippet: error.snippet,\n value: error.value,\n location: error.location\n ? {\n file: error.location.file,\n line: error.location.line,\n column: error.location.column,\n }\n : undefined,\n // Handle nested error cause (v1.49+)\n cause: error.cause\n ? {\n message: error.cause.message,\n stack: error.cause.stack,\n snippet: error.cause.snippet,\n value: error.cause.value,\n location: error.cause.location\n ? {\n file: error.cause.location.file,\n line: error.cause.location.line,\n column: error.cause.location.column,\n }\n : undefined,\n }\n : undefined,\n };\n }\n\n /**\n * Print a prominent configuration error banner\n */\n private printConfigurationError(message: string, solutions: string[]): void {\n const border = '═'.repeat(70);\n console.error('');\n console.error(border);\n console.error(' ❌ TestDino Reporter Configuration Error');\n console.error(border);\n console.error(` ${message}`);\n console.error('');\n console.error(' Solutions:');\n solutions.forEach((solution, index) => {\n console.error(` ${index + 1}. ${solution}`);\n });\n console.error(border);\n console.error('');\n }\n\n /**\n * Print quota error with plan details and upgrade information\n * @param error - Quota error (QUOTA_EXHAUSTED or QUOTA_EXCEEDED)\n */\n private printQuotaError(error: unknown): void {\n const border = '═'.repeat(70);\n const errorData = error as Record<string, unknown>;\n const details = errorData.details as Record<string, unknown>;\n const planName = (details?.planName as string) || 'Unknown';\n const resetDate = details?.resetDate;\n const formattedResetDate = resetDate\n ? new Date(resetDate as string).toLocaleDateString('en-US', {\n day: '2-digit',\n month: 'short',\n year: 'numeric',\n })\n : undefined;\n\n console.error('');\n console.error(border);\n\n if (errorData.code === 'QUOTA_EXCEEDED') {\n const exceeded = details as {\n total?: number;\n used?: number;\n remaining?: number;\n totalTests?: number;\n projectName?: string;\n projectLimit?: number;\n projectUsed?: number;\n projectBorrowed?: number;\n };\n\n // Always calculate remaining from total - used (don't trust server's remaining field)\n const orgRemaining = (exceeded.total ?? 0) - (exceeded.used ?? 0);\n const effectiveLimit = (exceeded.projectLimit ?? 0) + (exceeded.projectBorrowed ?? 0);\n\n console.error(' ❌ TestDino Project Execution Limit Reached');\n console.error(border);\n console.error('');\n console.error(' The test case limit allocated to this project has been exceeded.');\n console.error('');\n console.error(' Project Usage:');\n if (exceeded.projectName) {\n console.error(` Project: ${exceeded.projectName}`);\n }\n if (exceeded.projectLimit != null) {\n console.error(\n ` Limit: ${effectiveLimit} test cases${exceeded.projectBorrowed ? ` (${exceeded.projectLimit} allocated + ${exceeded.projectBorrowed} borrowed)` : ''}`\n );\n }\n if (exceeded.projectUsed != null) {\n console.error(` Used: ${exceeded.projectUsed}`);\n }\n console.error(` This run: ${exceeded.totalTests || 'Unknown'} test cases`);\n console.error(' Status: Project quota exhausted');\n console.error('');\n console.error(' Organization Usage:');\n console.error(` Plan: ${planName} (${exceeded.total || 'Unknown'} test cases / month)`);\n console.error(` Used: ${exceeded.used || 'Unknown'}`);\n console.error(` Remaining: ${orgRemaining}`);\n if (orgRemaining > 0) {\n console.error('');\n console.error(' Note:');\n console.error(` Your organization still has ${orgRemaining} test cases available,`);\n console.error(' but they are not allocated to this project.');\n }\n console.error('');\n console.error(' Solutions:');\n console.error(' 1. Allocate more test case quota to this project in');\n console.error(' Settings → Billing & Usage → Test Limits');\n console.error(' 2. Enable Auto Allocation to automatically distribute');\n console.error(' remaining organization quota');\n } else if (errorData.code === 'QUOTA_EXHAUSTED') {\n console.error(' ❌ TestDino Organization Execution Limit Reached');\n console.error(border);\n console.error('');\n console.error(' Your organization has exhausted its monthly test case limit.');\n console.error('');\n console.error(' Organization Usage:');\n console.error(` Plan: ${planName} (${details.totalLimit || 'Unknown'} test cases / month)`);\n console.error(` Used: ${details.used || 'Unknown'}`);\n console.error(' Remaining: 0');\n console.error('');\n console.error(' Solutions:');\n console.error(' 1. Upgrade your plan to increase monthly test case limit');\n }\n\n if (formattedResetDate) {\n console.error('');\n console.error(' Monthly Reset:');\n console.error(` ${formattedResetDate}`);\n }\n\n console.error('');\n console.error(' Docs: https://docs.testdino.com/platform/billing-and-usage/test-limits');\n console.error(' Pricing: https://testdino.com/pricing');\n console.error(border);\n console.error('');\n }\n\n /**\n * Truncate console chunk to max size and convert Buffer to string\n */\n private truncateChunk(chunk: string | Buffer): { text: string; truncated?: boolean } {\n // Convert Buffer to string (UTF-8)\n let convertedText: string;\n if (Buffer.isBuffer(chunk)) {\n convertedText = chunk.toString('utf-8');\n } else {\n convertedText = chunk;\n }\n\n // Check if truncation is needed\n if (convertedText.length > MAX_CONSOLE_CHUNK_SIZE) {\n return {\n text: convertedText.substring(0, MAX_CONSOLE_CHUNK_SIZE) + '\\n[truncated]',\n truncated: true,\n };\n }\n\n return { text: convertedText };\n }\n\n /**\n * Extract attachment metadata for step:end event\n */\n private extractAttachments(step: TestStep): TestStepEndEvent['attachments'] {\n return step.attachments.map((a) => ({\n name: a.name,\n contentType: a.contentType,\n path: a.path, // undefined for in-memory attachments\n }));\n }\n\n /**\n * Extract steps summary for test:end event\n */\n private extractTestStepsSummary(result: TestResult): TestEndEvent['steps'] {\n return {\n total: result.steps.length,\n passed: result.steps.filter((s) => !s.error).length,\n failed: result.steps.filter((s) => s.error).length,\n };\n }\n\n /**\n * Extract console output for test:end event\n */\n private extractConsoleOutput(output: Array<string | Buffer>): string[] {\n return output.map((item) => (typeof item === 'string' ? item : item.toString()));\n }\n\n /**\n * Check if this is a duplicate instance of TestdinoReporter\n * This happens when the CLI injects our reporter via --reporter flag,\n * but the user already has TestdinoReporter configured in playwright.config\n *\n * @param reporters - The resolved reporters array from FullConfig\n * @returns true if there are multiple TestdinoReporter instances, false otherwise\n */\n private isDuplicateInstance(reporters: unknown[]): boolean {\n const count = this.countTestdinoReporters(reporters);\n\n // If count > 1, it means we have both:\n // - One from playwright.config (user's manual config)\n // - One from CLI injection (our --reporter flag)\n // The config-based instance should handle reporting, so we exit\n return count > 1;\n }\n\n /**\n * Count how many TestdinoReporter instances are in the reporters array\n *\n * @param reporters - The resolved reporters array from FullConfig\n * @returns Number of TestdinoReporter instances found\n */\n private countTestdinoReporters(reporters: unknown): number {\n // Guard against undefined or non-array reporters\n if (!reporters || !Array.isArray(reporters)) {\n return 0;\n }\n\n let count = 0;\n\n for (const reporter of reporters) {\n // Reporters can be in multiple formats:\n // - ['@testdino/playwright'] - string in array\n // - ['@testdino/playwright', options] - string with options\n // - Class instance (when instantiated with new)\n\n if (Array.isArray(reporter) && reporter.length > 0) {\n const reporterName = reporter[0];\n if (this.isTestdinoReporter(reporterName)) {\n count++;\n }\n }\n }\n\n return count;\n }\n\n /**\n * Check if a reporter name/path matches TestdinoReporter\n *\n * @param value - Reporter name, path, or class reference\n * @returns true if this is our reporter, false otherwise\n */\n private isTestdinoReporter(value: unknown): boolean {\n if (typeof value !== 'string') {\n return false;\n }\n\n // Check various forms our reporter might appear as:\n // - '@testdino/playwright' (npm package name)\n // - 'TestdinoReporter' (class name)\n // - Path ending with 'testdino-playwright'\n return (\n value.includes('@testdino/playwright') ||\n value.includes('TestdinoReporter') ||\n value.endsWith('testdino-playwright')\n );\n }\n\n /**\n * Register signal handlers for graceful shutdown on interruption\n */\n private registerSignalHandlers(): void {\n // Use a wrapper that properly handles async operations\n // Node.js signal handlers don't wait for async operations,\n // so we need to prevent immediate exit and handle manually\n this.sigintHandler = () => {\n // Remove handler immediately to prevent duplicate signals during shutdown\n if (this.sigintHandler) {\n process.removeListener('SIGINT', this.sigintHandler);\n }\n if (this.sigtermHandler) {\n process.removeListener('SIGTERM', this.sigtermHandler);\n }\n\n this.handleInterruption('SIGINT', 130);\n };\n\n this.sigtermHandler = () => {\n // Remove handler immediately to prevent duplicate signals during shutdown\n if (this.sigintHandler) {\n process.removeListener('SIGINT', this.sigintHandler);\n }\n if (this.sigtermHandler) {\n process.removeListener('SIGTERM', this.sigtermHandler);\n }\n\n this.handleInterruption('SIGTERM', 143);\n };\n\n process.on('SIGINT', this.sigintHandler);\n process.on('SIGTERM', this.sigtermHandler);\n }\n\n /**\n * Remove signal handlers (called on normal completion or after handling interruption)\n */\n private removeSignalHandlers(): void {\n if (this.sigintHandler) {\n process.removeListener('SIGINT', this.sigintHandler);\n }\n if (this.sigtermHandler) {\n process.removeListener('SIGTERM', this.sigtermHandler);\n }\n }\n\n /**\n * Handle process interruption by sending run:end event with interrupted status\n * @param signal - The signal that triggered the interruption (SIGINT or SIGTERM)\n * @param exitCode - The exit code to use when exiting\n */\n private handleInterruption(signal: string, exitCode: number): void {\n // Prevent duplicate handling\n if (this.isShuttingDown) return;\n this.isShuttingDown = true;\n\n this.log.warn(`Received ${signal}, sending interruption event...`);\n\n if (!this.initPromise) {\n // Init never started, just exit\n process.exit(exitCode);\n }\n\n // Wait up to 2000ms for pending test:end events to finish (matches send timeout)\n // This allows time for in-progress uploads to complete during graceful shutdown\n const waitForPending = async () => {\n if (this.pendingTestEndPromises.size > 0) {\n try {\n await Promise.race([\n Promise.allSettled(Array.from(this.pendingTestEndPromises)),\n this.timeoutPromise(2000, 'Pending events timeout'),\n ]);\n } catch {\n // Timeout expected, continue with shutdown\n }\n }\n };\n\n // Create interruption event\n const event: TestRunEndEvent = {\n type: 'run:end',\n runId: this.runId,\n ...this.getEventMetadata(),\n status: 'interrupted',\n duration: this.runStartTime ? Date.now() - this.runStartTime : 0,\n startTime: this.runStartTime ?? Date.now(),\n shard: this.shardInfo,\n };\n\n // Keep event loop alive with interval - CRITICAL for ensuring async operations complete\n const keepAlive = setInterval(() => {\n // Empty interval to keep process alive\n }, 100);\n\n // Hard timeout to force exit if send hangs\n const forceExitTimer = setTimeout(() => {\n clearInterval(keepAlive);\n this.log.error('Force exit - send timeout exceeded');\n this.wsClient?.close();\n process.exit(exitCode);\n }, 3000); // 3 second hard limit\n\n // Send interruption event directly (bypass buffer to avoid flush delays)\n const sendAndExit = async () => {\n try {\n // Wait briefly for pending test:end events (best effort)\n await waitForPending();\n\n // Send directly without buffering for speed\n await Promise.race([this.sendInterruptionEvent(event), this.timeoutPromise(2500, 'Send timeout')]);\n\n this.log.success('Interruption event sent');\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n this.log.error(`Failed to send interruption event: ${errorMsg}`);\n } finally {\n // Clean up\n clearTimeout(forceExitTimer);\n clearInterval(keepAlive);\n this.wsClient?.close();\n process.exit(exitCode);\n }\n };\n\n // Execute\n sendAndExit().catch(() => {\n clearTimeout(forceExitTimer);\n clearInterval(keepAlive);\n process.exit(exitCode);\n });\n }\n\n /**\n * Send interruption event directly without buffering\n * Uses optimized path for speed during shutdown\n */\n private async sendInterruptionEvent(event: TestRunEndEvent): Promise<void> {\n // Try WebSocket first if connected\n if (!this.useHttpFallback && this.wsClient?.isConnected()) {\n try {\n await this.wsClient.send(event);\n return;\n } catch {\n // Fall through to HTTP\n this.log.warn('WebSocket send failed, trying HTTP');\n }\n }\n\n // Use HTTP with reduced retries for speed\n if (this.httpClient) {\n try {\n // Single attempt, no retries for interruption (speed is critical)\n await this.httpClient.sendEvent(event);\n } catch (error) {\n throw new Error(`HTTP send failed: ${error}`);\n }\n } else {\n throw new Error('No client available');\n }\n }\n\n /**\n * Create a promise that rejects after a timeout\n * @param ms - Timeout in milliseconds\n * @param message - Error message for timeout\n */\n private timeoutPromise(ms: number, message: string): Promise<never> {\n return new Promise((_, reject) => setTimeout(() => reject(new Error(message)), ms));\n }\n\n /**\n * Initialize artifact uploader by requesting SAS token\n * Gracefully handles failures - uploads disabled if SAS token request fails\n */\n private async initializeArtifactUploader(token: string, serverUrl: string): Promise<void> {\n try {\n // Get base server URL (without /api/reporter)\n const baseServerUrl = this.getBaseServerUrl(serverUrl);\n\n const sasTokenClient = new SASTokenClient({\n token,\n serverUrl: baseServerUrl,\n });\n\n const sasToken = await sasTokenClient.requestToken();\n\n this.artifactUploader = new ArtifactUploader(sasToken, {\n debug: this.config.debug,\n });\n\n this.log.debug('Artifact uploads enabled');\n } catch (error) {\n // Graceful degradation - disable uploads but continue\n this.log.warn(`Artifact uploads disabled - ${error instanceof Error ? error.message : String(error)}`);\n this.artifactsEnabled = false;\n this.artifactUploader = null;\n }\n }\n\n /**\n * Upload attachments and return with Azure URLs\n * If uploads disabled or failed, returns attachments with local paths\n */\n private async uploadAttachments(\n attachments: Array<{ name: string; contentType: string; path?: string; body?: Buffer }>,\n testId: string\n ): Promise<Array<{ name: string; contentType: string; path?: string }>> {\n // If uploads not enabled or no uploader, return original attachments\n if (!this.artifactsEnabled || !this.artifactUploader) {\n return attachments.map((a) => ({\n name: a.name,\n contentType: a.contentType,\n path: a.path,\n }));\n }\n\n // Filter to file-based attachments only (with path)\n const fileAttachments: AttachmentForUpload[] = attachments\n .filter((a): a is { name: string; contentType: string; path: string } => !!a.path)\n .map((a) => ({\n name: a.name,\n contentType: a.contentType,\n path: a.path,\n }));\n\n // If no file attachments, return original\n if (fileAttachments.length === 0) {\n return attachments.map((a) => ({\n name: a.name,\n contentType: a.contentType,\n path: a.path,\n }));\n }\n\n // Upload files in parallel\n const uploadResults = await this.artifactUploader.uploadAll(fileAttachments, testId);\n\n // Create a map of upload results by name\n const uploadMap = new Map<string, UploadResult>();\n for (const result of uploadResults) {\n uploadMap.set(result.name, result);\n }\n\n // Map attachments with Azure URLs where upload succeeded\n return attachments.map((a) => {\n const uploadResult = uploadMap.get(a.name);\n if (uploadResult?.success && uploadResult.uploadUrl) {\n // Replace local path with Azure URL\n return {\n name: a.name,\n contentType: a.contentType,\n path: uploadResult.uploadUrl,\n };\n }\n // Keep original path if upload failed or not applicable\n return {\n name: a.name,\n contentType: a.contentType,\n path: a.path,\n };\n });\n }\n\n /**\n * Get base server URL without /api/reporter suffix\n */\n private getBaseServerUrl(serverUrl: string): string {\n // Remove /api/reporter suffix if present\n return serverUrl.replace(/\\/api\\/reporter$/, '');\n }\n\n /**\n * Walk up the suite hierarchy to find the project name for a test.\n */\n private getProjectName(test: TestCase): string | undefined {\n let suite: Suite | undefined = test.parent;\n while (suite) {\n const project = suite.project();\n if (project) {\n return project.name || project.use?.defaultBrowserType || 'default';\n }\n suite = suite.parent;\n }\n return undefined;\n }\n\n /**\n * Extract coverage fragment from test result attachments and merge incrementally.\n * The fixture attaches coverage as an in-memory JSON attachment named 'testdino-coverage'.\n */\n private extractCoverageFromResult(result: TestResult, merger: CoverageMerger): void {\n const coverageAttachment = result.attachments.find((a) => a.name === 'testdino-coverage');\n\n if (!coverageAttachment?.body) return;\n\n try {\n const fragment: CoverageFragment = JSON.parse(coverageAttachment.body.toString());\n merger.addFragment(fragment);\n } catch (error) {\n this.log.debug(\n `Malformed coverage attachment, skipping: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n /**\n * Build a coverage:data event from the merged coverage data.\n *\n * Non-sharded runs: summary + per-file metrics only (small payload).\n * Sharded runs: also includes compact hit counts for server-side cross-shard merging.\n */\n private buildCoverageEvent(merger: CoverageMerger): CoverageDataEvent {\n const rootDir = this.runMetadata?.playwright?.rootDir || process.cwd();\n const summary = merger.computeSummary();\n const files = merger.computeFileCoverage(rootDir);\n const isSharded = !!this.shardInfo;\n\n if (files.length > COVERAGE_FILE_COUNT_WARNING) {\n this.log.warn(\n `Coverage includes ${files.length} files — consider using coverage.include/exclude to reduce scope`\n );\n }\n\n const event: CoverageDataEvent = {\n type: 'coverage:data',\n runId: this.runId,\n ...this.getEventMetadata(),\n summary,\n files,\n metadata: {\n instrumentationType: 'istanbul',\n fileCount: files.length,\n sharded: isSharded,\n },\n };\n\n // For sharded runs, include compact hit counts for server-side merging\n if (isSharded) {\n const coverageMapJSON = merger.toJSON();\n event.compactCounts = extractCompactCounts(coverageMapJSON, rootDir);\n event.shard = this.shardInfo;\n }\n\n return event;\n }\n\n /**\n * Check coverage against configured thresholds.\n * Returns an array of failure messages (empty if all thresholds pass).\n */\n private checkCoverageThresholds(summary: import('../types/index.js').CoverageSummary): string[] {\n const thresholds = this.config.coverage?.thresholds;\n if (!thresholds) return [];\n\n const failures: string[] = [];\n const check = (name: string, actual: number, threshold: number | undefined) => {\n if (threshold !== undefined && actual < threshold) {\n failures.push(`${name}: ${actual.toFixed(2)}% < ${threshold}%`);\n }\n };\n\n check('Statements', summary.statements.pct, thresholds.statements);\n check('Branches', summary.branches.pct, thresholds.branches);\n check('Functions', summary.functions.pct, thresholds.functions);\n check('Lines', summary.lines.pct, thresholds.lines);\n\n return failures;\n }\n}\n","/**\n * WebSocket client for real-time event streaming\n */\n\nimport WebSocket from 'ws';\nimport type { TestEvent, NackMessage } from '../types/index.js';\nimport { isServerError, QuotaExceededError, QuotaExhaustedError } from '../reporter/errors.js';\n\nconst HANDSHAKE_TIMEOUT_MS = 10_000;\n\nexport interface WebSocketClientOptions {\n token: string;\n serverUrl: string;\n sessionId?: string;\n maxRetries?: number;\n retryDelay?: number;\n debug?: boolean;\n onConnected?: () => void;\n onDisconnected?: () => void;\n onError?: (error: Error) => void;\n}\n\n// ACK timeout for waiting on server acknowledgment\nconst DEFAULT_ACK_TIMEOUT_MS = 5_000;\n\n// Pending ACK tracking\ninterface PendingAck {\n resolve: () => void;\n reject: (error: Error) => void;\n timer: NodeJS.Timeout;\n}\n\nexport class WebSocketClient {\n private ws: WebSocket | null = null;\n private options: Required<WebSocketClientOptions>;\n private reconnectAttempts = 0;\n private reconnectTimer: NodeJS.Timeout | null = null;\n private isConnecting = false;\n private isClosed = false;\n private pingInterval: NodeJS.Timeout | null = null;\n private pendingAcks: Map<number, PendingAck> = new Map();\n private ackTimeout: number = DEFAULT_ACK_TIMEOUT_MS;\n private debug: boolean;\n\n constructor(options: WebSocketClientOptions) {\n this.debug = options.debug ?? false;\n this.options = {\n sessionId: '',\n maxRetries: 5,\n retryDelay: 1000,\n debug: false,\n onConnected: () => {},\n onDisconnected: () => {},\n onError: () => {},\n ...options,\n };\n }\n\n /**\n * Establish WebSocket connection.\n * Resolves only after the server sends its 'connected' handshake message,\n * guaranteeing the server's message handler is registered before events are sent.\n * Passes sessionId from HTTP auth so the server reuses the existing session.\n */\n async connect(): Promise<void> {\n if (this.isConnecting || this.ws?.readyState === WebSocket.OPEN) {\n return;\n }\n\n this.isConnecting = true;\n\n return new Promise((resolve, reject) => {\n try {\n // Include sessionId in URL if available from prior HTTP auth\n let wsUrl = `${this.options.serverUrl}/stream?token=${this.options.token}`;\n if (this.options.sessionId) {\n wsUrl += `&sessionId=${this.options.sessionId}`;\n }\n this.ws = new WebSocket(wsUrl);\n\n // Track whether we've received the server's 'connected' handshake\n let serverReady = false;\n\n const handshakeTimeout = setTimeout(() => {\n if (!serverReady) {\n // Server didn't send 'connected' in time — resolve anyway to avoid blocking\n // the test run indefinitely. The early message buffer on the server will\n // catch any messages sent before the handler is ready.\n if (this.debug) {\n console.warn(\n `⚠️ TestDino: WebSocket handshake timeout — server did not send 'connected' within ${HANDSHAKE_TIMEOUT_MS}ms. Resolving anyway.`\n );\n }\n serverReady = true;\n this.isConnecting = false;\n this.options.onConnected();\n resolve();\n }\n }, HANDSHAKE_TIMEOUT_MS);\n\n this.ws.on('open', () => {\n this.reconnectAttempts = 0;\n this.startPing();\n });\n\n this.ws.on('message', (data) => {\n const raw = data.toString();\n\n // Intercept the 'connected' handshake message\n if (!serverReady) {\n try {\n const msg = JSON.parse(raw) as Record<string, unknown>;\n if (msg.type === 'connected') {\n serverReady = true;\n clearTimeout(handshakeTimeout);\n this.isConnecting = false;\n this.options.onConnected();\n resolve();\n return;\n }\n } catch {\n // Not JSON — fall through to normal handling\n }\n }\n\n this.handleMessage(raw);\n });\n\n this.ws.on('close', (code, reason) => {\n clearTimeout(handshakeTimeout);\n if (!serverReady) {\n this.isConnecting = false;\n reject(new Error(`WebSocket closed before server ready: code=${code} reason=${reason.toString()}`));\n return;\n }\n this.handleClose(code, reason.toString());\n });\n\n this.ws.on('error', (error) => {\n clearTimeout(handshakeTimeout);\n this.isConnecting = false;\n this.options.onError(error);\n reject(error);\n });\n\n this.ws.on('pong', () => {\n // Keep-alive response received\n });\n } catch (error) {\n this.isConnecting = false;\n reject(error);\n }\n });\n }\n\n /**\n * Send event through WebSocket\n */\n async send(event: TestEvent): Promise<void> {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('WebSocket is not connected');\n }\n\n return new Promise((resolve, reject) => {\n this.ws!.send(JSON.stringify(event), (error) => {\n if (error) {\n reject(error);\n } else {\n resolve();\n }\n });\n });\n }\n\n /**\n * Send multiple events in batch (parallel for speed)\n * Note: Uses Promise.all intentionally - if one send fails, connection is broken\n */\n async sendBatch(events: TestEvent[]): Promise<void> {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('WebSocket is not connected');\n }\n\n // Send all events in parallel for better performance\n await Promise.all(events.map((event) => this.send(event)));\n }\n\n /**\n * Send event and wait for server ACK\n * Use this for critical events like run:end where delivery must be confirmed\n */\n async sendAndWaitForAck(event: TestEvent, timeout: number = this.ackTimeout): Promise<void> {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n throw new Error('WebSocket is not connected');\n }\n\n const sequence = event.sequence;\n\n return new Promise((resolve, reject) => {\n // Set up ACK timeout\n const timer = setTimeout(() => {\n this.pendingAcks.delete(sequence);\n reject(new Error(`ACK timeout for sequence ${sequence} after ${timeout}ms`));\n }, timeout);\n\n // Register pending ACK\n this.pendingAcks.set(sequence, { resolve, reject, timer });\n\n // Send the event\n this.ws!.send(JSON.stringify(event), (error) => {\n if (error) {\n clearTimeout(timer);\n this.pendingAcks.delete(sequence);\n reject(error);\n }\n // Don't resolve here - wait for ACK from server\n });\n });\n }\n\n /**\n * Set ACK timeout for sendAndWaitForAck\n */\n setAckTimeout(timeout: number): void {\n this.ackTimeout = timeout;\n }\n\n /**\n * Check if WebSocket is connected\n */\n isConnected(): boolean {\n return this.ws?.readyState === WebSocket.OPEN;\n }\n\n /**\n * Close WebSocket connection\n */\n close(): void {\n this.isClosed = true;\n this.stopPing();\n this.clearReconnectTimer();\n this.clearPendingAcks();\n\n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n }\n\n /**\n * Clear all pending ACKs (reject them with connection closed error)\n */\n private clearPendingAcks(): void {\n for (const [sequence, pending] of this.pendingAcks) {\n clearTimeout(pending.timer);\n pending.reject(new Error(`Connection closed while waiting for ACK on sequence ${sequence}`));\n }\n this.pendingAcks.clear();\n }\n\n /**\n * Handle incoming messages\n */\n private handleMessage(data: string): void {\n try {\n const message = JSON.parse(data) as Record<string, unknown>;\n\n if (message.type === 'connected') {\n // Server acknowledged connection\n } else if (message.type === 'ack') {\n // Server acknowledged event receipt - resolve pending ACK promise\n // Validate sequence is actually a number (server might send string)\n const sequence = typeof message.sequence === 'number' ? message.sequence : undefined;\n if (sequence === undefined) {\n // ACK without valid sequence - can't match to pending request\n return;\n }\n if (this.pendingAcks.has(sequence)) {\n const pending = this.pendingAcks.get(sequence)!;\n clearTimeout(pending.timer);\n this.pendingAcks.delete(sequence);\n pending.resolve();\n }\n } else if (message.type === 'nack') {\n // Server rejected event - convert to Error and notify via onError\n const nack = message as unknown as NackMessage;\n\n // Convert NACK to Error and notify via lifecycle callback\n // Reporter will handle all error display (quota errors with special banner)\n if (isServerError(nack.error)) {\n // Pass the server error directly (it's already an Error instance)\n this.options.onError(nack.error);\n } else if (typeof nack.error === 'object' && nack.error !== null) {\n // Check if this is a structured quota error object\n const errorObj = nack.error as Record<string, unknown>;\n const errorCode = errorObj.code || errorObj.error;\n\n if (errorCode === 'QUOTA_EXCEEDED' && errorObj.details && typeof errorObj.details === 'object') {\n // Construct QuotaExceededError instance\n const details = errorObj.details as Record<string, unknown>;\n this.options.onError(\n new QuotaExceededError(errorObj.message?.toString() || 'Quota exceeded', {\n planName: details.planName?.toString() || 'Unknown',\n totalTests: Number(details.totalTests) || 0,\n remaining: Number(details.remaining) || 0,\n used: Number(details.used) || 0,\n total: Number(details.total) || 0,\n resetDate: details.resetDate?.toString(),\n canPartialSubmit: Boolean(details.canPartialSubmit),\n allowedCount: Number(details.allowedCount) || 0,\n projectName: details.projectName?.toString(),\n projectLimit: details.projectLimit != null ? Number(details.projectLimit) : undefined,\n projectUsed: details.projectUsed != null ? Number(details.projectUsed) : undefined,\n projectBorrowed: details.projectBorrowed != null ? Number(details.projectBorrowed) : undefined,\n })\n );\n } else if (errorCode === 'QUOTA_EXHAUSTED' && errorObj.details && typeof errorObj.details === 'object') {\n // Construct QuotaExhaustedError instance\n const details = errorObj.details as Record<string, unknown>;\n this.options.onError(\n new QuotaExhaustedError(errorObj.message?.toString() || 'Quota exhausted', {\n planName: details.planName?.toString() || 'Unknown',\n totalLimit: Number(details.totalLimit) || 0,\n used: Number(details.used) || 0,\n resetDate: details.resetDate?.toString(),\n })\n );\n } else {\n // Generic structured error\n const errorMessage =\n errorObj.message?.toString() || errorObj.error?.toString() || JSON.stringify(nack.error);\n this.options.onError(new Error(`Event rejected: ${errorMessage}`));\n }\n } else {\n // String or primitive error\n const errorMessage = typeof nack.error === 'string' ? nack.error : String(nack.error);\n this.options.onError(new Error(`Event rejected: ${errorMessage}`));\n }\n }\n } catch (error) {\n if (this.debug) {\n console.error('Failed to parse WebSocket message:', error);\n }\n }\n }\n\n /**\n * Handle connection close\n */\n private handleClose(_code: number, _reason: string): void {\n this.stopPing();\n this.clearPendingAcks();\n this.options.onDisconnected();\n\n // Don't reconnect if explicitly closed\n if (this.isClosed) {\n return;\n }\n\n // Attempt reconnection with exponential backoff\n if (this.reconnectAttempts < this.options.maxRetries) {\n this.scheduleReconnect();\n } else {\n this.options.onError(new Error(`WebSocket connection failed after ${this.options.maxRetries} attempts`));\n }\n }\n\n /**\n * Schedule reconnection with exponential backoff\n */\n private scheduleReconnect(): void {\n this.clearReconnectTimer();\n\n const delay = this.options.retryDelay * Math.pow(2, this.reconnectAttempts);\n this.reconnectAttempts++;\n\n this.reconnectTimer = setTimeout(() => {\n this.connect().catch((error) => {\n if (this.debug) {\n console.error('Reconnection failed:', error);\n }\n });\n }, delay);\n }\n\n /**\n * Clear reconnection timer\n */\n private clearReconnectTimer(): void {\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n }\n\n /**\n * Start ping interval for keep-alive\n */\n private startPing(): void {\n this.stopPing();\n this.pingInterval = setInterval(() => {\n if (this.ws?.readyState === WebSocket.OPEN) {\n this.ws.ping();\n }\n }, 30000); // Ping every 30 seconds\n }\n\n /**\n * Stop ping interval\n */\n private stopPing(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n}\n","/**\n * Server error classes for quota and infrastructure errors\n * These are received from the TestDino server and handled gracefully in the reporter\n * Follows the same Error class pattern as cli/errors.ts\n */\n\n/**\n * Base class for all TestDino server errors\n * Extends Error to work with standard error handling\n */\nexport class TestDinoServerError extends Error {\n public readonly code: string;\n\n constructor(code: string, message: string) {\n super(message);\n this.name = 'TestDinoServerError';\n this.code = code;\n Error.captureStackTrace(this, this.constructor);\n }\n}\n\n/**\n * Quota exhausted error (session creation)\n * HTTP 402 response when organization pool is exhausted\n */\nexport class QuotaExhaustedError extends TestDinoServerError {\n public readonly details: {\n planName: string;\n totalLimit: number;\n used: number;\n resetDate?: string;\n };\n\n constructor(\n message: string,\n details: {\n planName: string;\n totalLimit: number;\n used: number;\n resetDate?: string;\n }\n ) {\n super('QUOTA_EXHAUSTED', message);\n this.name = 'QuotaExhaustedError';\n this.details = details;\n }\n}\n\n/**\n * Quota exceeded error (run:begin)\n * WebSocket NACK when test count exceeds available quota\n */\nexport class QuotaExceededError extends TestDinoServerError {\n public readonly details: {\n planName: string;\n totalTests: number;\n remaining: number;\n used: number;\n total: number;\n resetDate?: string;\n canPartialSubmit: boolean;\n allowedCount: number;\n projectName?: string;\n projectLimit?: number;\n projectUsed?: number;\n projectBorrowed?: number;\n };\n\n constructor(\n message: string,\n details: {\n planName: string;\n totalTests: number;\n remaining: number;\n used: number;\n total: number;\n resetDate?: string;\n canPartialSubmit: boolean;\n allowedCount: number;\n projectName?: string;\n projectLimit?: number;\n projectUsed?: number;\n projectBorrowed?: number;\n }\n ) {\n super('QUOTA_EXCEEDED', message);\n this.name = 'QuotaExceededError';\n this.details = details;\n }\n}\n\n/**\n * Queue full error (backpressure)\n * WebSocket NACK when event queue is full\n */\nexport class QueueFullError extends TestDinoServerError {\n constructor(message: string) {\n super('QUEUE_FULL', message);\n this.name = 'QueueFullError';\n }\n}\n\n/**\n * Circuit breaker error (Redis unavailable)\n * WebSocket NACK when circuit breaker is open\n */\nexport class CircuitBreakerError extends TestDinoServerError {\n public readonly retryAfter?: number;\n\n constructor(message: string, retryAfter?: number) {\n super('CIRCUIT_BREAKER_OPEN', message);\n this.name = 'CircuitBreakerError';\n this.retryAfter = retryAfter;\n }\n}\n\n/**\n * Union type for all server error classes\n */\nexport type ServerError = QuotaExhaustedError | QuotaExceededError | QueueFullError | CircuitBreakerError;\n\n/**\n * Type guard to check if error is a server error instance\n * @param error - Unknown error value to check\n * @returns true if error is an instance of TestDinoServerError\n */\nexport function isServerError(error: unknown): error is ServerError {\n return error instanceof TestDinoServerError;\n}\n\n/**\n * Type guard to check if error is a quota-related error\n * @param error - Unknown error value to check\n * @returns true if error is QuotaExhaustedError or QuotaExceededError\n */\nexport function isQuotaError(error: unknown): error is QuotaExhaustedError | QuotaExceededError {\n return error instanceof QuotaExhaustedError || error instanceof QuotaExceededError;\n}\n\n/**\n * Type guard to check if error is a retryable infrastructure error\n * @param error - Unknown error value to check\n * @returns true if error is QueueFullError or CircuitBreakerError\n */\nexport function isRetryableError(error: unknown): error is QueueFullError | CircuitBreakerError {\n return error instanceof QueueFullError || error instanceof CircuitBreakerError;\n}\n","/**\n * HTTP client for fallback event transmission\n */\n\nimport axios, { type AxiosInstance } from 'axios';\nimport type { TestEvent, SessionCreationQuotaResponse } from '../types/index.js';\nimport { QuotaExceededError, QuotaExhaustedError } from '../reporter/errors.js';\nimport { sleep } from '../utils/index.js';\n\nexport interface HttpClientOptions {\n token: string;\n serverUrl: string;\n maxRetries?: number;\n retryDelay?: number;\n}\n\nexport class HttpClient {\n private client: AxiosInstance;\n private options: Required<HttpClientOptions>;\n\n constructor(options: HttpClientOptions) {\n this.options = {\n maxRetries: 3,\n retryDelay: 1000,\n ...options,\n };\n\n this.client = axios.create({\n baseURL: this.options.serverUrl,\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.options.token}`,\n },\n timeout: 10000,\n });\n }\n\n /**\n * Authenticate with server\n */\n async authenticate(): Promise<{ sessionId: string; wsUrl: string }> {\n try {\n const response = await this.client.post('/auth');\n return response.data;\n } catch (error) {\n // Handle quota exhausted error (402)\n if (axios.isAxiosError(error) && error.response?.status === 402) {\n const quotaError = error.response.data as SessionCreationQuotaResponse;\n\n // Throw proper Error class instance\n throw new QuotaExhaustedError(quotaError.message, quotaError.details);\n }\n\n throw new Error(`Authentication failed: ${this.getErrorMessage(error)}`);\n }\n }\n\n /**\n * Send events via HTTP (fallback)\n */\n async sendEvents(events: TestEvent[]): Promise<void> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < this.options.maxRetries; attempt++) {\n try {\n await this.client.post('/events', { events });\n return;\n } catch (error) {\n // Don't retry on quota errors — throw immediately\n if (axios.isAxiosError(error) && error.response?.status === 402) {\n const data = error.response.data;\n const details = data?.details;\n if (data?.error === 'QUOTA_EXCEEDED' && details) {\n throw new QuotaExceededError(data.message || 'Quota exceeded', details);\n }\n throw new QuotaExhaustedError(data?.message || 'Quota exceeded', details);\n }\n\n lastError = new Error(this.getErrorMessage(error));\n\n if (attempt < this.options.maxRetries - 1) {\n // Wait before retry with exponential backoff\n const delay = this.options.retryDelay * Math.pow(2, attempt);\n await sleep(delay);\n }\n }\n }\n\n throw lastError || new Error('Failed to send events via HTTP');\n }\n\n /**\n * Send single event via HTTP\n */\n async sendEvent(event: TestEvent): Promise<void> {\n await this.sendEvents([event]);\n }\n\n /**\n * Extract error message from various error types\n */\n private getErrorMessage(error: unknown): string {\n if (axios.isAxiosError(error)) {\n return error.response?.data?.message || error.message;\n }\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n }\n}\n","/**\n * Shared utility functions\n */\n\nimport { relative } from 'path';\n\n/**\n * Normalize file path to repo-root-relative.\n * Ensures consistent paths across different machines and shards.\n */\nexport function normalizePath(filePath: string, rootDir?: string): string {\n if (rootDir && filePath.startsWith(rootDir)) {\n return relative(rootDir, filePath);\n }\n return filePath;\n}\n\n/**\n * Sleep utility for retry delays\n */\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Check if debug mode is enabled (standardized check)\n */\nexport function isDebugEnabled(): boolean {\n return process.env.TESTDINO_DEBUG === 'true' || process.env.TESTDINO_DEBUG === '1' || process.env.DEBUG === 'true';\n}\n","/**\n * Event buffer for batching and managing event transmission\n */\n\nimport type { TestEvent } from '../types/index.js';\n\nexport interface EventBufferOptions {\n maxSize?: number;\n onFlush?: (events: TestEvent[]) => Promise<void>;\n}\n\nexport class EventBuffer {\n private events: TestEvent[] = [];\n private maxSize: number;\n private onFlush: (events: TestEvent[]) => Promise<void>;\n private flushPromise: Promise<void> | null = null;\n\n constructor(options: EventBufferOptions) {\n this.maxSize = options.maxSize || 10;\n this.onFlush = options.onFlush || (async () => {});\n }\n\n /**\n * Add event to buffer\n * Automatically flushes if buffer reaches max size\n */\n async add(event: TestEvent): Promise<void> {\n this.events.push(event);\n\n if (this.events.length >= this.maxSize) {\n await this.flush();\n }\n }\n\n /**\n * Flush all buffered events\n * If a flush is already in progress, waits for it to complete then flushes any new events\n */\n async flush(): Promise<void> {\n // If a flush is in progress, wait for it to complete\n if (this.flushPromise) {\n await this.flushPromise;\n // After waiting, check if there are more events to flush (added during wait)\n // Recursively call flush to handle them\n if (this.events.length > 0) {\n return this.flush();\n }\n return;\n }\n\n // Nothing to flush\n if (this.events.length === 0) {\n return;\n }\n\n // Start the flush operation and track it\n this.flushPromise = this.doFlush();\n\n try {\n await this.flushPromise;\n } finally {\n this.flushPromise = null;\n }\n }\n\n /**\n * Internal flush implementation\n */\n private async doFlush(): Promise<void> {\n try {\n const eventsToFlush = [...this.events];\n this.events = [];\n await this.onFlush(eventsToFlush);\n } catch (error) {\n // If flush fails, log error but don't restore events (they were already sent partially)\n console.error('Failed to flush events:', error);\n throw error;\n }\n }\n\n /**\n * Get current buffer size\n */\n size(): number {\n return this.events.length;\n }\n\n /**\n * Check if buffer is empty\n */\n isEmpty(): boolean {\n return this.events.length === 0;\n }\n\n /**\n * Clear buffer without flushing\n */\n clear(): void {\n this.events = [];\n }\n\n /**\n * Get all events without removing them\n */\n getEvents(): TestEvent[] {\n return [...this.events];\n }\n}\n","/**\n * Git metadata collector\n * Collects git repository information using git commands\n */\n\nimport { readFile } from 'fs/promises';\nimport { BaseMetadataCollector } from './base.js';\nimport type { GitMetadata, PRMetadata } from './types.js';\n\n// Dynamic import for execa (ESM-only package, needs dynamic import in CJS context)\nlet execaPromise: Promise<typeof import('execa').execa> | null = null;\n\n/**\n * Dynamically import execa (ESM-only package) for CJS compatibility only once per process using memoization.\n *\n * @returns Promise resolving to the execa function\n * @throws Error if execa cannot be imported\n */\nasync function getExeca(): Promise<typeof import('execa').execa> {\n if (!execaPromise) {\n execaPromise = import('execa')\n .then((m) => m.execa)\n .catch((error) => {\n console.error('Failed to import execa:', error.message);\n throw new Error('Failed to load execa. Ensure execa is installed: npm install execa');\n });\n }\n return execaPromise;\n}\n\n/**\n * Minimal GitHub event data interface for type safety\n */\ninterface GitHubEventData {\n pull_request?: {\n title?: unknown;\n number?: unknown;\n state?: unknown;\n head?: { ref?: unknown; sha?: unknown };\n base?: { ref?: unknown };\n user?: { login?: unknown };\n labels?: Array<{ name?: unknown }>;\n merged?: unknown;\n mergeable?: unknown;\n merge_commit_sha?: unknown;\n };\n}\n\n/**\n * Resolved GitHub author information for deduplication and canonical naming\n */\ninterface GitHubAuthorInfo {\n authorId: string;\n authorLogin?: string;\n}\n\nexport interface GitCollectorOptions {\n /**\n * Timeout for git commands in milliseconds\n * @default 3000\n */\n timeout?: number;\n\n /**\n * Working directory for git commands\n * @default process.cwd()\n */\n cwd?: string;\n}\n\n/**\n * Collects git repository metadata\n */\nexport class GitMetadataCollector extends BaseMetadataCollector<GitMetadata> {\n private options: Required<GitCollectorOptions>;\n\n constructor(options: GitCollectorOptions = {}) {\n super('git');\n this.options = {\n timeout: options.timeout || 3000,\n cwd: options.cwd || process.cwd(),\n };\n }\n\n /**\n * Collect git metadata\n */\n protected async collectMetadata(): Promise<GitMetadata> {\n // Fix git ownership issues in CI containers\n await this.configureGitForCI();\n\n // Check if we're in a git repository\n const isGitRepo = await this.isGitRepository();\n if (!isGitRepo) {\n return this.getEmptyMetadata();\n }\n\n // Collect all git metadata in parallel using Promise.allSettled for error isolation\n const results = await Promise.allSettled([\n this.getBranch(),\n this.getCommitHash(),\n this.getCommitMessage(),\n this.getAuthorName(),\n this.getAuthorEmail(),\n this.getCommitTimestamp(),\n this.getRepoUrl(),\n this.isDirtyWorkingTree(),\n ]);\n\n // Extract values, using undefined for rejected promises\n const extractedValues = results.map((r) => (r.status === 'fulfilled' ? r.value : undefined));\n\n // Variables that may be reassigned in PR context below\n let branch = extractedValues[0] as string | undefined;\n let hash = extractedValues[1] as string | undefined;\n let message = extractedValues[2] as string | undefined;\n let author = extractedValues[3] as string | undefined;\n let email = extractedValues[4] as string | undefined;\n let timestamp = extractedValues[5] as string | undefined;\n\n // Variables that are never reassigned\n const repoUrl = extractedValues[6] as string | undefined;\n const isDirty = extractedValues[7] as boolean | undefined;\n\n // Handle PR context — override merge commit data with real PR data\n let prMetadata: PRMetadata | undefined;\n\n if (process.env.GITHUB_EVENT_NAME === 'pull_request') {\n const eventData = await this.readGitHubEventFile();\n\n if (eventData?.pull_request) {\n prMetadata = this.extractPRMetadata(eventData);\n\n // Override branch with real source branch\n const headRef = process.env.GITHUB_HEAD_REF;\n if (this.isNonEmptyString(headRef)) {\n branch = headRef;\n }\n\n // Override commit data with real PR head commit\n const headSha = eventData.pull_request.head?.sha;\n if (this.isNonEmptyString(headSha)) {\n hash = headSha;\n const realCommit = await this.getCommitInfoFromSha(headSha);\n if (realCommit) {\n message = realCommit.message ?? message;\n author = realCommit.author ?? author;\n email = realCommit.email ?? email;\n timestamp = realCommit.timestamp ?? timestamp;\n }\n }\n }\n }\n\n // Resolve GitHub author info: numeric ID for deduplication, login for canonical name\n const githubAuthor = await this.resolveGitHubAuthor(hash);\n\n return {\n branch,\n commit: {\n hash,\n message,\n author: githubAuthor.authorLogin || author,\n authorId: githubAuthor.authorId,\n email,\n timestamp,\n isDirty,\n },\n repository: {\n name: this.extractRepoName(repoUrl),\n url: repoUrl,\n },\n ...(prMetadata ? { pr: prMetadata } : {}),\n };\n }\n\n /**\n * Get empty metadata\n */\n protected getEmptyMetadata(): GitMetadata {\n return {};\n }\n\n /**\n * Configure git for CI environments\n * Fixes \"dubious ownership\" errors when workspace is mounted with different ownership\n */\n private async configureGitForCI(): Promise<void> {\n const isCI = process.env.CI === 'true' || process.env.GITHUB_ACTIONS === 'true';\n if (!isCI) return;\n\n try {\n const execa = await getExeca();\n await execa('git', ['config', '--global', '--add', 'safe.directory', this.options.cwd], {\n timeout: this.options.timeout,\n reject: true,\n });\n } catch {\n // Silently continue — this is a best-effort fix\n }\n }\n\n /**\n * Read and parse the GitHub Actions event file\n */\n private async readGitHubEventFile(): Promise<GitHubEventData | undefined> {\n const eventPath = process.env.GITHUB_EVENT_PATH;\n if (!eventPath) return undefined;\n\n try {\n const content = await this.withTimeout(\n readFile(eventPath, 'utf-8'),\n this.options.timeout,\n 'GitHub event file read'\n );\n return this.safeJsonParse<GitHubEventData>(content, {});\n } catch (error) {\n console.warn(\n '⚠️ TestDino: Failed to read GitHub event data:',\n error instanceof Error ? error.message : String(error)\n );\n return undefined;\n }\n }\n\n /**\n * Extract PR metadata from GitHub event data\n */\n private extractPRMetadata(eventData: GitHubEventData): PRMetadata | undefined {\n const pullRequest = eventData?.pull_request;\n if (!pullRequest) return undefined;\n\n const prMetadata: PRMetadata = {};\n\n // Basic PR information\n if (this.isNonEmptyString(pullRequest.title)) {\n prMetadata.title = pullRequest.title;\n }\n\n if (typeof pullRequest.number === 'number') {\n prMetadata.number = pullRequest.number;\n }\n\n if (this.isNonEmptyString(pullRequest.state)) {\n prMetadata.status = pullRequest.state;\n }\n\n // Build PR URL\n const serverUrl = process.env.GITHUB_SERVER_URL;\n const repository = process.env.GITHUB_REPOSITORY;\n if (prMetadata.number && serverUrl && repository) {\n prMetadata.url = `${serverUrl}/${repository}/pull/${prMetadata.number}`;\n }\n\n // Branch information\n if (pullRequest.head?.ref && this.isNonEmptyString(pullRequest.head.ref)) {\n prMetadata.branch = pullRequest.head.ref;\n }\n\n if (pullRequest.base?.ref && this.isNonEmptyString(pullRequest.base.ref)) {\n prMetadata.targetBranch = pullRequest.base.ref;\n }\n\n // Author information\n if (pullRequest.user?.login && this.isNonEmptyString(pullRequest.user.login)) {\n prMetadata.author = pullRequest.user.login;\n }\n\n // Labels\n if (Array.isArray(pullRequest.labels) && pullRequest.labels.length > 0) {\n const labels = pullRequest.labels\n .map((label) => label?.name)\n .filter((name): name is string => this.isNonEmptyString(name));\n\n if (labels.length > 0) {\n prMetadata.labels = labels;\n }\n }\n\n // Merge status\n if (typeof pullRequest.merged === 'boolean') {\n prMetadata.merged = pullRequest.merged;\n }\n\n if (typeof pullRequest.mergeable === 'boolean') {\n prMetadata.mergeable = pullRequest.mergeable;\n }\n\n if (this.isNonEmptyString(pullRequest.merge_commit_sha)) {\n prMetadata.mergeCommitSha = pullRequest.merge_commit_sha;\n }\n\n // Return undefined if no meaningful data was extracted\n const hasData = Object.keys(prMetadata).length > 0;\n return hasData ? prMetadata : undefined;\n }\n\n /**\n * Get commit details for a specific SHA using git show\n * Used to resolve real commit data in PR context (instead of merge commit)\n */\n private async getCommitInfoFromSha(sha: string): Promise<\n | {\n message?: string;\n author?: string;\n email?: string;\n timestamp?: string;\n }\n | undefined\n > {\n try {\n const result = await this.execGit(['show', '-s', '--format=%s%n%an%n%ae%n%aI', sha]);\n const lines = result.split('\\n');\n if (lines.length < 4) return undefined;\n\n return {\n message: this.isNonEmptyString(lines[0]) ? lines[0] : undefined,\n author: this.isNonEmptyString(lines[1]) ? lines[1] : undefined,\n email: this.isNonEmptyString(lines[2]) ? lines[2] : undefined,\n timestamp: this.isNonEmptyString(lines[3]) ? lines[3] : undefined,\n };\n } catch (error) {\n console.warn(\n '⚠️ TestDino: Failed to get commit info from SHA:',\n error instanceof Error ? error.message : String(error)\n );\n return undefined;\n }\n }\n\n /**\n * Resolve GitHub author info via the Commits API\n * Only runs on GitHub Actions where GITHUB_REPOSITORY is available\n */\n private async resolveGitHubAuthor(commitHash: string | undefined): Promise<GitHubAuthorInfo> {\n const empty: GitHubAuthorInfo = { authorId: '' };\n\n if (process.env.GITHUB_ACTIONS !== 'true') {\n return empty;\n }\n\n if (!commitHash) {\n return empty;\n }\n\n const repository = process.env.GITHUB_REPOSITORY;\n if (!repository) {\n return empty;\n }\n\n const url = `https://api.github.com/repos/${repository}/commits/${commitHash}`;\n\n try {\n const headers: Record<string, string> = {\n Accept: 'application/vnd.github.v3+json',\n 'User-Agent': 'testdino-playwright',\n };\n\n const token = process.env.GITHUB_TOKEN;\n if (token) {\n headers.Authorization = `token ${token}`;\n }\n\n const response = await this.withTimeout(fetch(url, { headers }), this.options.timeout, 'GitHub Commits API');\n\n if (!response.ok) {\n return empty;\n }\n\n const data = (await response.json()) as {\n author?: { id?: number; login?: string } | null;\n };\n\n if (data?.author?.id) {\n const authorId = String(data.author.id);\n const authorLogin = this.isNonEmptyString(data.author.login) ? data.author.login : undefined;\n return { authorId, authorLogin };\n }\n\n return this.resolveGitHubAuthorFromActor();\n } catch (error) {\n console.warn(\n '⚠️ TestDino: Failed to resolve GitHub author from Commits API:',\n error instanceof Error ? error.message : String(error)\n );\n return this.resolveGitHubAuthorFromActor();\n }\n }\n\n /**\n * Fallback: resolve GitHub author info from GITHUB_ACTOR via the Users API\n */\n private async resolveGitHubAuthorFromActor(): Promise<GitHubAuthorInfo> {\n const actor = process.env.GITHUB_ACTOR;\n if (!actor) {\n return { authorId: '' };\n }\n\n const url = `https://api.github.com/users/${actor}`;\n\n try {\n const response = await this.withTimeout(\n fetch(url, {\n headers: {\n Accept: 'application/vnd.github.v3+json',\n 'User-Agent': 'testdino-playwright',\n },\n }),\n this.options.timeout,\n 'GitHub Users API'\n );\n\n if (!response.ok) {\n return { authorId: '' };\n }\n\n const data = (await response.json()) as {\n id?: number;\n login?: string;\n };\n\n const authorId = data?.id ? String(data.id) : '';\n const authorLogin = this.isNonEmptyString(data?.login) ? data.login : actor;\n\n return { authorId, authorLogin };\n } catch (error) {\n console.warn(\n '⚠️ TestDino: Failed to resolve GitHub author from GITHUB_ACTOR:',\n error instanceof Error ? error.message : String(error)\n );\n return { authorId: '' };\n }\n }\n\n /**\n * Check if current directory is a git repository\n */\n private async isGitRepository(): Promise<boolean> {\n try {\n await this.execGit(['rev-parse', '--git-dir']);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Get current branch name\n * Uses: git rev-parse --abbrev-ref HEAD\n */\n private async getBranch(): Promise<string | undefined> {\n try {\n const result = await this.execGit(['rev-parse', '--abbrev-ref', 'HEAD']);\n return this.isNonEmptyString(result) ? result : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Get current commit hash (full SHA)\n * Uses: git rev-parse HEAD\n */\n private async getCommitHash(): Promise<string | undefined> {\n try {\n const result = await this.execGit(['rev-parse', 'HEAD']);\n return this.isNonEmptyString(result) ? result : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Get commit message of current HEAD\n * Uses: git log -1 --pretty=format:%s\n */\n private async getCommitMessage(): Promise<string | undefined> {\n try {\n const result = await this.execGit(['log', '-1', '--pretty=format:%s']);\n return this.isNonEmptyString(result) ? result : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Get commit author name\n * Uses: git log -1 --pretty=format:%an\n */\n private async getAuthorName(): Promise<string | undefined> {\n try {\n const result = await this.execGit(['log', '-1', '--pretty=format:%an']);\n return this.isNonEmptyString(result) ? result : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Get commit author email\n * Uses: git log -1 --pretty=format:%ae\n */\n private async getAuthorEmail(): Promise<string | undefined> {\n try {\n const result = await this.execGit(['log', '-1', '--pretty=format:%ae']);\n return this.isNonEmptyString(result) ? result : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Get commit timestamp (ISO format)\n * Uses: git log -1 --pretty=format:%aI\n */\n private async getCommitTimestamp(): Promise<string | undefined> {\n try {\n const result = await this.execGit(['log', '-1', '--pretty=format:%aI']);\n return this.isNonEmptyString(result) ? result : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Get remote origin URL\n * Uses: git config --get remote.origin.url\n */\n private async getRepoUrl(): Promise<string | undefined> {\n try {\n const result = await this.execGit(['config', '--get', 'remote.origin.url']);\n return this.isNonEmptyString(result) ? result : undefined;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Check if working tree has uncommitted changes\n * Uses: git status --porcelain\n * Returns true if there are any changes (staged, unstaged, or untracked)\n */\n private async isDirtyWorkingTree(): Promise<boolean | undefined> {\n try {\n const result = await this.execGit(['status', '--porcelain']);\n // If output is non-empty, there are changes\n return result.trim().length > 0;\n } catch {\n return undefined;\n }\n }\n\n /**\n * Extract repository name from remote URL\n * e.g., \"https://github.com/user/repo.git\" → \"repo\"\n */\n private extractRepoName(repoUrl: string | undefined): string | undefined {\n if (!repoUrl) return undefined;\n return repoUrl.split('/').pop()?.replace('.git', '') || undefined;\n }\n\n /**\n * Execute git command with timeout\n */\n private async execGit(args: string[]): Promise<string> {\n const execa = await getExeca();\n const { stdout } = await this.withTimeout(\n execa('git', args, {\n cwd: this.options.cwd,\n timeout: this.options.timeout,\n reject: true,\n }),\n this.options.timeout,\n `git ${args.join(' ')}`\n );\n\n return stdout.trim();\n }\n}\n","/**\n * Base metadata collector implementation\n */\n\nimport type { MetadataCollector, MetadataCollectionResult } from './types.js';\n\n/**\n * Abstract base class for metadata collectors\n * Provides common functionality and error handling patterns\n */\nexport abstract class BaseMetadataCollector<T = Record<string, unknown>> implements MetadataCollector<T> {\n protected name: string;\n\n constructor(name: string) {\n this.name = name;\n }\n\n /**\n * Get collector name\n */\n getName(): string {\n return this.name;\n }\n\n /**\n * Collect metadata with error handling and timing\n */\n async collect(): Promise<T> {\n try {\n const data = await this.collectMetadata();\n return data;\n } catch (error) {\n // Log error but don't throw - graceful degradation\n console.warn(\n `⚠️ TestDino: ${this.name} metadata collection failed:`,\n error instanceof Error ? error.message : String(error)\n );\n return this.getEmptyMetadata();\n }\n }\n\n /**\n * Collect metadata with result tracking\n */\n async collectWithResult(): Promise<MetadataCollectionResult<T>> {\n const startTime = Date.now();\n const collector = this.getName();\n\n try {\n const data = await this.collectMetadata();\n const duration = Date.now() - startTime;\n\n return {\n data,\n success: true,\n duration,\n collector,\n };\n } catch (error) {\n const duration = Date.now() - startTime;\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n // Log error but return empty data for graceful degradation\n console.warn(`⚠️ TestDino: ${collector} metadata collection failed:`, errorMessage);\n\n return {\n data: this.getEmptyMetadata(),\n success: false,\n error: errorMessage,\n duration,\n collector,\n };\n }\n }\n\n /**\n * Abstract method to be implemented by concrete collectors\n */\n protected abstract collectMetadata(): Promise<T>;\n\n /**\n * Abstract method to provide empty metadata on errors\n */\n protected abstract getEmptyMetadata(): T;\n\n /**\n * Utility method to run command with timeout\n */\n protected async withTimeout<R>(promise: Promise<R>, timeoutMs: number, operation: string): Promise<R> {\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error(`${operation} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n return Promise.race([promise, timeoutPromise]);\n }\n\n /**\n * Utility method to safely parse JSON\n */\n protected safeJsonParse<R>(jsonString: string, fallback: R): R {\n try {\n return JSON.parse(jsonString);\n } catch {\n return fallback;\n }\n }\n\n /**\n * Utility method to check if a value is non-empty string\n */\n protected isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0;\n }\n}\n","/**\n * CI metadata collector\n * Collects CI environment information, focusing on GitHub Actions\n */\n\nimport { type as osType, release as osRelease } from 'os';\nimport { BaseMetadataCollector } from './base.js';\nimport type { CIMetadata } from './types.js';\n\nexport interface CICollectorOptions {\n /**\n * Timeout for CI metadata collection in milliseconds\n * @default 3000\n */\n timeout?: number;\n}\n\n/**\n * Collects CI environment metadata\n */\nexport class CIMetadataCollector extends BaseMetadataCollector<CIMetadata> {\n constructor(_options: CICollectorOptions = {}) {\n super('ci');\n }\n\n /**\n * Collect CI metadata\n */\n protected async collectMetadata(): Promise<CIMetadata> {\n const provider = this.detectCIProvider();\n\n if (!provider) {\n return {\n provider: 'local',\n environment: this.collectEnvironment(),\n };\n }\n\n if (provider === 'github-actions') {\n return this.collectGitHubActionsMetadata();\n }\n\n // Fallback for unknown CI providers\n return {\n provider,\n environment: this.collectEnvironment(),\n };\n }\n\n /**\n * Get empty metadata\n */\n protected getEmptyMetadata(): CIMetadata {\n return {};\n }\n\n /**\n * Detect CI provider from environment variables\n */\n private detectCIProvider(): string | undefined {\n const { env } = process;\n\n // GitHub Actions detection\n if (env.GITHUB_ACTIONS === 'true') {\n return 'github-actions';\n }\n\n // Could add other CI providers here in the future\n // if (env.GITLAB_CI) return 'gitlab';\n // if (env.CIRCLECI) return 'circleci';\n\n return undefined;\n }\n\n /**\n * Collect GitHub Actions specific metadata\n */\n private collectGitHubActionsMetadata(): CIMetadata {\n const { env } = process;\n\n return {\n provider: 'github-actions',\n pipeline: {\n id: env.GITHUB_RUN_ID,\n name: env.GITHUB_WORKFLOW,\n url: this.buildPipelineUrl(),\n },\n build: {\n number: env.GITHUB_RUN_NUMBER,\n trigger: env.GITHUB_EVENT_NAME,\n },\n environment: this.collectEnvironment(),\n };\n }\n\n /**\n * Build the pipeline URL from GitHub Actions environment variables\n */\n private buildPipelineUrl(): string | undefined {\n const { env } = process;\n const serverUrl = env.GITHUB_SERVER_URL;\n const repository = env.GITHUB_REPOSITORY;\n const runId = env.GITHUB_RUN_ID;\n\n if (serverUrl && repository && runId) {\n return `${serverUrl}/${repository}/actions/runs/${runId}`;\n }\n\n return undefined;\n }\n\n /**\n * Collect runner environment information\n */\n private collectEnvironment(): CIMetadata['environment'] {\n try {\n return {\n name: osType(),\n type: process.platform,\n os: `${osType()} ${osRelease()}`,\n node: process.version,\n };\n } catch {\n return {};\n }\n }\n}\n","/**\n * System metadata collector\n * Collects system information using Node.js built-in modules\n */\n\nimport { platform, release, cpus, totalmem, hostname } from 'os';\nimport { version } from 'process';\nimport { BaseMetadataCollector } from './base.js';\nimport type { SystemMetadata } from './types.js';\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface SystemCollectorOptions {\n // Reserved for future options\n}\n\n/**\n * Collects system metadata using Node.js built-in modules\n */\nexport class SystemMetadataCollector extends BaseMetadataCollector<SystemMetadata> {\n constructor(_options: SystemCollectorOptions = {}) {\n super('system');\n // System metadata collection is synchronous and doesn't need configuration\n }\n\n /**\n * Collect system metadata\n */\n protected async collectMetadata(): Promise<SystemMetadata> {\n // System metadata collection is synchronous and fast\n // but we wrap it in a promise for consistency with other collectors\n return new Promise((resolve) => {\n const metadata: SystemMetadata = {\n os: this.getOperatingSystem(),\n cpu: this.getCpuInfo(),\n memory: this.getMemoryInfo(),\n nodeVersion: this.getNodeVersion(),\n platform: this.getPlatform(),\n hostname: this.getHostname(),\n };\n\n resolve(metadata);\n });\n }\n\n /**\n * Get empty metadata\n */\n protected getEmptyMetadata(): SystemMetadata {\n return {};\n }\n\n /**\n * Get operating system information\n * Format: \"platform release\" (e.g., \"darwin 23.1.0\", \"linux 5.4.0\")\n */\n private getOperatingSystem(): string {\n let platformName = 'unknown';\n let releaseVersion = 'unknown';\n\n try {\n platformName = platform();\n } catch {\n // Keep default 'unknown'\n }\n\n try {\n releaseVersion = release();\n } catch {\n // Keep default 'unknown'\n }\n\n return `${platformName} ${releaseVersion}`;\n }\n\n /**\n * Get CPU information\n * Format: \"model (X cores)\" (e.g., \"Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz (12 cores)\")\n */\n private getCpuInfo(): string {\n try {\n const cpuList = cpus();\n if (cpuList.length === 0) {\n return 'unknown';\n }\n\n const model = cpuList[0].model.trim();\n const coreCount = cpuList.length;\n\n return `${model} (${coreCount} cores)`;\n } catch {\n return 'unknown';\n }\n }\n\n /**\n * Get memory information\n * Format: \"X.X GB\" (e.g., \"16.0 GB\", \"8.5 GB\")\n */\n private getMemoryInfo(): string {\n try {\n const totalBytes = totalmem();\n const totalGB = totalBytes / (1024 * 1024 * 1024);\n\n // Round to 1 decimal place\n return `${totalGB.toFixed(1)} GB`;\n } catch {\n return 'unknown';\n }\n }\n\n /**\n * Get Node.js version\n * Returns process.version (e.g., \"v18.17.0\")\n */\n private getNodeVersion(): string {\n try {\n return version;\n } catch {\n return 'unknown';\n }\n }\n\n /**\n * Get platform identifier\n * Returns os.platform() (e.g., \"darwin\", \"linux\", \"win32\")\n */\n private getPlatform(): string {\n try {\n return platform();\n } catch {\n return 'unknown';\n }\n }\n\n /**\n * Get hostname\n * Returns os.hostname() (e.g., \"MacBook-Pro.local\")\n */\n private getHostname(): string {\n try {\n return hostname();\n } catch {\n return 'unknown';\n }\n }\n}\n","/**\n * Playwright metadata collector\n * Collects Playwright configuration and version information\n */\n\nimport { readFile } from 'fs/promises';\nimport { BaseMetadataCollector } from './base.js';\nimport type {\n PlaywrightMetadata,\n ProjectConfig,\n ProjectUseOptions,\n RunSkeleton,\n SkeletonSuite,\n SkeletonTest,\n} from './types.js';\n\n/**\n * Playwright FullProject interface (per-project config)\n * @see docs/PLAYWRIGHT-TYPES-REFERENCE.md\n */\ninterface PlaywrightFullProject {\n name?: string;\n testDir?: string;\n timeout?: number;\n retries?: number;\n repeatEach?: number;\n dependencies?: string[];\n grep?: RegExp | RegExp[];\n use?: {\n browserName?: 'chromium' | 'firefox' | 'webkit';\n defaultBrowserType?: 'chromium' | 'firefox' | 'webkit'; // From device presets\n channel?: string;\n headless?: boolean;\n viewport?: { width: number; height: number } | null;\n baseURL?: string;\n trace?: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | { mode: string };\n screenshot?: 'off' | 'on' | 'only-on-failure' | { mode: string };\n video?: 'off' | 'on' | 'retain-on-failure' | 'on-first-retry' | { mode: string };\n isMobile?: boolean;\n locale?: string;\n [key: string]: unknown;\n };\n}\n\n/**\n * Playwright FullConfig interface (global config)\n * Note: testDir, timeout, retries are on FullProject, not FullConfig\n * @see docs/PLAYWRIGHT-TYPES-REFERENCE.md\n */\ninterface PlaywrightFullConfig {\n fullyParallel?: boolean;\n workers?: number;\n shard?: {\n current: number;\n total: number;\n } | null;\n projects?: PlaywrightFullProject[];\n version?: string;\n configFile?: string;\n forbidOnly?: boolean;\n globalTimeout?: number;\n grep?: RegExp | RegExp[];\n maxFailures?: number;\n metadata?: Record<string, unknown>;\n reportSlowTests?: {\n max: number;\n threshold: number;\n } | null;\n rootDir?: string;\n tags?: string[];\n webServer?: Record<string, unknown> | null;\n}\n\n/**\n * Playwright Suite interface (minimal)\n */\ninterface PlaywrightSuite {\n title: string;\n type: 'root' | 'project' | 'file' | 'describe';\n location?: {\n file: string;\n line: number;\n column: number;\n };\n suites: PlaywrightSuite[];\n tests: PlaywrightTestCase[];\n allTests(): PlaywrightTestCase[];\n}\n\n/**\n * Playwright TestCase interface (minimal)\n */\ninterface PlaywrightTestCase {\n id: string;\n title: string;\n location: {\n file: string;\n line: number;\n column: number;\n };\n tags: string[];\n expectedStatus: 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted';\n annotations: Array<{\n type: string;\n description?: string;\n }>;\n}\n\nexport interface PlaywrightCollectorOptions {\n /**\n * Timeout for Playwright metadata collection in milliseconds\n * @default 3000\n */\n timeout?: number;\n\n /**\n * Playwright FullConfig object (when available from reporter context)\n */\n config?: PlaywrightFullConfig;\n\n /**\n * Playwright Suite object (when available from reporter context)\n */\n suite?: PlaywrightSuite;\n\n /**\n * Custom path to @playwright/test package.json for testing\n */\n packageJsonPath?: string;\n}\n\n/**\n * Collects Playwright metadata including version and configuration\n */\nexport class PlaywrightMetadataCollector extends BaseMetadataCollector<PlaywrightMetadata> {\n private options: Required<Omit<PlaywrightCollectorOptions, 'config' | 'suite' | 'packageJsonPath'>> &\n Pick<PlaywrightCollectorOptions, 'config' | 'suite' | 'packageJsonPath'>;\n\n constructor(options: PlaywrightCollectorOptions = {}) {\n super('playwright');\n this.options = {\n timeout: options.timeout || 3000,\n config: options.config,\n suite: options.suite,\n packageJsonPath: options.packageJsonPath,\n };\n }\n\n /**\n * Collect Playwright metadata\n */\n protected async collectMetadata(): Promise<PlaywrightMetadata> {\n const metadata: PlaywrightMetadata = {};\n\n // Collect version (always attempt this)\n const version = await this.getPlaywrightVersion();\n if (version) {\n metadata.version = version;\n }\n\n // Collect config metadata if FullConfig is available\n if (this.options.config) {\n const configMetadata = this.extractConfigMetadata(this.options.config);\n Object.assign(metadata, configMetadata);\n }\n\n return metadata;\n }\n\n /**\n * Build skeleton from Suite and include it in CompleteMetadata\n */\n buildSkeletonMetadata(suite: PlaywrightSuite): { skeleton: RunSkeleton } {\n const skeleton = this.buildSkeleton(suite);\n return { skeleton };\n }\n\n /**\n * Get empty metadata\n */\n protected getEmptyMetadata(): PlaywrightMetadata {\n return {};\n }\n\n /**\n * Get Playwright version from @playwright/test package.json\n */\n private async getPlaywrightVersion(): Promise<string | undefined> {\n try {\n const packageJsonPath = this.options.packageJsonPath || this.resolvePlaywrightPackageJson();\n\n if (!packageJsonPath) {\n return undefined;\n }\n\n const packageJsonContent = await this.withTimeout(\n readFile(packageJsonPath, 'utf-8'),\n this.options.timeout,\n 'Playwright package.json read'\n );\n\n const packageJson = this.safeJsonParse<{ version?: string }>(packageJsonContent, {});\n\n return this.isNonEmptyString(packageJson.version) ? packageJson.version : undefined;\n } catch (error) {\n // Log error but don't fail the entire collection\n console.warn(\n '⚠️ TestDino: Failed to read Playwright version:',\n error instanceof Error ? error.message : String(error)\n );\n return undefined;\n }\n }\n\n /**\n * Resolve @playwright/test package.json path\n */\n private resolvePlaywrightPackageJson(): string | undefined {\n try {\n // Try to resolve @playwright/test package.json\n return require.resolve('@playwright/test/package.json');\n } catch {\n // @playwright/test not found\n return undefined;\n }\n }\n\n /**\n * Extract metadata from Playwright FullConfig\n */\n private extractConfigMetadata(config: PlaywrightFullConfig): Partial<PlaywrightMetadata> {\n const metadata: Partial<PlaywrightMetadata> = {};\n\n // Config file path\n if (this.isNonEmptyString(config.configFile)) {\n metadata.configFile = config.configFile;\n }\n\n // Forbid only\n if (typeof config.forbidOnly === 'boolean') {\n metadata.forbidOnly = config.forbidOnly;\n }\n\n // Fully parallel mode\n if (typeof config.fullyParallel === 'boolean') {\n metadata.fullyParallel = config.fullyParallel;\n metadata.parallel = config.fullyParallel; // Keep for backward compatibility\n }\n\n // Global timeout\n if (typeof config.globalTimeout === 'number') {\n metadata.globalTimeout = config.globalTimeout;\n }\n\n // Grep pattern(s)\n if (config.grep) {\n const patterns = Array.isArray(config.grep) ? config.grep : [config.grep];\n metadata.grep = patterns.map((p) => p.source);\n }\n\n // Max failures\n if (typeof config.maxFailures === 'number') {\n metadata.maxFailures = config.maxFailures;\n }\n\n // Custom metadata\n if (config.metadata && typeof config.metadata === 'object') {\n metadata.metadata = config.metadata;\n }\n\n // Workers\n if (typeof config.workers === 'number') {\n metadata.workers = config.workers;\n }\n\n // Projects (full configuration from FullProject)\n if (Array.isArray(config.projects) && config.projects.length > 0) {\n const projectConfigs = config.projects\n .filter((project) => this.isNonEmptyString(project.name))\n .map((project) => this.extractProjectConfig(project));\n\n if (projectConfigs.length > 0) {\n metadata.projects = projectConfigs;\n\n // Extract unique browsers from all projects\n const browsers = this.extractBrowsersFromProjects(config.projects);\n if (browsers.length > 0) {\n metadata.browsers = browsers;\n }\n }\n }\n\n // Report slow tests\n if (config.reportSlowTests && typeof config.reportSlowTests === 'object') {\n metadata.reportSlowTests = {\n max: config.reportSlowTests.max,\n threshold: config.reportSlowTests.threshold,\n };\n }\n\n // Root directory\n if (this.isNonEmptyString(config.rootDir)) {\n metadata.rootDir = config.rootDir;\n }\n\n // Shard information\n if (config.shard && typeof config.shard.current === 'number' && typeof config.shard.total === 'number') {\n metadata.shard = {\n current: config.shard.current,\n total: config.shard.total,\n };\n }\n\n // Tags\n if (Array.isArray(config.tags) && config.tags.length > 0) {\n metadata.tags = config.tags;\n }\n\n // Web server\n if (config.webServer && typeof config.webServer === 'object') {\n metadata.webServer = config.webServer;\n }\n\n return metadata;\n }\n\n /**\n * Extract project configuration from FullProject\n */\n private extractProjectConfig(project: PlaywrightFullProject): ProjectConfig {\n const config: ProjectConfig = {\n name: project.name || '',\n };\n\n // Project-level settings (these exist on FullProject, not FullConfig)\n if (this.isNonEmptyString(project.testDir)) {\n config.testDir = project.testDir;\n }\n\n if (typeof project.timeout === 'number') {\n config.timeout = project.timeout;\n }\n\n if (typeof project.retries === 'number') {\n config.retries = project.retries;\n }\n\n if (typeof project.repeatEach === 'number' && project.repeatEach > 1) {\n config.repeatEach = project.repeatEach;\n }\n\n // Dependencies (projects that must run first)\n if (Array.isArray(project.dependencies) && project.dependencies.length > 0) {\n config.dependencies = project.dependencies;\n }\n\n // Project-specific grep patterns\n if (project.grep) {\n const patterns = Array.isArray(project.grep) ? project.grep : [project.grep];\n const grepStrings = patterns.map((p) => p.source);\n if (grepStrings.length > 0) {\n config.grep = grepStrings;\n }\n }\n\n // Extract use options (browser, viewport, etc.)\n if (project.use) {\n const useOptions = this.extractUseOptions(project.use);\n if (Object.keys(useOptions).length > 0) {\n config.use = useOptions;\n }\n }\n\n return config;\n }\n\n /**\n * Extract use options from project.use\n */\n private extractUseOptions(use: PlaywrightFullProject['use']): ProjectUseOptions {\n const options: ProjectUseOptions = {};\n\n if (!use) return options;\n\n // Extract channel first (needed for browser inference)\n if (this.isNonEmptyString(use.channel)) {\n options.channel = use.channel;\n }\n\n // Determine browserName - explicit or inferred from channel\n const browserName = this.resolveBrowserName(use.browserName, use.defaultBrowserType, use.channel);\n if (browserName) {\n options.browserName = browserName;\n }\n\n if (typeof use.headless === 'boolean') {\n options.headless = use.headless;\n }\n\n if (use.viewport && typeof use.viewport === 'object') {\n options.viewport = {\n width: use.viewport.width,\n height: use.viewport.height,\n };\n } else if (use.viewport === null) {\n options.viewport = null;\n }\n\n if (this.isNonEmptyString(use.baseURL)) {\n options.baseURL = use.baseURL;\n }\n\n // Artifact capture settings\n const trace = this.normalizeArtifactMode(use.trace);\n if (trace) {\n options.trace = trace;\n }\n\n const screenshot = this.normalizeArtifactMode(use.screenshot);\n if (screenshot) {\n options.screenshot = screenshot;\n }\n\n const video = this.normalizeArtifactMode(use.video);\n if (video) {\n options.video = video;\n }\n\n // Device emulation\n if (typeof use.isMobile === 'boolean') {\n options.isMobile = use.isMobile;\n }\n\n // Locale\n if (this.isNonEmptyString(use.locale)) {\n options.locale = use.locale;\n }\n\n return options;\n }\n\n /**\n * Normalize artifact mode (trace/screenshot/video can be string or { mode: string })\n */\n private normalizeArtifactMode(value: unknown): string | undefined {\n if (!value) return undefined;\n if (typeof value === 'string' && value !== 'off') {\n return value;\n }\n if (typeof value === 'object' && value !== null && 'mode' in value) {\n const mode = (value as { mode: string }).mode;\n if (mode && mode !== 'off') {\n return mode;\n }\n }\n return undefined;\n }\n\n /**\n * Resolve browserName from explicit value, defaultBrowserType (device presets), or channel\n * Priority: browserName > defaultBrowserType > channel inference\n */\n private resolveBrowserName(\n browserName: string | undefined,\n defaultBrowserType: string | undefined,\n channel: string | undefined\n ): 'chromium' | 'firefox' | 'webkit' | undefined {\n const validBrowsers = ['chromium', 'firefox', 'webkit'];\n\n // 1. Use explicit browserName if valid\n if (browserName && validBrowsers.includes(browserName)) {\n return browserName as 'chromium' | 'firefox' | 'webkit';\n }\n\n // 2. Use defaultBrowserType from device presets (e.g., devices['Desktop Chrome'])\n if (defaultBrowserType && validBrowsers.includes(defaultBrowserType)) {\n return defaultBrowserType as 'chromium' | 'firefox' | 'webkit';\n }\n\n // 3. Infer from channel\n if (channel) {\n // Chrome-based channels use chromium engine\n if (\n [\n 'chrome',\n 'chrome-beta',\n 'chrome-dev',\n 'chrome-canary',\n 'msedge',\n 'msedge-beta',\n 'msedge-dev',\n 'msedge-canary',\n ].includes(channel)\n ) {\n return 'chromium';\n }\n }\n\n return undefined;\n }\n\n /**\n * Extract unique browsers from all projects\n */\n private extractBrowsersFromProjects(projects: PlaywrightFullProject[]): string[] {\n const browsers = new Set<string>();\n\n for (const project of projects) {\n const browserName = this.resolveBrowserName(\n project.use?.browserName,\n project.use?.defaultBrowserType,\n project.use?.channel\n );\n if (browserName) {\n browsers.add(browserName);\n }\n }\n\n return Array.from(browsers);\n }\n\n /**\n * Build skeleton from Suite\n */\n buildSkeleton(suite: PlaywrightSuite): RunSkeleton {\n const totalTests = suite.allTests().length;\n const suites = this.buildSuiteTree(suite);\n\n return {\n totalTests,\n suites,\n };\n }\n\n /**\n * Recursively build suite tree\n */\n private buildSuiteTree(suite: PlaywrightSuite): SkeletonSuite[] {\n const suites: SkeletonSuite[] = [];\n\n // Process child suites\n for (const childSuite of suite.suites) {\n const skeletonSuite: SkeletonSuite = {\n title: childSuite.title,\n type: childSuite.type === 'file' ? 'file' : 'describe',\n tests: childSuite.tests.map((test) => this.buildSkeletonTest(test)),\n };\n\n // Add file for file-type suites\n if (childSuite.type === 'file' && childSuite.location) {\n skeletonSuite.file = childSuite.location.file;\n }\n\n // Add location if available\n if (childSuite.location) {\n skeletonSuite.location = {\n file: childSuite.location.file,\n line: childSuite.location.line,\n column: childSuite.location.column,\n };\n }\n\n // Recursively build nested suites\n if (childSuite.suites.length > 0) {\n skeletonSuite.suites = this.buildSuiteTree(childSuite);\n }\n\n suites.push(skeletonSuite);\n }\n\n return suites;\n }\n\n /**\n * Build skeleton test from TestCase\n */\n private buildSkeletonTest(test: PlaywrightTestCase): SkeletonTest {\n const skeletonTest: SkeletonTest = {\n testId: test.id,\n title: test.title,\n location: {\n file: test.location.file,\n line: test.location.line,\n column: test.location.column,\n },\n };\n\n // Add tags if present\n if (test.tags && test.tags.length > 0) {\n skeletonTest.tags = test.tags;\n }\n\n // Add expected status\n if (test.expectedStatus) {\n skeletonTest.expectedStatus = test.expectedStatus;\n }\n\n // Add annotations if present\n if (test.annotations && test.annotations.length > 0) {\n skeletonTest.annotations = test.annotations.map((ann) => ({\n type: ann.type,\n description: ann.description,\n }));\n }\n\n return skeletonTest;\n }\n}\n","/**\n * Metadata collection main exports and aggregator\n */\n\nimport type {\n MetadataCollectionOptions,\n MetadataCollectionResult,\n MetadataCollectionSummary,\n CompleteMetadata,\n GitMetadata,\n CIMetadata,\n SystemMetadata,\n PlaywrightMetadata,\n} from './types.js';\nimport { GitMetadataCollector } from './git.js';\nimport { CIMetadataCollector } from './ci.js';\nimport { SystemMetadataCollector } from './system.js';\nimport { PlaywrightMetadataCollector } from './playwright.js';\n\n/**\n * Default metadata collection options\n * @internal\n */\nconst DEFAULT_METADATA_OPTIONS: Required<MetadataCollectionOptions> = {\n timeout: 5000,\n debug: false,\n};\n\n/**\n * Playwright FullConfig interface (minimal subset for metadata collection)\n */\ninterface PlaywrightFullConfig {\n fullyParallel?: boolean;\n workers?: number;\n shard?: {\n current: number;\n total: number;\n } | null;\n projects?: Array<{\n name?: string;\n }>;\n}\n\n/**\n * Playwright Suite interface (minimal subset for skeleton building)\n */\ninterface PlaywrightSuite {\n title: string;\n type: 'root' | 'project' | 'file' | 'describe';\n location?: {\n file: string;\n line: number;\n column: number;\n };\n suites: PlaywrightSuite[];\n tests: PlaywrightTestCase[];\n allTests(): PlaywrightTestCase[];\n}\n\n/**\n * Playwright TestCase interface (minimal subset for skeleton building)\n */\ninterface PlaywrightTestCase {\n id: string;\n title: string;\n location: {\n file: string;\n line: number;\n column: number;\n };\n tags: string[];\n expectedStatus: 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted';\n annotations: Array<{\n type: string;\n description?: string;\n }>;\n}\n\n/**\n * Main metadata aggregator class\n * Orchestrates collection from all metadata collectors\n */\nexport class MetadataAggregator {\n private options: Required<MetadataCollectionOptions>;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n private collectors: Array<{ collectWithResult: () => Promise<MetadataCollectionResult<any>> }> = [];\n private playwrightCollector?: PlaywrightMetadataCollector;\n\n constructor(options: MetadataCollectionOptions = {}) {\n this.options = { ...DEFAULT_METADATA_OPTIONS, ...options };\n }\n\n /**\n * Register a metadata collector\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n registerCollector(collector: { collectWithResult: () => Promise<MetadataCollectionResult<any>> }): void {\n this.collectors.push(collector);\n\n // Keep reference to PlaywrightMetadataCollector for skeleton building\n if (collector instanceof PlaywrightMetadataCollector) {\n this.playwrightCollector = collector;\n }\n }\n\n /**\n * Build skeleton from Suite (if PlaywrightMetadataCollector is registered with a suite)\n */\n buildSkeleton(suite: PlaywrightSuite): CompleteMetadata['skeleton'] {\n if (!this.playwrightCollector) {\n return undefined;\n }\n\n return this.playwrightCollector.buildSkeleton(suite);\n }\n\n /**\n * Collect metadata from all registered collectors\n * Uses Promise.allSettled for error isolation\n */\n async collectAll(): Promise<MetadataCollectionSummary> {\n const startTime = Date.now();\n\n // Collect from all collectors in parallel with error isolation\n const settledResults = await Promise.allSettled(\n this.collectors.map((collector) =>\n this.withTimeout(collector.collectWithResult(), this.options.timeout, 'Metadata collection')\n )\n );\n\n // Process results\n const results: MetadataCollectionResult[] = [];\n const metadata: CompleteMetadata = {};\n\n for (const settledResult of settledResults) {\n if (settledResult.status === 'fulfilled') {\n const result = settledResult.value;\n results.push(result);\n\n // Aggregate metadata by collector type\n this.aggregateMetadata(metadata, result);\n } else {\n // Handle rejected promises (shouldn't happen due to error handling in collectors)\n const error = settledResult.reason;\n console.warn('⚠️ TestDino: Metadata collector promise rejected:', error);\n\n results.push({\n data: {},\n success: false,\n error: error instanceof Error ? error.message : String(error),\n duration: 0,\n collector: 'unknown',\n });\n }\n }\n\n const totalDuration = Date.now() - startTime;\n const successCount = results.filter((r) => r.success).length;\n const failureCount = results.length - successCount;\n\n return {\n metadata,\n results,\n totalDuration,\n successCount,\n failureCount,\n };\n }\n\n /**\n * Aggregate individual collector results into complete metadata\n */\n private aggregateMetadata(metadata: CompleteMetadata, result: MetadataCollectionResult): void {\n const { collector, data } = result;\n\n switch (collector) {\n case 'git':\n metadata.git = data as GitMetadata;\n break;\n case 'ci':\n metadata.ci = data as CIMetadata;\n break;\n case 'system':\n metadata.system = data as SystemMetadata;\n break;\n case 'playwright':\n metadata.playwright = data as PlaywrightMetadata;\n break;\n default:\n if (this.options.debug) {\n console.warn(`⚠️ TestDino: Unknown metadata collector: ${collector}`);\n }\n }\n }\n\n /**\n * Utility method to run operation with timeout\n */\n private async withTimeout<T>(promise: Promise<T>, timeoutMs: number, operation: string): Promise<T> {\n const timeoutPromise = new Promise<never>((_, reject) => {\n setTimeout(() => {\n reject(new Error(`${operation} timed out after ${timeoutMs}ms`));\n }, timeoutMs);\n });\n\n return Promise.race([promise, timeoutPromise]);\n }\n}\n\n// Re-export types\nexport type {\n MetadataCollectionOptions,\n MetadataCollectionResult,\n MetadataCollectionSummary,\n CompleteMetadata,\n GitMetadata,\n CIMetadata,\n SystemMetadata,\n PlaywrightMetadata,\n PRMetadata,\n ShardMetadata,\n MetadataCollector,\n RunSkeleton,\n SkeletonSuite,\n SkeletonTest,\n} from './types.js';\n\n// Re-export base class\nexport { BaseMetadataCollector } from './base.js';\n\n// Re-export collectors\nexport { GitMetadataCollector } from './git.js';\nexport { CIMetadataCollector } from './ci.js';\nexport { SystemMetadataCollector } from './system.js';\nexport { PlaywrightMetadataCollector } from './playwright.js';\n\n/**\n * Factory function to create a metadata aggregator with all collectors registered\n *\n * @param playwrightConfig Optional Playwright FullConfig for enhanced metadata collection\n * @param playwrightSuite Optional Playwright Suite for skeleton building\n * @returns MetadataAggregator instance with all collectors registered\n */\nexport function createMetadataCollector(\n playwrightConfig?: PlaywrightFullConfig,\n playwrightSuite?: PlaywrightSuite\n): MetadataAggregator {\n const aggregator = new MetadataAggregator();\n\n // Register all collectors\n aggregator.registerCollector(new GitMetadataCollector());\n aggregator.registerCollector(new CIMetadataCollector());\n aggregator.registerCollector(new SystemMetadataCollector());\n aggregator.registerCollector(\n new PlaywrightMetadataCollector({\n config: playwrightConfig,\n suite: playwrightSuite,\n })\n );\n\n return aggregator;\n}\n","/**\n * SAS Token client for requesting Azure Blob Storage upload tokens\n */\n\nimport axios, { type AxiosInstance } from 'axios';\nimport type { SASTokenResponse, SASTokenServerResponse } from './types.js';\nimport { sleep } from '../utils/index.js';\n\nexport interface SASTokenClientOptions {\n token: string;\n serverUrl: string;\n maxRetries?: number;\n retryDelay?: number;\n}\n\n/**\n * Client for requesting SAS tokens from TestDino server\n */\nexport class SASTokenClient {\n private client: AxiosInstance;\n private options: Required<SASTokenClientOptions>;\n\n constructor(options: SASTokenClientOptions) {\n this.options = {\n maxRetries: 2,\n retryDelay: 1000,\n ...options,\n };\n\n // Use x-api-key header for storage endpoint (different from Bearer token)\n this.client = axios.create({\n baseURL: this.options.serverUrl,\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.options.token,\n },\n timeout: 10000,\n });\n }\n\n /**\n * Request SAS token for artifact uploads\n * @param expiryHours - Token validity duration (1-48 hours, default 48)\n * @returns SAS token response with upload instructions\n */\n async requestToken(expiryHours: number = 48): Promise<SASTokenResponse> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < this.options.maxRetries; attempt++) {\n try {\n const response = await this.client.post<SASTokenServerResponse>('/api/storage/token', undefined, {\n params: {\n expiryHours,\n permissions: 'write,create',\n },\n });\n\n if (!response.data.success) {\n throw new Error(response.data.message || 'SAS token request failed');\n }\n\n return response.data.data;\n } catch (error) {\n lastError = new Error(this.getErrorMessage(error));\n\n if (attempt < this.options.maxRetries - 1) {\n // Wait before retry with exponential backoff\n const delay = this.options.retryDelay * Math.pow(2, attempt);\n await sleep(delay);\n }\n }\n }\n\n throw lastError || new Error('Failed to request SAS token');\n }\n\n /**\n * Extract error message from various error types\n */\n private getErrorMessage(error: unknown): string {\n if (axios.isAxiosError(error)) {\n // Handle specific HTTP status codes\n if (error.response?.status === 401) {\n return 'Invalid API key for artifact uploads';\n }\n if (error.response?.status === 403) {\n return 'API key does not have write permission for uploads';\n }\n if (error.response?.status === 429) {\n return 'Rate limit exceeded for SAS token requests';\n }\n return error.response?.data?.message || error.message;\n }\n if (error instanceof Error) {\n return error.message;\n }\n return String(error);\n }\n}\n","/**\n * Artifact uploader for Azure Blob Storage\n * Uploads test artifacts (screenshots, videos, traces) using SAS tokens\n */\n\nimport { createReadStream, statSync } from 'fs';\nimport { basename, extname } from 'path';\nimport axios from 'axios';\nimport type { SASTokenResponse, UploadResult, AttachmentForUpload } from './types.js';\nimport { sleep } from '../utils/index.js';\n\nexport interface ArtifactUploaderOptions {\n /** Upload timeout per file in milliseconds */\n timeout?: number;\n /** Maximum retries per file */\n maxRetries?: number;\n /** Debug mode for logging */\n debug?: boolean;\n}\n\n/**\n * Uploads artifacts to Azure Blob Storage using SAS tokens\n */\nexport class ArtifactUploader {\n private sasToken: SASTokenResponse;\n private options: Required<ArtifactUploaderOptions>;\n\n constructor(sasToken: SASTokenResponse, options: ArtifactUploaderOptions = {}) {\n this.sasToken = sasToken;\n this.options = {\n timeout: 60000,\n maxRetries: 2,\n debug: false,\n ...options,\n };\n }\n\n /**\n * Upload a single file to Azure Blob Storage\n * @param attachment - Attachment with path and content type\n * @param testId - Test identifier for organizing uploads\n * @returns Upload result with Azure URL or error\n */\n async uploadFile(attachment: AttachmentForUpload, testId: string): Promise<UploadResult> {\n const startTime = Date.now();\n const fileName = basename(attachment.path);\n\n try {\n // Validate file exists and get size\n const stats = statSync(attachment.path);\n const fileSize = stats.size;\n\n // Check file size limit\n if (fileSize > this.sasToken.maxSize) {\n return {\n name: attachment.name,\n success: false,\n error: `File size ${fileSize} bytes exceeds maximum ${this.sasToken.maxSize} bytes`,\n fileSize,\n duration: Date.now() - startTime,\n };\n }\n\n // Check file type is allowed\n const extension = extname(fileName).slice(1).toLowerCase();\n const allowedTypes = this.sasToken.allowedFileTypes;\n if (allowedTypes.length > 0 && !allowedTypes.includes(extension)) {\n return {\n name: attachment.name,\n success: false,\n error: `File extension '.${extension}' not in allowed types: ${allowedTypes.join(', ')}`,\n fileSize,\n duration: Date.now() - startTime,\n };\n }\n\n // Build upload URL: baseUrl/pathPrefix/testId/fileName\n const uploadUrl = this.buildUploadUrl(testId, fileName);\n\n // Upload with retries\n await this.uploadWithRetry(attachment.path, uploadUrl, attachment.contentType);\n\n // Build public URL (without SAS token query params)\n const publicUrl = this.buildPublicUrl(testId, fileName);\n\n if (this.options.debug) {\n console.log(`📤 Uploaded: ${attachment.name} → ${publicUrl}`);\n }\n\n return {\n name: attachment.name,\n success: true,\n uploadUrl: publicUrl,\n fileSize,\n duration: Date.now() - startTime,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n if (this.options.debug) {\n console.warn(`⚠️ Upload failed: ${attachment.name} - ${errorMessage}`);\n }\n\n return {\n name: attachment.name,\n success: false,\n error: errorMessage,\n duration: Date.now() - startTime,\n };\n }\n }\n\n /**\n * Upload multiple attachments in parallel\n * @param attachments - Array of attachments to upload\n * @param testId - Test identifier for organizing uploads\n * @returns Array of upload results\n */\n async uploadAll(attachments: AttachmentForUpload[], testId: string): Promise<UploadResult[]> {\n if (attachments.length === 0) {\n return [];\n }\n\n // Filter to only attachments with valid paths\n const validAttachments = attachments.filter((a) => a.path);\n\n if (validAttachments.length === 0) {\n return [];\n }\n\n // Upload all in parallel using Promise.allSettled\n const results = await Promise.allSettled(validAttachments.map((attachment) => this.uploadFile(attachment, testId)));\n\n // Convert settled results to upload results\n return results.map((result, index) => {\n if (result.status === 'fulfilled') {\n return result.value;\n }\n // Promise rejection (shouldn't happen as uploadFile catches errors)\n return {\n name: validAttachments[index].name,\n success: false,\n error: result.reason?.message || 'Upload failed',\n };\n });\n }\n\n /**\n * Build the full upload URL with SAS token\n * Format: containerUrl/blobPath/uniqueId/testId/fileName?sasToken\n */\n private buildUploadUrl(testId: string, fileName: string): string {\n const { containerUrl, blobPath, uniqueId, sasToken } = this.sasToken;\n const path = `${blobPath}/${uniqueId}/${this.sanitizeTestId(testId)}/${fileName}`;\n return `${containerUrl}/${path}?${sasToken}`;\n }\n\n /**\n * Build public URL without SAS token (for storage in events)\n * Format: containerUrl/blobPath/uniqueId/testId/fileName\n */\n private buildPublicUrl(testId: string, fileName: string): string {\n const { containerUrl, blobPath, uniqueId } = this.sasToken;\n const path = `${blobPath}/${uniqueId}/${this.sanitizeTestId(testId)}/${fileName}`;\n return `${containerUrl}/${path}`;\n }\n\n /**\n * Sanitize test ID for use in URL path\n */\n private sanitizeTestId(testId: string): string {\n // Replace characters that are problematic in URLs\n return testId.replace(/[^a-zA-Z0-9-_]/g, '_');\n }\n\n /**\n * Upload file with retry logic\n */\n private async uploadWithRetry(filePath: string, uploadUrl: string, contentType: string): Promise<void> {\n let lastError: Error | null = null;\n\n for (let attempt = 0; attempt < this.options.maxRetries; attempt++) {\n try {\n await this.doUpload(filePath, uploadUrl, contentType);\n return;\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n if (attempt < this.options.maxRetries - 1) {\n // Wait before retry with exponential backoff\n const delay = 1000 * Math.pow(2, attempt);\n await sleep(delay);\n }\n }\n }\n\n throw lastError || new Error('Upload failed after retries');\n }\n\n /**\n * Perform actual file upload to Azure\n */\n private async doUpload(filePath: string, uploadUrl: string, contentType: string): Promise<void> {\n const fileStream = createReadStream(filePath);\n const stats = statSync(filePath);\n\n await axios.put(uploadUrl, fileStream, {\n headers: {\n 'Content-Type': contentType,\n 'Content-Length': stats.size,\n 'x-ms-blob-type': 'BlockBlob',\n },\n timeout: this.options.timeout,\n maxContentLength: Infinity,\n maxBodyLength: Infinity,\n });\n }\n\n /**\n * Check if SAS token is still valid\n */\n isTokenValid(): boolean {\n const expiresAt = new Date(this.sasToken.expiresAt).getTime();\n const now = Date.now();\n // Consider token invalid if it expires within 5 minutes\n return expiresAt > now + 5 * 60 * 1000;\n }\n\n /**\n * Get the unique ID for this upload session\n */\n getSessionId(): string {\n return this.sasToken.uniqueId;\n }\n}\n","/**\n * Incremental coverage merger using istanbul-lib-coverage.\n * Merges per-test coverage fragments as tests complete.\n * See docs/CODE_COVERAGE.md for design decisions and algorithms.\n */\n\nimport istanbulCoverage from 'istanbul-lib-coverage';\nimport picomatch from 'picomatch';\nimport { normalizePath } from '../utils/index.js';\nimport type { CoverageFragment, CoverageSummary, FileCoverage, CoverageMetric, IstanbulCoverageMap } from './types.js';\n\n/**\n * Type adapter: our IstanbulCoverageMap → istanbul-lib-coverage CoverageMapData.\n * Both represent the same JSON shape; the double cast is isolated here.\n */\nfunction toIstanbulMapData(map: IstanbulCoverageMap): istanbulCoverage.CoverageMapData {\n return map as unknown as istanbulCoverage.CoverageMapData;\n}\n\n/**\n * Type adapter: istanbul-lib-coverage CoverageMapData → our IstanbulCoverageMap.\n */\nfunction fromIstanbulMapData(data: istanbulCoverage.CoverageMapData): IstanbulCoverageMap {\n return data as unknown as IstanbulCoverageMap;\n}\n\n/**\n * Options for filtering coverage files during merge.\n */\nexport interface CoverageMergerOptions {\n /** Glob patterns — only files whose path matches a pattern are included */\n include?: string[];\n /** Glob patterns — files whose path matches a pattern are excluded */\n exclude?: string[];\n /** Error callback for merge failures. Falls back to console.warn if not provided. */\n onError?: (message: string) => void;\n}\n\n/**\n * Incremental coverage merger.\n * Merges per-test Istanbul coverage fragments into a single coverage map.\n */\nexport class CoverageMerger {\n private coverageMap = istanbulCoverage.createCoverageMap({});\n private hasData = false;\n private includePatterns?: string[];\n private excludePatterns?: string[];\n private onError?: (message: string) => void;\n\n constructor(options?: CoverageMergerOptions) {\n this.includePatterns = options?.include;\n this.excludePatterns = options?.exclude;\n this.onError = options?.onError;\n }\n\n /**\n * Merge a coverage fragment from a completed test.\n * The fragment is merged directly and can be GC'd immediately.\n */\n addFragment(fragment: CoverageFragment): void {\n if (!fragment.istanbul) return;\n\n try {\n const filtered = this.filterCoverageMap(fragment.istanbul);\n this.coverageMap.merge(toIstanbulMapData(filtered));\n if (this.coverageMap.files().length > 0) {\n this.hasData = true;\n }\n } catch (error) {\n // Malformed coverage data — report and skip\n const msg = `[TestDino] Failed to merge coverage fragment: ${error instanceof Error ? error.message : String(error)}`;\n if (this.onError) {\n this.onError(msg);\n } else {\n console.warn(msg);\n }\n }\n }\n\n /**\n * Filter files from a coverage map based on include/exclude glob patterns.\n */\n private filterCoverageMap(coverageMap: IstanbulCoverageMap): IstanbulCoverageMap {\n const hasInclude = this.includePatterns && this.includePatterns.length > 0;\n const hasExclude = this.excludePatterns && this.excludePatterns.length > 0;\n\n if (!hasInclude && !hasExclude) return coverageMap;\n\n const isExcluded = hasExclude ? picomatch(this.excludePatterns!) : undefined;\n const isIncluded = hasInclude ? picomatch(this.includePatterns!) : undefined;\n\n const filtered: IstanbulCoverageMap = {};\n\n for (const [filePath, fileCoverage] of Object.entries(coverageMap)) {\n if (isExcluded && isExcluded(filePath)) {\n continue;\n }\n\n if (isIncluded && !isIncluded(filePath)) {\n continue;\n }\n\n filtered[filePath] = fileCoverage;\n }\n\n return filtered;\n }\n\n /**\n * Whether any coverage data has been collected.\n */\n get hasCoverage(): boolean {\n return this.hasData;\n }\n\n /**\n * Compute aggregate summary metrics from the merged coverage map.\n */\n computeSummary(): CoverageSummary {\n const globalSummary = this.coverageMap.getCoverageSummary();\n return {\n lines: extractMetric(globalSummary.lines),\n branches: extractMetric(globalSummary.branches),\n functions: extractMetric(globalSummary.functions),\n statements: extractMetric(globalSummary.statements),\n };\n }\n\n /**\n * Compute per-file coverage metrics.\n * Normalizes file paths to be relative to git root.\n */\n computeFileCoverage(gitRoot?: string): FileCoverage[] {\n const root = gitRoot || process.cwd();\n\n return this.coverageMap.files().map((filePath: string) => {\n const fileCoverage = this.coverageMap.fileCoverageFor(filePath);\n const fileSummary = fileCoverage.toSummary();\n\n // Normalize path to be relative to git root\n const normalizedPath = normalizePath(filePath, root);\n\n return {\n path: normalizedPath,\n lines: extractMetric(fileSummary.lines),\n branches: extractMetric(fileSummary.branches),\n functions: extractMetric(fileSummary.functions),\n statements: extractMetric(fileSummary.statements),\n };\n });\n }\n\n /**\n * Get the raw merged coverage map (for local report generation or compact extraction).\n */\n getRawCoverageMap(): istanbulCoverage.CoverageMap {\n return this.coverageMap;\n }\n\n /**\n * Get the merged coverage map as a plain JSON object.\n */\n toJSON(): IstanbulCoverageMap {\n return fromIstanbulMapData(this.coverageMap.toJSON());\n }\n}\n\ninterface IstanbulSummaryMetric {\n total: number;\n covered: number;\n skipped: number;\n pct: number;\n}\n\nfunction extractMetric(metric: IstanbulSummaryMetric): CoverageMetric {\n return {\n total: metric.total,\n covered: metric.covered,\n pct: metric.pct,\n };\n}\n","/**\n * Compact count extractor for cross-shard coverage merging.\n * Extracts minimal hit count data from merged coverage maps.\n * See docs/CODE_COVERAGE.md for design decisions and algorithms.\n */\n\nimport { createHash } from 'crypto';\nimport { normalizePath } from '../utils/index.js';\nimport type { CompactCoverageData, CompactFileCounts, IstanbulFileCoverage } from './types.js';\n\n/**\n * Extract compact hit counts from a merged coverage map JSON.\n *\n * @param coverageMapJSON - Raw coverage map data from CoverageMerger.toJSON()\n * @param gitRoot - Git repository root for path normalization (optional)\n * @returns Compact coverage data suitable for cross-shard upload\n */\nexport function extractCompactCounts(\n coverageMapJSON: Record<string, IstanbulFileCoverage>,\n gitRoot?: string\n): CompactCoverageData {\n const files: Record<string, CompactFileCounts> = {};\n let fileCount = 0;\n\n for (const [filePath, fileCoverage] of Object.entries(coverageMapJSON)) {\n const normalizedPath = normalizePath(filePath, gitRoot);\n\n files[normalizedPath] = {\n s: fileCoverage.s,\n f: fileCoverage.f,\n b: fileCoverage.b,\n totals: {\n s: Object.keys(fileCoverage.statementMap || {}).length,\n f: Object.keys(fileCoverage.fnMap || {}).length,\n b: countBranchPaths(fileCoverage.branchMap || {}),\n },\n shapeHash: computeShapeHash(fileCoverage),\n };\n fileCount++;\n }\n\n return { files, fileCount };\n}\n\n/**\n * Count total branch paths across all branches in a file.\n * Each branch (if/else, ternary, switch) can have multiple paths.\n */\nfunction countBranchPaths(branchMap: IstanbulFileCoverage['branchMap']): number {\n let total = 0;\n for (const branch of Object.values(branchMap)) {\n total += (branch.locations || []).length;\n }\n return total;\n}\n\n/**\n * Compute a shape hash from the structural maps of a file.\n * Used to detect build mismatches across shards — if two shards produce\n * different shape hashes for the same file, the builds were different.\n *\n * The hash encodes the count of statements, functions, and branch paths,\n * plus the per-branch path counts for granularity.\n */\nfunction computeShapeHash(fileCoverage: IstanbulFileCoverage): string {\n const branchMap = fileCoverage.branchMap || {};\n const shape = {\n s: Object.keys(fileCoverage.statementMap || {}).length,\n f: Object.keys(fileCoverage.fnMap || {}).length,\n b: countBranchPaths(branchMap),\n bp: Object.values(branchMap).map((b) => (b.locations || []).length),\n };\n return createHash('sha256').update(JSON.stringify(shape)).digest('hex').slice(0, 12);\n}\n","/**\n * Istanbul HTML report generator.\n * Uses the standard istanbul-reports package to generate a full HTML report\n * with source code display, line-by-line coverage, and drill-down navigation.\n */\n\nimport { createContext } from 'istanbul-lib-report';\nimport { create as createReporter } from 'istanbul-reports';\nimport { mkdir, rm } from 'fs/promises';\nimport type { CoverageMerger } from './merger.js';\n\nexport interface IstanbulHtmlReportOptions {\n outputDir: string;\n}\n\n/**\n * Generate a full Istanbul HTML coverage report in the specified output directory.\n * Returns the path to the generated index.html entry point.\n */\nexport async function generateIstanbulHtmlReport(\n coverageMerger: CoverageMerger,\n options: IstanbulHtmlReportOptions\n): Promise<string> {\n // Clean output directory to remove stale files from previous runs\n await rm(options.outputDir, { recursive: true, force: true }).catch(() => {});\n await mkdir(options.outputDir, { recursive: true });\n\n // Get the raw coverage map from the merger\n const coverageMap = coverageMerger.getRawCoverageMap();\n\n // Create Istanbul report context\n const context = createContext({\n dir: options.outputDir,\n watermarks: {\n statements: [50, 80],\n functions: [50, 80],\n branches: [50, 80],\n lines: [50, 80],\n },\n coverageMap,\n });\n\n // Create HTML reporter\n const reporter = createReporter('html', {\n skipEmpty: false,\n subdir: '',\n });\n\n // Generate the report\n reporter.execute(context);\n\n // Return the path to the main index file\n return `${options.outputDir}/index.html`;\n}\n","/**\n * Coverage fixtures for automatic Istanbul coverage collection.\n *\n * The fixture runs automatically (auto: true) for every test:\n * 1. Test executes normally\n * 2. After test: extracts window.__coverage__ from the page\n * 3. Attaches coverage JSON to testInfo for the reporter to pick up\n *\n * Gracefully no-ops when the app is not instrumented (window.__coverage__ absent).\n */\n\nimport { test as base, expect, type Page, type TestInfo } from '@playwright/test';\nimport type { IstanbulCoverageMap, CoverageFragment } from './types.js';\n\n// Timeout for extracting Istanbul coverage from the page (30 seconds)\nconst COVERAGE_EXTRACT_TIMEOUT_MS = 30_000;\n\ntype CoverageFixtures = {\n _testdinoCoverage: void;\n};\n\n/**\n * Extract Istanbul coverage from a Playwright Page.\n * Returns null if window.__coverage__ does not exist or if extraction times out.\n */\nexport async function extractCoverageFromPage(\n page: Page,\n timeoutMs: number = COVERAGE_EXTRACT_TIMEOUT_MS\n): Promise<IstanbulCoverageMap | null> {\n return Promise.race([\n page.evaluate(() => (globalThis as unknown as { __coverage__?: IstanbulCoverageMap }).__coverage__ ?? null),\n new Promise<null>((resolve) => setTimeout(() => resolve(null), timeoutMs)),\n ]).catch(() => null);\n}\n\n/**\n * Attach an Istanbul coverage map to a TestInfo as a JSON attachment.\n * The reporter picks up the attachment by name ('testdino-coverage').\n */\nexport async function attachCoverageToTestInfo(testInfo: TestInfo, coverage: IstanbulCoverageMap): Promise<void> {\n const fragment: CoverageFragment = {\n istanbul: coverage,\n };\n\n await testInfo.attach('testdino-coverage', {\n body: JSON.stringify(fragment),\n contentType: 'application/json',\n });\n}\n\nexport const coverageFixtures = {\n _testdinoCoverage: [\n async ({ page }: { page: Page }, use: () => Promise<void>, testInfo: TestInfo) => {\n // Test executes here\n await use();\n\n // Extract Istanbul coverage (any browser, if app is instrumented).\n const istanbulCoverage = await extractCoverageFromPage(page);\n\n // Attach coverage data to test result for the reporter to pick up\n if (istanbulCoverage) {\n await attachCoverageToTestInfo(testInfo, istanbulCoverage);\n }\n },\n { auto: true },\n ],\n};\n\n/**\n * Pre-extended test with coverage fixtures for direct import.\n *\n * Usage:\n * ```typescript\n * import { test, expect } from '@testdino/playwright';\n *\n * test('my test', async ({ page }) => {\n * // coverage collected automatically\n * });\n * ```\n */\nexport const test = base.extend<CoverageFixtures>(\n coverageFixtures as Parameters<typeof base.extend<CoverageFixtures>>[0]\n);\nexport { expect };\n","/**\n * Reporter logging and summary rendering utilities\n */\n\nimport type { FullResult } from '@playwright/test/reporter';\nimport type { CoverageDataEvent, RunMetadata } from '../types/index.js';\n\n// Dynamic import for chalk (ESM-only package)\nlet chalkPromise: Promise<typeof import('chalk').default> | null = null;\n\n/**\n * Dynamically import chalk (ESM-only package) for CJS compatibility only once per process using memoization.\n *\n * @returns Promise resolving to the chalk default export\n * @throws Never - returns plain text fallback on import failure\n */\nasync function getChalk(): Promise<typeof import('chalk').default> {\n if (!chalkPromise) {\n chalkPromise = import('chalk')\n .then((m) => m.default)\n .catch((error) => {\n console.warn('⚠️ TestDino: Failed to load chalk, using plain text output');\n console.debug('Chalk import error:', error.message);\n // Return no-op chalk that returns strings unchanged\n return {\n green: (s: string) => s,\n yellow: (s: string) => s,\n red: (s: string) => s,\n blue: (s: string) => s,\n cyan: (s: string) => s,\n dim: (s: string) => s,\n bold: (s: string) => s,\n } as unknown as typeof import('chalk').default;\n });\n }\n return chalkPromise;\n}\n\nexport interface ReporterLogOptions {\n debug: boolean;\n}\n\nexport interface RunSummaryData {\n runId: string;\n runMetadata: RunMetadata | null;\n testCounts: {\n passed: number;\n failed: number;\n skipped: number;\n timedOut: number;\n interrupted: number;\n flaky: number;\n retried: number;\n };\n totalTests: number;\n workerCount: number;\n projectNames: Set<string>;\n shardInfo?: { current: number; total: number };\n useHttpFallback: boolean;\n lastCoverageEvent: CoverageDataEvent | null;\n}\n\n// ── Formatting helpers (exported for testing) ──\n\nfunction stripAnsi(str: string): string {\n // eslint-disable-next-line no-control-regex\n return str.replace(/\\u001b\\[[0-9;]*m/g, '');\n}\n\nexport function pad(str: string, len: number): string {\n const vLen = stripAnsi(str).length;\n return vLen >= len ? str : str + ' '.repeat(len - vLen);\n}\n\nexport function padStart(str: string, len: number): string {\n const vLen = stripAnsi(str).length;\n return vLen >= len ? str : ' '.repeat(len - vLen) + str;\n}\n\nexport function formatDuration(ms: number): string {\n if (ms < 1000) return `${ms}ms`;\n const seconds = ms / 1000;\n if (seconds < 60) return `${seconds.toFixed(1)}s`;\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = (seconds % 60).toFixed(0);\n return `${minutes}m ${remainingSeconds}s`;\n}\n\nexport function shortenPath(filePath: string): string {\n const markers = ['/src/', '/lib/', '/app/'];\n for (const marker of markers) {\n const idx = filePath.indexOf(marker);\n if (idx !== -1) return filePath.slice(idx + 1);\n }\n const parts = filePath.split('/');\n return parts.length > 2 ? parts.slice(-2).join('/') : filePath;\n}\n\ntype ChalkInstance = typeof import('chalk').default;\n\nexport function colorPct(pct: number, chalk: ChalkInstance): string {\n const color = pct >= 80 ? chalk.green : pct >= 50 ? chalk.yellow : chalk.red;\n return color(pct === 100 ? '100%' : `${pct.toFixed(1)}%`);\n}\n\n// ── Summary rendering ──\n\nfunction printCoverageTableWithChalk(event: CoverageDataEvent, chalk: ChalkInstance): void {\n const { summary, files } = event;\n const row = (content: string) => ` ${chalk.dim('│')} ${content}`;\n const nameW = 30;\n const colW = 10;\n\n console.log(row(`${chalk.bold('Coverage')} ${chalk.dim(`${files.length} files`)}`));\n console.log(row(''));\n\n // Header\n console.log(\n row(\n ` ${chalk.dim(`${pad('File', nameW)}${padStart('Stmts', colW)}${padStart('Branch', colW)}${padStart('Funcs', colW)}${padStart('Lines', colW)}`)}`\n )\n );\n\n // Per-file rows\n for (const file of files) {\n const name = shortenPath(file.path);\n const short = name.length > nameW ? name.slice(0, nameW - 1) + '~' : name;\n console.log(\n row(\n ` ${pad(short, nameW)}` +\n padStart(colorPct(file.statements.pct, chalk), colW) +\n padStart(colorPct(file.branches.pct, chalk), colW) +\n padStart(colorPct(file.functions.pct, chalk), colW) +\n padStart(colorPct(file.lines.pct, chalk), colW)\n )\n );\n }\n\n // Separator + totals\n console.log(row(` ${chalk.dim('─'.repeat(nameW + colW * 4))}`));\n console.log(\n row(\n ` ${chalk.bold(pad('All files', nameW))}` +\n padStart(colorPct(summary.statements.pct, chalk), colW) +\n padStart(colorPct(summary.branches.pct, chalk), colW) +\n padStart(colorPct(summary.functions.pct, chalk), colW) +\n padStart(colorPct(summary.lines.pct, chalk), colW)\n )\n );\n}\n\nfunction printRunSummaryWithChalk(\n result: FullResult,\n streamingSuccess: boolean,\n data: RunSummaryData,\n chalk: ChalkInstance\n): void {\n const W = 72;\n const topBorder = ` ${chalk.dim(`┌${'─'.repeat(W)}┐`)}`;\n const bottomBorder = ` ${chalk.dim(`└${'─'.repeat(W)}┘`)}`;\n const divider = ` ${chalk.dim(`├${'─'.repeat(W)}┤`)}`;\n const row = (content: string) => ` ${chalk.dim('│')} ${content}`;\n\n console.log('');\n console.log(topBorder);\n console.log(row(chalk.bold('TestDino Run Summary')));\n console.log(divider);\n\n // Run & Git\n console.log(row(`${chalk.dim('Run')} ${data.runId}`));\n const git = data.runMetadata?.git;\n if (git?.branch || git?.commit?.hash) {\n const branch = git.branch ? chalk.cyan(git.branch) : '';\n const sha = git.commit?.hash ? chalk.dim(git.commit.hash.slice(0, 7)) : '';\n const sep = branch && sha ? ` ${chalk.dim('@')} ` : '';\n const msg = git.commit?.message ? ` ${chalk.dim(git.commit.message.split('\\n')[0].slice(0, 50))}` : '';\n console.log(row(`${chalk.dim('Git')} ${branch}${sep}${sha}${msg}`));\n }\n\n console.log(divider);\n\n // Test Results\n const statusColor = result.status === 'passed' ? chalk.green : result.status === 'failed' ? chalk.red : chalk.yellow;\n const statusLabel = result.status === 'timedout' ? 'timed out' : result.status;\n console.log(\n row(\n `${chalk.bold('Tests')} ${statusColor(statusLabel.toUpperCase())} ${chalk.dim(formatDuration(result.duration))}`\n )\n );\n\n const counts: string[] = [];\n if (data.testCounts.passed > 0) counts.push(chalk.green(`${data.testCounts.passed} passed`));\n if (data.testCounts.failed > 0) counts.push(chalk.red(`${data.testCounts.failed} failed`));\n if (data.testCounts.flaky > 0) counts.push(chalk.yellow(`${data.testCounts.flaky} flaky`));\n if (data.testCounts.skipped > 0) counts.push(chalk.dim(`${data.testCounts.skipped} skipped`));\n if (data.testCounts.timedOut > 0) counts.push(chalk.red(`${data.testCounts.timedOut} timed out`));\n if (data.testCounts.interrupted > 0) counts.push(chalk.yellow(`${data.testCounts.interrupted} interrupted`));\n const retriedStr = data.testCounts.retried > 0 ? ` ${chalk.dim(`(${data.testCounts.retried} retries)`)}` : '';\n console.log(row(` ${counts.join(chalk.dim(' · '))} ${chalk.dim(`of ${data.totalTests}`)}${retriedStr}`));\n\n // Workers & Shards\n console.log(divider);\n const shardStr = data.shardInfo ? `${data.shardInfo.current}/${data.shardInfo.total}` : '—';\n console.log(\n row(\n `${pad(`${chalk.dim('Workers')} ${data.workerCount > 0 ? data.workerCount : '—'}`, 28)}` +\n `${pad(`${chalk.dim('Shard')} ${shardStr}`, 28)}` +\n `${chalk.dim('Projects')} ${data.projectNames.size > 0 ? Array.from(data.projectNames).join(', ') : '—'}`\n )\n );\n\n // Streaming\n console.log(divider);\n const transport = data.useHttpFallback ? 'HTTP' : 'WebSocket';\n const streamIcon = streamingSuccess ? chalk.green('sent') : chalk.red('failed');\n console.log(row(`${chalk.bold('Stream')} ${streamIcon} ${chalk.dim(`via ${transport}`)}`));\n\n // Coverage\n if (data.lastCoverageEvent) {\n console.log(divider);\n printCoverageTableWithChalk(data.lastCoverageEvent, chalk);\n }\n\n console.log(bottomBorder);\n console.log('');\n}\n\n/**\n * Create a reporter log helper with consistent prefixes.\n * printRunSummary and printCoverageTable use dynamic chalk import (async).\n */\nexport const createReporterLog = (options: ReporterLogOptions) => ({\n success: (msg: string) => console.log(`✅ TestDino: ${msg}`),\n warn: (msg: string) => console.warn(`⚠️ TestDino: ${msg}`),\n error: (msg: string) => console.error(`❌ TestDino: ${msg}`),\n info: (msg: string) => console.log(`ℹ️ TestDino: ${msg}`),\n debug: (msg: string) => {\n if (options.debug) {\n console.log(`🔍 TestDino: ${msg}`);\n }\n },\n printRunSummary: async (result: FullResult, streamingSuccess: boolean, data: RunSummaryData): Promise<void> => {\n const chalk = await getChalk();\n printRunSummaryWithChalk(result, streamingSuccess, data, chalk);\n },\n printCoverageTable: async (event: CoverageDataEvent): Promise<void> => {\n const chalk = await getChalk();\n printCoverageTableWithChalk(event, chalk);\n },\n});\n\nexport type ReporterLog = ReturnType<typeof createReporterLog>;\n"],"mappings":"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","names":["readFile","version","readFile","test","axios","axios","axios","axios","istanbulCoverage","test"]}