@testdino/playwright 1.0.1

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.
@@ -0,0 +1,779 @@
1
+ import { Reporter, FullConfig, Suite, TestCase, TestResult, TestStep, FullResult, TestError } from '@playwright/test/reporter';
2
+
3
+ /**
4
+ * Metadata collection types and interfaces
5
+ */
6
+ /**
7
+ * Base interface for all metadata collectors
8
+ */
9
+ interface MetadataCollector<T = Record<string, unknown>> {
10
+ /**
11
+ * Collect metadata asynchronously
12
+ * Should handle errors gracefully and return partial data
13
+ */
14
+ collect(): Promise<T>;
15
+ /**
16
+ * Get collector name for debugging
17
+ */
18
+ getName(): string;
19
+ }
20
+ /**
21
+ * Result of metadata collection with error tracking
22
+ */
23
+ interface MetadataCollectionResult<T = Record<string, unknown>> {
24
+ /**
25
+ * Collected data (may be partial on errors)
26
+ */
27
+ data: T;
28
+ /**
29
+ * Whether collection was successful
30
+ */
31
+ success: boolean;
32
+ /**
33
+ * Error message if collection failed
34
+ */
35
+ error?: string;
36
+ /**
37
+ * Collection duration in milliseconds
38
+ */
39
+ duration: number;
40
+ /**
41
+ * Collector name
42
+ */
43
+ collector: string;
44
+ }
45
+ /**
46
+ * Git metadata structure
47
+ */
48
+ interface GitMetadata {
49
+ branch?: string;
50
+ commit?: {
51
+ hash?: string;
52
+ message?: string;
53
+ author?: string;
54
+ authorId?: string;
55
+ email?: string;
56
+ timestamp?: string;
57
+ isDirty?: boolean;
58
+ };
59
+ repository?: {
60
+ name?: string;
61
+ url?: string;
62
+ };
63
+ pr?: PRMetadata;
64
+ }
65
+ /**
66
+ * CI metadata structure
67
+ */
68
+ interface CIMetadata {
69
+ provider?: string;
70
+ pipeline?: {
71
+ id?: string;
72
+ name?: string;
73
+ url?: string;
74
+ };
75
+ build?: {
76
+ number?: string;
77
+ trigger?: string;
78
+ };
79
+ environment?: {
80
+ name?: string;
81
+ type?: string;
82
+ os?: string;
83
+ node?: string;
84
+ };
85
+ }
86
+ /**
87
+ * Pull Request metadata structure
88
+ */
89
+ interface PRMetadata {
90
+ title?: string;
91
+ number?: number;
92
+ url?: string;
93
+ status?: string;
94
+ branch?: string;
95
+ targetBranch?: string;
96
+ author?: string;
97
+ labels?: string[];
98
+ merged?: boolean;
99
+ mergeable?: boolean;
100
+ mergeCommitSha?: string;
101
+ }
102
+ /**
103
+ * System metadata structure
104
+ */
105
+ interface SystemMetadata {
106
+ os?: string;
107
+ cpu?: string;
108
+ memory?: string;
109
+ nodeVersion?: string;
110
+ platform?: string;
111
+ hostname?: string;
112
+ }
113
+ /**
114
+ * Test case metadata in skeleton
115
+ */
116
+ interface SkeletonTest {
117
+ testId: string;
118
+ title: string;
119
+ location: {
120
+ file: string;
121
+ line: number;
122
+ column: number;
123
+ };
124
+ tags?: string[];
125
+ expectedStatus?: string;
126
+ annotations?: Array<{
127
+ type: string;
128
+ description?: string;
129
+ }>;
130
+ }
131
+ /**
132
+ * Suite metadata in skeleton (recursive structure)
133
+ */
134
+ interface SkeletonSuite {
135
+ title: string;
136
+ file?: string;
137
+ type: 'describe' | 'file';
138
+ location?: {
139
+ file: string;
140
+ line: number;
141
+ column: number;
142
+ };
143
+ tests: SkeletonTest[];
144
+ suites?: SkeletonSuite[];
145
+ }
146
+ /**
147
+ * Test run skeleton structure
148
+ */
149
+ interface RunSkeleton {
150
+ totalTests: number;
151
+ suites: SkeletonSuite[];
152
+ }
153
+ /**
154
+ * Playwright metadata structure
155
+ */
156
+ interface PlaywrightMetadata {
157
+ version?: string;
158
+ parallel?: boolean;
159
+ workers?: number;
160
+ shard?: ShardMetadata;
161
+ projects?: string[];
162
+ browsers?: string[];
163
+ configFile?: string;
164
+ forbidOnly?: boolean;
165
+ fullyParallel?: boolean;
166
+ globalTimeout?: number;
167
+ grep?: string[];
168
+ maxFailures?: number;
169
+ metadata?: Record<string, unknown>;
170
+ reportSlowTests?: {
171
+ max: number;
172
+ threshold: number;
173
+ };
174
+ rootDir?: string;
175
+ tags?: string[];
176
+ webServer?: Record<string, unknown>;
177
+ }
178
+ /**
179
+ * Shard metadata structure
180
+ */
181
+ interface ShardMetadata {
182
+ current?: number;
183
+ total?: number;
184
+ }
185
+ /**
186
+ * Complete metadata collection result
187
+ */
188
+ interface CompleteMetadata {
189
+ git?: GitMetadata;
190
+ ci?: CIMetadata;
191
+ system?: SystemMetadata;
192
+ playwright?: PlaywrightMetadata;
193
+ skeleton?: RunSkeleton;
194
+ }
195
+ /**
196
+ * Metadata collection summary
197
+ */
198
+ interface MetadataCollectionSummary {
199
+ /**
200
+ * Complete collected metadata
201
+ */
202
+ metadata: CompleteMetadata;
203
+ /**
204
+ * Collection results for each collector
205
+ */
206
+ results: MetadataCollectionResult[];
207
+ /**
208
+ * Total collection time in milliseconds
209
+ */
210
+ totalDuration: number;
211
+ /**
212
+ * Number of successful collectors
213
+ */
214
+ successCount: number;
215
+ /**
216
+ * Number of failed collectors
217
+ */
218
+ failureCount: number;
219
+ }
220
+
221
+ /**
222
+ * Server error classes for quota and infrastructure errors
223
+ * These are received from the TestDino server and handled gracefully in the reporter
224
+ * Follows the same Error class pattern as cli/errors.ts
225
+ */
226
+ /**
227
+ * Base class for all TestDino server errors
228
+ * Extends Error to work with standard error handling
229
+ */
230
+ declare class TestDinoServerError extends Error {
231
+ readonly code: string;
232
+ constructor(code: string, message: string);
233
+ }
234
+ /**
235
+ * Quota exhausted error (session creation)
236
+ * HTTP 402 response when organization pool is exhausted
237
+ */
238
+ declare class QuotaExhaustedError extends TestDinoServerError {
239
+ readonly details: {
240
+ planName: string;
241
+ totalLimit: number;
242
+ used: number;
243
+ resetDate?: string;
244
+ };
245
+ constructor(message: string, details: {
246
+ planName: string;
247
+ totalLimit: number;
248
+ used: number;
249
+ resetDate?: string;
250
+ });
251
+ }
252
+ /**
253
+ * Quota exceeded error (run:begin)
254
+ * WebSocket NACK when test count exceeds available quota
255
+ */
256
+ declare class QuotaExceededError extends TestDinoServerError {
257
+ readonly details: {
258
+ planName: string;
259
+ totalTests: number;
260
+ remaining: number;
261
+ used: number;
262
+ total: number;
263
+ resetDate?: string;
264
+ canPartialSubmit: boolean;
265
+ allowedCount: number;
266
+ };
267
+ constructor(message: string, details: {
268
+ planName: string;
269
+ totalTests: number;
270
+ remaining: number;
271
+ used: number;
272
+ total: number;
273
+ resetDate?: string;
274
+ canPartialSubmit: boolean;
275
+ allowedCount: number;
276
+ });
277
+ }
278
+ /**
279
+ * Queue full error (backpressure)
280
+ * WebSocket NACK when event queue is full
281
+ */
282
+ declare class QueueFullError extends TestDinoServerError {
283
+ constructor(message: string);
284
+ }
285
+ /**
286
+ * Circuit breaker error (Redis unavailable)
287
+ * WebSocket NACK when circuit breaker is open
288
+ */
289
+ declare class CircuitBreakerError extends TestDinoServerError {
290
+ readonly retryAfter?: number;
291
+ constructor(message: string, retryAfter?: number);
292
+ }
293
+ /**
294
+ * Union type for all server error classes
295
+ */
296
+ type ServerError = QuotaExhaustedError | QuotaExceededError | QueueFullError | CircuitBreakerError;
297
+
298
+ /**
299
+ * Server response type definitions
300
+ * These are pure type definitions for server responses (not Error classes)
301
+ * Error classes are in reporter/errors.ts following the established pattern
302
+ */
303
+
304
+ /**
305
+ * HTTP 402 response from session creation
306
+ * This is a server response type, not an error class
307
+ */
308
+ interface SessionCreationQuotaResponse {
309
+ success: false;
310
+ message: string;
311
+ error: 'QUOTA_EXHAUSTED';
312
+ details: {
313
+ planName: string;
314
+ totalLimit: number;
315
+ used: number;
316
+ resetDate?: string;
317
+ };
318
+ }
319
+ /**
320
+ * WebSocket NACK message structure
321
+ * This is a server message type, not an error class
322
+ */
323
+ interface NackMessage {
324
+ type: 'nack';
325
+ eventId: string;
326
+ error: string | ServerError;
327
+ retryable: boolean;
328
+ timestamp: number;
329
+ }
330
+
331
+ /**
332
+ * Core types for TestDino Playwright integration
333
+ */
334
+
335
+ interface TestdinoConfig {
336
+ /**
337
+ * Record token for authentication
338
+ */
339
+ token?: string;
340
+ /**
341
+ * CI build ID for grouping sharded runs
342
+ */
343
+ ciBuildId?: string;
344
+ /**
345
+ * Server URL (for development/testing)
346
+ */
347
+ serverUrl?: string;
348
+ /**
349
+ * Enable debug logging
350
+ */
351
+ debug?: boolean;
352
+ /**
353
+ * Upload artifacts (screenshots, videos, traces) to cloud storage
354
+ * Default: true (enabled)
355
+ * Set to false or use --no-artifacts CLI flag to disable
356
+ */
357
+ artifacts?: boolean;
358
+ }
359
+ interface EventBuffer {
360
+ events: TestEvent[];
361
+ maxSize: number;
362
+ flush: () => Promise<void>;
363
+ add: (event: TestEvent) => Promise<void>;
364
+ }
365
+ interface ConnectionConfig {
366
+ token: string;
367
+ serverUrl: string;
368
+ maxRetries?: number;
369
+ retryDelay?: number;
370
+ }
371
+ type TestEvent = TestRunBeginEvent | TestBeginEvent | TestStepBeginEvent | TestStepEndEvent | TestEndEvent | TestRunEndEvent | TestRunErrorEvent | TestConsoleOutEvent | TestConsoleErrEvent;
372
+ interface BaseEvent {
373
+ type: string;
374
+ timestamp: number;
375
+ runId: string;
376
+ sequence: number;
377
+ }
378
+ interface TestRunBeginEvent extends BaseEvent {
379
+ type: 'run:begin';
380
+ metadata: CompleteMetadata;
381
+ }
382
+ interface TestBeginEvent extends BaseEvent {
383
+ type: 'test:begin';
384
+ testId: string;
385
+ title: string;
386
+ titlePath: Array<string>;
387
+ location: {
388
+ file: string;
389
+ line: number;
390
+ column: number;
391
+ };
392
+ tags: Array<string>;
393
+ expectedStatus: 'passed' | 'failed' | 'timedOut' | 'skipped' | 'interrupted';
394
+ timeout: number;
395
+ retries: number;
396
+ annotations: Array<{
397
+ type: string;
398
+ description?: string;
399
+ }>;
400
+ retry: number;
401
+ workerIndex: number;
402
+ parallelIndex: number;
403
+ repeatEachIndex?: number;
404
+ parentSuite: {
405
+ title: string;
406
+ type: 'root' | 'project' | 'file' | 'describe';
407
+ location?: {
408
+ file: string;
409
+ line: number;
410
+ column: number;
411
+ };
412
+ };
413
+ startTime: number;
414
+ }
415
+ interface TestStepBeginEvent extends BaseEvent {
416
+ type: 'step:begin';
417
+ testId: string;
418
+ stepId: string;
419
+ title: string;
420
+ titlePath: Array<string>;
421
+ category: string;
422
+ location?: {
423
+ file: string;
424
+ line: number;
425
+ column: number;
426
+ };
427
+ parentStep?: {
428
+ title: string;
429
+ category: string;
430
+ location?: {
431
+ file: string;
432
+ line: number;
433
+ column: number;
434
+ };
435
+ };
436
+ startTime: number;
437
+ retry: number;
438
+ workerIndex: number;
439
+ parallelIndex: number;
440
+ }
441
+ interface TestStepEndEvent extends BaseEvent {
442
+ type: 'step:end';
443
+ testId: string;
444
+ stepId: string;
445
+ title?: string;
446
+ titlePath?: string[];
447
+ duration: number;
448
+ error?: {
449
+ message: string;
450
+ stack?: string;
451
+ snippet?: string;
452
+ value?: string;
453
+ location?: {
454
+ file: string;
455
+ line: number;
456
+ column: number;
457
+ };
458
+ };
459
+ status: 'passed' | 'failed';
460
+ childSteps: {
461
+ count: number;
462
+ steps: Array<{
463
+ title: string;
464
+ status: 'passed' | 'failed';
465
+ }>;
466
+ };
467
+ workerIndex: number;
468
+ parallelIndex: number;
469
+ attachments: Array<{
470
+ name: string;
471
+ contentType: string;
472
+ path?: string;
473
+ }>;
474
+ annotations?: Array<{
475
+ type: string;
476
+ description?: string;
477
+ }>;
478
+ retry: number;
479
+ }
480
+ interface TestEndEvent extends BaseEvent {
481
+ type: 'test:end';
482
+ testId: string;
483
+ status: 'passed' | 'failed' | 'skipped' | 'timedOut' | 'interrupted';
484
+ outcome: 'expected' | 'unexpected' | 'flaky' | 'skipped';
485
+ duration: number;
486
+ retry: number;
487
+ workerIndex: number;
488
+ parallelIndex: number;
489
+ annotations: Array<{
490
+ type: string;
491
+ description?: string;
492
+ }>;
493
+ errors: Array<{
494
+ message: string;
495
+ stack?: string;
496
+ snippet?: string;
497
+ value?: string;
498
+ location?: {
499
+ file: string;
500
+ line: number;
501
+ column: number;
502
+ };
503
+ }>;
504
+ steps: {
505
+ total: number;
506
+ passed: number;
507
+ failed: number;
508
+ };
509
+ attachments: Array<{
510
+ name: string;
511
+ contentType: string;
512
+ path?: string;
513
+ }>;
514
+ stdout?: string[];
515
+ stderr?: string[];
516
+ }
517
+ interface TestRunEndEvent extends BaseEvent {
518
+ type: 'run:end';
519
+ status: 'passed' | 'failed' | 'timedout' | 'interrupted';
520
+ duration: number;
521
+ startTime: number;
522
+ shard?: {
523
+ current: number;
524
+ total: number;
525
+ };
526
+ }
527
+ interface TestRunErrorEvent extends BaseEvent {
528
+ type: 'run:error';
529
+ error: {
530
+ message?: string;
531
+ stack?: string;
532
+ value?: string;
533
+ snippet?: string;
534
+ location?: {
535
+ file: string;
536
+ line: number;
537
+ column: number;
538
+ };
539
+ cause?: {
540
+ message?: string;
541
+ stack?: string;
542
+ value?: string;
543
+ snippet?: string;
544
+ location?: {
545
+ file: string;
546
+ line: number;
547
+ column: number;
548
+ };
549
+ };
550
+ };
551
+ }
552
+ interface TestConsoleOutEvent extends BaseEvent {
553
+ type: 'console:out';
554
+ text: string;
555
+ testId?: string;
556
+ retry?: number;
557
+ truncated?: boolean;
558
+ }
559
+ interface TestConsoleErrEvent extends BaseEvent {
560
+ type: 'console:err';
561
+ text: string;
562
+ testId?: string;
563
+ retry?: number;
564
+ truncated?: boolean;
565
+ }
566
+
567
+ /**
568
+ * TestDino Playwright Reporter
569
+ * Streams test execution data in real-time
570
+ */
571
+
572
+ declare class TestdinoReporter implements Reporter {
573
+ private config;
574
+ private wsClient;
575
+ private httpClient;
576
+ private buffer;
577
+ private runId;
578
+ private useHttpFallback;
579
+ private sequenceNumber;
580
+ private shardInfo?;
581
+ private runStartTime?;
582
+ private sigintHandler?;
583
+ private sigtermHandler?;
584
+ private isShuttingDown;
585
+ private quotaExceeded;
586
+ private sessionId;
587
+ private artifactUploader;
588
+ private artifactsEnabled;
589
+ private initPromise;
590
+ private initFailed;
591
+ constructor(config?: TestdinoConfig);
592
+ /**
593
+ * Load configuration from CLI temp file if available
594
+ */
595
+ private loadCliConfig;
596
+ /**
597
+ * Called once before running tests
598
+ */
599
+ onBegin(config: FullConfig, suite: Suite): Promise<void>;
600
+ /**
601
+ * Perform all async initialization: metadata, auth, WebSocket, artifacts, run:begin
602
+ *
603
+ * Buffer's onFlush callback awaits this promise before transmitting events,
604
+ * ensuring no data is sent before authentication and connection are established.
605
+ *
606
+ * @param config - Playwright FullConfig for metadata collection
607
+ * @param suite - Playwright Suite for skeleton building
608
+ * @param token - Authentication token
609
+ * @returns true on success, false on failure
610
+ */
611
+ private performAsyncInit;
612
+ /**
613
+ * Called for each test before it starts
614
+ */
615
+ onTestBegin(test: TestCase, result: TestResult): Promise<void>;
616
+ /**
617
+ * Called when a test step begins
618
+ */
619
+ onStepBegin(test: TestCase, result: TestResult, step: TestStep): Promise<void>;
620
+ /**
621
+ * Called when a test step ends
622
+ */
623
+ onStepEnd(test: TestCase, result: TestResult, step: TestStep): Promise<void>;
624
+ /**
625
+ * Called after each test completes
626
+ */
627
+ onTestEnd(test: TestCase, result: TestResult): Promise<void>;
628
+ /**
629
+ * Called after all tests complete
630
+ */
631
+ onEnd(result: FullResult): Promise<void>;
632
+ /**
633
+ * Called on global errors
634
+ */
635
+ onError(error: TestError): Promise<void>;
636
+ /**
637
+ * Called when standard output is produced in worker process
638
+ */
639
+ onStdOut(chunk: string | Buffer, test?: TestCase, result?: TestResult): Promise<void>;
640
+ /**
641
+ * Called when standard error is produced in worker process
642
+ */
643
+ onStdErr(chunk: string | Buffer, test?: TestCase, result?: TestResult): Promise<void>;
644
+ /**
645
+ * Indicates whether this reporter outputs to stdout/stderr
646
+ * Returns false to allow Playwright to add its own terminal output
647
+ */
648
+ printsToStdio(): boolean;
649
+ /**
650
+ * Send events via WebSocket or HTTP fallback
651
+ */
652
+ private sendEvents;
653
+ /**
654
+ * Get token from config or environment
655
+ */
656
+ private getToken;
657
+ /**
658
+ * Get server URL from config or environment
659
+ */
660
+ private getServerUrl;
661
+ private getWebSocketUrl;
662
+ /**
663
+ * Collect metadata for the test run
664
+ */
665
+ private collectMetadata;
666
+ /**
667
+ * Get next sequence number and current timestamp
668
+ */
669
+ private getEventMetadata;
670
+ /**
671
+ * Extract parent suite information for skeleton mapping
672
+ */
673
+ private extractParentSuite;
674
+ /**
675
+ * Extract parent step information for step hierarchy mapping
676
+ */
677
+ private extractParentStep;
678
+ /**
679
+ * Extract child steps summary for step:end event
680
+ */
681
+ private extractChildSteps;
682
+ /**
683
+ * Extract error details from TestError (shared by step:end, test:end, and error events)
684
+ */
685
+ private extractError;
686
+ /**
687
+ * Extract global error details with cause support (for error events)
688
+ */
689
+ private extractGlobalError;
690
+ /**
691
+ * Print a prominent configuration error banner
692
+ */
693
+ private printConfigurationError;
694
+ /**
695
+ * Print quota error with plan details and upgrade information
696
+ * @param error - Quota error (QUOTA_EXHAUSTED or QUOTA_EXCEEDED)
697
+ */
698
+ private printQuotaError;
699
+ /**
700
+ * Truncate console chunk to max size and convert Buffer to string
701
+ */
702
+ private truncateChunk;
703
+ /**
704
+ * Extract attachment metadata for step:end event
705
+ */
706
+ private extractAttachments;
707
+ /**
708
+ * Extract steps summary for test:end event
709
+ */
710
+ private extractTestStepsSummary;
711
+ /**
712
+ * Extract console output for test:end event
713
+ */
714
+ private extractConsoleOutput;
715
+ /**
716
+ * Check if this is a duplicate instance of TestdinoReporter
717
+ * This happens when the CLI injects our reporter via --reporter flag,
718
+ * but the user already has TestdinoReporter configured in playwright.config
719
+ *
720
+ * @param reporters - The resolved reporters array from FullConfig
721
+ * @returns true if there are multiple TestdinoReporter instances, false otherwise
722
+ */
723
+ private isDuplicateInstance;
724
+ /**
725
+ * Count how many TestdinoReporter instances are in the reporters array
726
+ *
727
+ * @param reporters - The resolved reporters array from FullConfig
728
+ * @returns Number of TestdinoReporter instances found
729
+ */
730
+ private countTestdinoReporters;
731
+ /**
732
+ * Check if a reporter name/path matches TestdinoReporter
733
+ *
734
+ * @param value - Reporter name, path, or class reference
735
+ * @returns true if this is our reporter, false otherwise
736
+ */
737
+ private isTestdinoReporter;
738
+ /**
739
+ * Register signal handlers for graceful shutdown on interruption
740
+ */
741
+ private registerSignalHandlers;
742
+ /**
743
+ * Remove signal handlers (called on normal completion or after handling interruption)
744
+ */
745
+ private removeSignalHandlers;
746
+ /**
747
+ * Handle process interruption by sending run:end event with interrupted status
748
+ * @param signal - The signal that triggered the interruption (SIGINT or SIGTERM)
749
+ * @param exitCode - The exit code to use when exiting
750
+ */
751
+ private handleInterruption;
752
+ /**
753
+ * Send interruption event directly without buffering
754
+ * Uses optimized path for speed during shutdown
755
+ */
756
+ private sendInterruptionEvent;
757
+ /**
758
+ * Create a promise that rejects after a timeout
759
+ * @param ms - Timeout in milliseconds
760
+ * @param message - Error message for timeout
761
+ */
762
+ private timeoutPromise;
763
+ /**
764
+ * Initialize artifact uploader by requesting SAS token
765
+ * Gracefully handles failures - uploads disabled if SAS token request fails
766
+ */
767
+ private initializeArtifactUploader;
768
+ /**
769
+ * Upload attachments and return with Azure URLs
770
+ * If uploads disabled or failed, returns attachments with local paths
771
+ */
772
+ private uploadAttachments;
773
+ /**
774
+ * Get base server URL without /api/reporter suffix
775
+ */
776
+ private getBaseServerUrl;
777
+ }
778
+
779
+ export { type BaseEvent, type CIMetadata, CircuitBreakerError, type ConnectionConfig, type EventBuffer, type GitMetadata, type MetadataCollectionResult, type MetadataCollectionSummary, type MetadataCollector, type NackMessage, type PRMetadata, type PlaywrightMetadata, QueueFullError, QuotaExceededError, QuotaExhaustedError, type CompleteMetadata as RunMetadata, type ServerError, type SessionCreationQuotaResponse, type ShardMetadata, type SystemMetadata, type TestBeginEvent, type TestConsoleErrEvent, type TestConsoleOutEvent, type TestEndEvent, type TestEvent, type TestRunBeginEvent, type TestRunEndEvent, type TestRunErrorEvent, type TestStepBeginEvent, type TestStepEndEvent, type TestdinoConfig, TestdinoReporter as default };