RealtimeReporter API Reference¶
RealtimeReporter provides WebSocket-based real-time communication for test run events, including run lifecycle, test results, flaky detection, and report updates. It extends EventEmitter and manages WebSocket connections, progress tracking, and batch result broadcasting.
RealtimeReporter¶
Constructor¶
Creates a new RealtimeReporter instance. The WebSocket server is not started until initialize() is called.
Example¶
Instance Methods¶
initialize(server: Server): void¶
Initialize the WebSocket server by attaching to an existing HTTP server. Listens for upgrade requests on the /ws path. If already initialized, the previous instance is shut down first. New clients receive current running progress upon connection.
Parameters:
| Parameter | Type | Description |
|---|---|---|
server |
Server |
HTTP server instance to attach the WebSocket server to |
import { createServer } from 'http';
const server = createServer();
reporter.initialize(server);
server.listen(3001);
broadcastRunStarted(runId: string, version: string, totalTests?: number): void¶
Broadcast a run_started event to all connected clients. Initializes the internal progress tracking for the run.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
runId |
string |
Unique run identifier | |
version |
string |
Version of the test run | |
totalTests |
number |
0 |
Total number of tests expected in this run |
broadcastRunProgress(runId: string, progress: Partial<RunProgress>): void¶
Broadcast a run_progress event with updated progress data. Merges the provided partial progress into the current tracked progress for the run. Only broadcasts if the run exists in the progress map.
Parameters:
| Parameter | Type | Description |
|---|---|---|
runId |
string |
Unique run identifier |
progress |
Partial<RunProgress> |
Partial progress data to merge into current state |
reporter.broadcastRunProgress('run-001', {
progress: 60.5,
currentTest: 'Login Function Test',
estimatedTimeRemaining: 5000,
});
broadcastTestResult(runId: string, result: TestResult): void¶
Broadcast a single test result. Results are batched internally and flushed either when the batch reaches 50 items or after a 200ms interval, whichever comes first. Progress counters (passed, failed, skipped) are updated automatically. If a test with the same ID already exists in the current batch, its counters are adjusted.
Parameters:
| Parameter | Type | Description |
|---|---|---|
runId |
string |
Unique run identifier |
result |
TestResult |
Single test result to broadcast |
reporter.broadcastTestResult('run-001', {
id: 'test-id-1',
title: 'Login Function Test',
status: 'passed',
duration: 1234,
retries: 0,
timestamp: Date.now(),
browser: 'chromium',
});
broadcastTestResultBatch(runId: string, results: TestResult[]): void¶
Broadcast a batch of test results immediately without internal batching. Progress counters are updated for all results in the batch. If the results array is empty, the method returns immediately.
Parameters:
| Parameter | Type | Description |
|---|---|---|
runId |
string |
Unique run identifier |
results |
TestResult[] |
Array of test results to broadcast |
reporter.broadcastTestResultBatch('run-001', [
{ id: 'test-1', title: 'Test A', status: 'passed', duration: 100, retries: 0, timestamp: Date.now(), browser: 'chromium' },
{ id: 'test-2', title: 'Test B', status: 'failed', duration: 200, retries: 0, timestamp: Date.now(), browser: 'chromium' },
]);
broadcastSuiteCompleted(runId: string, suiteName: string): void¶
Broadcast a suite_completed event when a test suite finishes execution.
Parameters:
| Parameter | Type | Description |
|---|---|---|
runId |
string |
Unique run identifier |
suiteName |
string |
Name of the completed suite |
broadcastRunCompleted(runId: string, result: RunResult): void¶
Broadcast a run_completed event when a test run finishes. Marks the internal progress as completed (status: 'completed', progress: 100), and adds the run ID to the completed runs list for cleanup.
Parameters:
| Parameter | Type | Description |
|---|---|---|
runId |
string |
Unique run identifier |
result |
RunResult |
Complete run result data |
reporter.broadcastRunCompleted('run-001', {
id: 'run-001',
version: '1.0.0',
status: 'success',
startTime: 1715673600000,
duration: 12345,
suites: [],
totalTests: 50,
passed: 45,
failed: 3,
skipped: 2,
flakyTests: [],
});
broadcastFlakyDetected(runId: string, test: TestResult, extra?: { weightedFailureRate?: number; classification?: string; rootCause?: string }): void¶
Broadcast a flaky_detected event when a flaky test is detected. Includes test identification, failure rate, classification, and optional root cause information.
Parameters:
| Parameter | Type | Description |
|---|---|---|
runId |
string |
Unique run identifier |
test |
TestResult |
The flaky test result |
extra |
object |
Optional additional flaky analysis data |
extra.weightedFailureRate |
number |
Weighted failure rate (defaults to 0.5) |
extra.classification |
string |
Flaky classification (defaults to 'flaky') |
extra.rootCause |
string |
Root cause description |
reporter.broadcastFlakyDetected('run-001', testResult, {
weightedFailureRate: 0.65,
classification: 'timing',
rootCause: 'race_condition',
});
broadcastError(runId: string, error: string): void¶
Broadcast an error event when a run error occurs.
Parameters:
| Parameter | Type | Description |
|---|---|---|
runId |
string |
Unique run identifier |
error |
string |
Error message |
broadcastLog(runId: string, message: string, logType?: string): void¶
Broadcast a log event with a log message. Supports differentiating between log types such as stdout, stderr, and info.
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
runId |
string |
Unique run identifier | |
message |
string |
Log message content | |
logType |
string |
'info' |
Log type ('stdout', 'stderr', 'info') |
reporter.broadcastLog('run-001', 'Running test: Login Function Test', 'info');
reporter.broadcastLog('run-001', 'Warning: slow network detected', 'stderr');
broadcastReportCreated(report: RunResult): void¶
Broadcast a report_created event when a new test report is generated.
Parameters:
| Parameter | Type | Description |
|---|---|---|
report |
RunResult |
The created report data. The runId is derived from report.id |
broadcastReportUpdated(runId: string, updates: { totalTests?: number; passed?: number; failed?: number; skipped?: number; status?: 'running' | 'completed' | 'failed' | 'cancelled'; testResult?: TestResult }): void¶
Broadcast a report_updated event when a test report is updated with new data.
Parameters:
| Parameter | Type | Description |
|---|---|---|
runId |
string |
Unique run identifier |
updates |
object |
Partial report update data |
updates.totalTests |
number |
Updated total test count |
updates.passed |
number |
Updated passed count |
updates.failed |
number |
Updated failed count |
updates.skipped |
number |
Updated skipped count |
updates.status |
string |
Updated run status |
updates.testResult |
TestResult |
Individual test result to include |
reporter.broadcastReportUpdated('run-001', {
totalTests: 50,
passed: 45,
failed: 3,
skipped: 2,
status: 'completed',
});
broadcastQuarantineUpdated(testId: string, action: string, details?: Record<string, unknown>): void¶
Broadcast a quarantine_updated event when quarantine status changes for a test.
Parameters:
| Parameter | Type | Description |
|---|---|---|
testId |
string |
Test identifier |
action |
string |
Quarantine action (e.g. 'quarantined', 'released', 'validated_released') |
details |
Record<string, unknown> |
Optional additional details about the quarantine change |
getProgress(runId: string): RunProgress | undefined¶
Get the current progress data for a specific run.
Parameters:
| Parameter | Type | Description |
|---|---|---|
runId |
string |
Unique run identifier |
Returns: RunProgress | undefined — The progress data, or undefined if the run is not tracked.
const progress = reporter.getProgress('run-001');
if (progress) {
console.log(`Progress: ${progress.progress}%, Passed: ${progress.passed}`);
}
getAllProgress(): RunProgress[]¶
Get progress data for all tracked runs, both running and completed.
Returns: RunProgress[] — Array of all run progress entries.
const allProgress = reporter.getAllProgress();
allProgress.forEach((p) => {
console.log(`${p.runId}: ${p.status} (${p.progress}%)`);
});
getConnectedClients(): number¶
Get the current number of connected WebSocket clients.
Returns: number — Number of active WebSocket connections.
const clientCount = reporter.getConnectedClients();
console.log(`${clientCount} clients connected`);
shutdown(): void¶
Shut down the WebSocket server and clean up all resources. Closes the WebSocket server, removes HTTP server listeners, and clears all client connections and progress data.
RealtimeReporterClient¶
Client for connecting to a RealtimeReporter WebSocket server. Extends EventEmitter and provides automatic reconnection with exponential backoff and jitter.
Constructor¶
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
url |
string |
'ws://localhost:3001' |
WebSocket server URL |
Example¶
import { RealtimeReporterClient } from 'yuantest-playwright';
const client = new RealtimeReporterClient('ws://localhost:5274/ws');
Instance Methods¶
connect(): Promise<void>¶
Connect to the WebSocket server. Resolves when the connection is established. Emits a 'connected' event on success. On connection close, automatically attempts reconnection with exponential backoff.
Returns: Promise<void>
disconnect(): void¶
Disconnect from the WebSocket server and disable automatic reconnection. Sets the reconnect attempt counter to the maximum to prevent further reconnection attempts.
isConnected(): boolean¶
Check whether the client is currently connected to the WebSocket server.
Returns: boolean — true if connected, false otherwise.
Events¶
The RealtimeReporterClient extends EventEmitter and emits the following events:
'connected'¶
Emitted when the WebSocket connection is established.
'disconnected'¶
Emitted when the WebSocket connection is closed.
'message'¶
Emitted for every incoming message with the full RealTimeMessage object.
Server Event Types¶
All server event types are emitted as separate events using the message type field. The payload is passed as the event argument.
| Event | Payload Type | Description |
|---|---|---|
'connected' |
{ message: string } |
Connection established |
'run_started' |
{ runId: string; version: string; startTime: number } |
Run started |
'run_progress' |
RunProgress |
Run progress updated |
'run_completed' |
RunResult |
Run completed |
'test_result' |
TestResult & { currentProgress: RunProgress } |
Single test result |
'test_result_batch' |
{ results: TestResult[]; currentProgress?: RunProgress } |
Batch test results |
'suite_completed' |
{ suiteName: string; timestamp: number } |
Suite completed |
'error' |
{ error: string } |
Run error |
'flaky_detected' |
{ testId: string; title: string; failureRate: number; weightedFailureRate: number; classification: FlakyClassification; rootCause?: RootCauseType; timestamp: number } |
Flaky test detected |
'quarantine_updated' |
Record<string, unknown> |
Quarantine status changed |
'log' |
{ message: string; timestamp: number; logType?: string } |
Log message |
'report_created' |
RunResult |
Report created |
'report_updated' |
{ runId: string; totalTests?: number; passed?: number; failed?: number; skipped?: number; status?: string; testResult?: TestResult } |
Report updated |
client.on('run_started', (payload) => {
console.log(`Run started: ${payload.runId}`);
});
client.on('test_result_batch', (payload) => {
console.log(`Batch results: ${payload.results.length} tests`);
});
client.on('flaky_detected', (payload) => {
console.log(`Flaky: ${payload.title}, classification: ${payload.classification}`);
});
Reconnection Behavior¶
The client automatically reconnects on disconnection with the following configuration:
| Setting | Value | Description |
|---|---|---|
MAX_RECONNECT_ATTEMPTS |
10 |
Maximum number of reconnection attempts |
RECONNECT_BASE_DELAY |
1000 |
Base delay in milliseconds before first reconnect |
RECONNECT_MAX_DELAY |
30000 |
Maximum delay cap in milliseconds |
The delay uses exponential backoff with random jitter: baseDelay * 2^attempt with 50-100% randomization, capped at RECONNECT_MAX_DELAY.
Type Definitions¶
interface RunProgress {
runId: string;
status: 'running' | 'completed' | 'cancelled';
progress: number;
totalTests: number;
currentSuite?: string;
currentTest?: string;
passed: number;
failed: number;
skipped: number;
flakyTests: string[];
startTime: number;
estimatedTimeRemaining?: number;
}
interface RunResult {
id: string;
version: string;
status: 'success' | 'failed' | 'cancelled' | 'running';
startTime: number;
endTime?: number;
duration?: number;
suites: SuiteResult[];
totalTests: number;
passed: number;
failed: number;
skipped: number;
flakyTests: TestResult[];
metadata?: RunMetadata;
}
interface TestResult {
id: string;
title: string;
fullTitle?: string;
file?: string;
line?: number;
column?: number;
status: 'passed' | 'failed' | 'skipped' | 'timedout';
duration: number;
error?: string;
retries: number;
manualReruns?: number;
runHistory?: TestRunHistory[];
timestamp: number;
browser: BrowserType;
shard?: number;
screenshots?: string[];
videos?: string[];
traces?: string[];
logs?: string[];
stackTrace?: string;
}
interface SuiteResult {
name: string;
totalTests: number;
passed: number;
failed: number;
skipped: number;
duration: number;
tests: TestResult[];
}
interface FlakyTest {
testId: string;
title: string;
failureRate: number;
totalRuns: number;
lastFailure?: number;
isQuarantined: boolean;
quarantinedAt?: number;
consecutivePassesSinceQuarantine?: number;
history: FlakyHistoryEntry[];
classification: FlakyClassification;
weightedFailureRate: number;
}
type FlakyClassification =
| 'flaky'
| 'broken'
| 'regression'
| 'monitor'
| 'stable'
| 'insufficient_data';
type RootCauseType =
| 'timing'
| 'data_race'
| 'environment'
| 'external_service'
| 'test_order'
| 'resource_leak'
| 'assertion_flaky'
| 'unknown';
type RealTimeMessage =
| { type: 'connected'; payload: { message: string }; timestamp: number; runId: string }
| { type: 'run_started'; payload: { runId: string; version: string; startTime: number }; timestamp: number; runId: string }
| { type: 'run_progress'; payload: RunProgress; timestamp: number; runId: string }
| { type: 'run_completed'; payload: RunResult; timestamp: number; runId: string }
| { type: 'test_result'; payload: TestResult & { currentProgress: RunProgress }; timestamp: number; runId: string }
| { type: 'test_result_batch'; payload: { results: TestResult[]; currentProgress?: RunProgress }; timestamp: number; runId: string }
| { type: 'suite_completed'; payload: { suiteName: string; timestamp: number }; timestamp: number; runId: string }
| { type: 'error'; payload: { error: string }; timestamp: number; runId: string }
| { type: 'flaky_detected'; payload: { testId: string; title: string; failureRate: number; weightedFailureRate: number; classification: FlakyClassification; rootCause?: RootCauseType; timestamp: number }; timestamp: number; runId: string }
| { type: 'quarantine_updated'; payload: Record<string, unknown>; timestamp: number; runId: string }
| { type: 'log'; payload: { message: string; timestamp: number; logType?: string }; timestamp: number; runId: string }
| { type: 'report_created'; payload: RunResult; timestamp: number; runId: string }
| { type: 'report_updated'; payload: { runId: string; totalTests?: number; passed?: number; failed?: number; skipped?: number; status?: 'running' | 'completed' | 'failed' | 'cancelled'; testResult?: TestResult }; timestamp: number; runId: string };