@piwitests/reporter 0.12.0 → 0.14.0

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.
Files changed (77) hide show
  1. package/dist/global-setup-module.d.ts +2 -1
  2. package/dist/global-setup-module.js +432 -3
  3. package/dist/index.d.ts +204 -8
  4. package/dist/index.js +4724 -23
  5. package/dist/internal/capture/attachments.d.ts +8 -3
  6. package/dist/internal/capture/attachments.js +46 -21
  7. package/dist/internal/capture/capture-fixtures.d.ts +16 -14
  8. package/dist/internal/capture/capture-fixtures.js +2211 -971
  9. package/dist/internal/capture/inspect-on-failure.d.ts +50 -0
  10. package/dist/internal/capture/inspect-on-failure.js +66 -0
  11. package/dist/internal/capture/locator-healing.d.ts +33 -190
  12. package/dist/internal/capture/locator-healing.js +599 -815
  13. package/dist/internal/capture/pick-on-failure.d.ts +198 -0
  14. package/dist/internal/capture/pick-on-failure.js +1203 -0
  15. package/package.json +9 -4
  16. package/dist/internal/collect/error-text.d.ts +0 -18
  17. package/dist/internal/collect/error-text.js +0 -79
  18. package/dist/internal/collect/metadata-collector.d.ts +0 -32
  19. package/dist/internal/collect/metadata-collector.js +0 -246
  20. package/dist/internal/collect/skip-classify.d.ts +0 -27
  21. package/dist/internal/collect/skip-classify.js +0 -40
  22. package/dist/internal/collect/step-analyzer.d.ts +0 -103
  23. package/dist/internal/collect/step-analyzer.js +0 -221
  24. package/dist/internal/config/env.d.ts +0 -46
  25. package/dist/internal/config/env.js +0 -162
  26. package/dist/internal/files/compression.d.ts +0 -5
  27. package/dist/internal/files/compression.js +0 -69
  28. package/dist/internal/files/file-handler.d.ts +0 -38
  29. package/dist/internal/files/file-handler.js +0 -207
  30. package/dist/internal/streaming/crash-recovery.d.ts +0 -23
  31. package/dist/internal/streaming/crash-recovery.js +0 -106
  32. package/dist/internal/streaming/stream-buffer.d.ts +0 -17
  33. package/dist/internal/streaming/stream-buffer.js +0 -102
  34. package/dist/internal/streaming/stream-manager.d.ts +0 -88
  35. package/dist/internal/streaming/stream-manager.js +0 -395
  36. package/dist/internal/submit/run-submitter.d.ts +0 -67
  37. package/dist/internal/submit/run-submitter.js +0 -190
  38. package/dist/internal/submit/serializer.d.ts +0 -45
  39. package/dist/internal/submit/serializer.js +0 -108
  40. package/dist/internal/submit/uploader.d.ts +0 -89
  41. package/dist/internal/submit/uploader.js +0 -226
  42. package/dist/internal/support/ci.d.ts +0 -2
  43. package/dist/internal/support/ci.js +0 -32
  44. package/dist/internal/support/cli-filters.d.ts +0 -1
  45. package/dist/internal/support/cli-filters.js +0 -51
  46. package/dist/internal/support/errors.d.ts +0 -8
  47. package/dist/internal/support/errors.js +0 -15
  48. package/dist/internal/support/instance-id.d.ts +0 -4
  49. package/dist/internal/support/instance-id.js +0 -48
  50. package/dist/internal/support/limiter.d.ts +0 -2
  51. package/dist/internal/support/limiter.js +0 -27
  52. package/dist/internal/support/logger.d.ts +0 -26
  53. package/dist/internal/support/logger.js +0 -43
  54. package/dist/internal/support/reporter-version.d.ts +0 -2
  55. package/dist/internal/support/reporter-version.js +0 -54
  56. package/dist/internal/support/setup-file.d.ts +0 -13
  57. package/dist/internal/support/setup-file.js +0 -61
  58. package/dist/internal/support/source-snippet.d.ts +0 -12
  59. package/dist/internal/support/source-snippet.js +0 -97
  60. package/dist/internal/support/worker-index.d.ts +0 -7
  61. package/dist/internal/support/worker-index.js +0 -14
  62. package/dist/internal/transport/http-client.d.ts +0 -52
  63. package/dist/internal/transport/http-client.js +0 -201
  64. package/dist/public/config-wrapper.d.ts +0 -21
  65. package/dist/public/config-wrapper.js +0 -64
  66. package/dist/public/global-setup.d.ts +0 -13
  67. package/dist/public/global-setup.js +0 -146
  68. package/dist/public/options.d.ts +0 -86
  69. package/dist/public/options.js +0 -2
  70. package/dist/public/reporter.d.ts +0 -68
  71. package/dist/public/reporter.js +0 -376
  72. package/dist/types/collected.d.ts +0 -97
  73. package/dist/types/collected.js +0 -10
  74. package/dist/types/wire.d.ts +0 -176
  75. package/dist/types/wire.js +0 -14
  76. package/dist/types.d.ts +0 -11
  77. package/dist/types.js +0 -27
@@ -1,207 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.FileHandler = void 0;
37
- const fs = __importStar(require("node:fs"));
38
- const errors_js_1 = require("../support/errors.js");
39
- const path = __importStar(require("node:path"));
40
- const crypto = __importStar(require("node:crypto"));
41
- const compression_js_1 = require("./compression.js");
42
- const logger_js_1 = require("../support/logger.js");
43
- const attachments_js_1 = require("../capture/attachments.js");
44
- /**
45
- * File-system helpers for discovering report directories, trace files, and
46
- * test attachments, as well as compressing report directories and computing
47
- * trace-file hashes for deduplication.
48
- */
49
- class FileHandler {
50
- constructor(logger = new logger_js_1.Logger()) {
51
- this.logger = logger;
52
- }
53
- /** Locate a Playwright HTML report directory containing `index.html`. Optionally override the search path. */
54
- findHTMLReportDirectory(customDir) {
55
- const possibleDirs = customDir
56
- ? [customDir, path.join(process.cwd(), customDir)]
57
- : ['playwright-report', './playwright-report', path.join(process.cwd(), 'playwright-report')];
58
- for (const reportDir of possibleDirs) {
59
- if (fs.existsSync(reportDir) && fs.statSync(reportDir).isDirectory()) {
60
- if (fs.existsSync(path.join(reportDir, 'index.html')))
61
- return reportDir;
62
- }
63
- }
64
- return null;
65
- }
66
- /** Check if `dir` exists and is a directory; also checks `path.join(process.cwd(), dir)` */
67
- findReportDirectory(dir) {
68
- const candidates = [dir, path.join(process.cwd(), dir)];
69
- for (const candidate of candidates) {
70
- if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory())
71
- return candidate;
72
- }
73
- return null;
74
- }
75
- /** Gzip-compress an entire report directory into a single buffer. Returns `null` on failure. */
76
- async compressReportDirectory(reportDir) {
77
- try {
78
- return (await (0, compression_js_1.compressDirectory)(reportDir)) || null;
79
- }
80
- catch (error) {
81
- this.logger.warn(`Failed to compress report directory ${reportDir}: ${(0, errors_js_1.errorMessage)(error)}`);
82
- return null;
83
- }
84
- }
85
- /** Return resolved paths for all trace attachments on a test case */
86
- findTraceFiles(testCase) {
87
- const set = new Set();
88
- if (testCase.attachments) {
89
- for (const a of testCase.attachments) {
90
- if (a.name === 'trace' && a.path)
91
- set.add(path.resolve(a.path));
92
- }
93
- }
94
- return Array.from(set);
95
- }
96
- /** Return all non-trace, non-internal attachments from a test case. Skips `trace` and `piwi-*` attachments. */
97
- findAllAttachments(testCase) {
98
- const result = [];
99
- if (testCase.attachments) {
100
- for (const a of testCase.attachments) {
101
- if (a.name === 'trace')
102
- continue;
103
- if (a.name && attachments_js_1.INTERNAL_ATTACHMENT_NAMES.has(a.name))
104
- continue;
105
- if (a.path && fs.existsSync(a.path)) {
106
- result.push({
107
- name: a.name || 'attachment',
108
- path: path.resolve(a.path),
109
- contentType: a.contentType || 'application/octet-stream',
110
- originalName: path.basename(a.path),
111
- });
112
- }
113
- }
114
- }
115
- return result;
116
- }
117
- /** Mapping of well-known report type names to their default output directories */
118
- getDefaultReportDirs() {
119
- return {
120
- html: 'playwright-report',
121
- monocart: 'monocart-report',
122
- allure: 'allure-report',
123
- blob: 'blob-report',
124
- };
125
- }
126
- /** Parse Piwi-internal attachment bodies (`piwi-network`, `piwi-web-vitals`, `piwi-console`, `piwi-aria-snapshot`) into structured fields on the test case */
127
- parsePerformanceAttachments(testCase, attachments) {
128
- const find = (name) => attachments.find((a) => a.name === name);
129
- const net = find(attachments_js_1.ATTACHMENT_NAMES.network);
130
- if (net?.body) {
131
- try {
132
- testCase.networkRequests = JSON.parse(net.body.toString());
133
- }
134
- catch {
135
- /* ignore */
136
- }
137
- }
138
- const vitals = find(attachments_js_1.ATTACHMENT_NAMES.webVitals);
139
- if (vitals?.body) {
140
- try {
141
- testCase.webVitals = JSON.parse(vitals.body.toString());
142
- }
143
- catch {
144
- /* ignore */
145
- }
146
- }
147
- const consoleLog = find(attachments_js_1.ATTACHMENT_NAMES.console);
148
- if (consoleLog?.body) {
149
- try {
150
- testCase.consoleLogs = JSON.parse(consoleLog.body.toString());
151
- }
152
- catch {
153
- /* ignore */
154
- }
155
- }
156
- const aria = find(attachments_js_1.ATTACHMENT_NAMES.ariaSnapshot);
157
- if (aria?.body)
158
- testCase.ariaSnapshot = aria.body.toString();
159
- const pageState = find(attachments_js_1.ATTACHMENT_NAMES.pageState);
160
- if (pageState?.body) {
161
- try {
162
- testCase.pageState = JSON.parse(pageState.body.toString());
163
- }
164
- catch {
165
- /* ignore */
166
- }
167
- }
168
- }
169
- /** Compute SHA-256 hash and size for a single test case's trace file. Returns `null` when the case has no trace on disk. */
170
- async computeSingleTraceHash(testCase) {
171
- const tracePaths = this.findTraceFiles(testCase);
172
- let lastPath = null;
173
- for (const tp of tracePaths) {
174
- if (fs.existsSync(tp))
175
- lastPath = tp;
176
- }
177
- if (!lastPath)
178
- return null;
179
- const hash = crypto.createHash('sha256');
180
- await new Promise((resolve, reject) => {
181
- fs.createReadStream(lastPath)
182
- .on('data', (chunk) => hash.update(chunk))
183
- .on('end', resolve)
184
- .on('error', reject);
185
- });
186
- return {
187
- tracePath: lastPath,
188
- hash: hash.digest('hex'),
189
- size: fs.statSync(lastPath).size,
190
- };
191
- }
192
- /** Ask the server which trace hashes it already has, returning the set of hashes that are missing */
193
- async checkMissingTraces(httpClient, projectName, traceHashMap, auth) {
194
- if (traceHashMap.size === 0)
195
- return new Set();
196
- const hashes = [...traceHashMap.values()].map((h) => h.hash);
197
- try {
198
- const response = await httpClient.postJSON('/api/traces/check', { projectName, hashes }, auth);
199
- const missing = Array.isArray(response.missing) ? response.missing : hashes;
200
- return new Set(missing);
201
- }
202
- catch {
203
- return new Set(hashes);
204
- }
205
- }
206
- }
207
- exports.FileHandler = FileHandler;
@@ -1,23 +0,0 @@
1
- import type { HttpClient } from '../transport/http-client.js';
2
- import { Logger } from '../support/logger.js';
3
- /**
4
- * Persists a test-run payload to disk when all upload strategies fail, so the
5
- * data can be retried on the next run.
6
- */
7
- export declare class CrashRecovery {
8
- private readonly logger;
9
- private readonly filePath;
10
- /**
11
- * @param projectName Used to derive the temp-file name so recovery data is project-scoped.
12
- * @param logger Prefixed logger.
13
- */
14
- constructor(projectName: string, logger?: Logger);
15
- /** Serialise the payload to a temp file for later retry */
16
- save(data: Record<string, unknown>): void;
17
- /** Read the saved payload from disk, or return `null` if none exists */
18
- load(): Record<string, unknown> | null;
19
- /** Delete the recovery file from disk */
20
- clear(): void;
21
- /** Attempt to submit a previously saved payload. Clears the recovery file on success. */
22
- tryUpload(httpClient: HttpClient, auth?: string | null): Promise<void>;
23
- }
@@ -1,106 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.CrashRecovery = void 0;
37
- const path = __importStar(require("node:path"));
38
- const errors_js_1 = require("../support/errors.js");
39
- const os = __importStar(require("node:os"));
40
- const fs = __importStar(require("node:fs"));
41
- const instance_id_js_1 = require("../support/instance-id.js");
42
- const logger_js_1 = require("../support/logger.js");
43
- /**
44
- * Persists a test-run payload to disk when all upload strategies fail, so the
45
- * data can be retried on the next run.
46
- */
47
- class CrashRecovery {
48
- /**
49
- * @param projectName Used to derive the temp-file name so recovery data is project-scoped.
50
- * @param logger Prefixed logger.
51
- */
52
- constructor(projectName, logger = new logger_js_1.Logger()) {
53
- this.logger = logger;
54
- this.filePath = path.join(os.tmpdir(), `piwi-dashboard-recovery-${(0, instance_id_js_1.hashForProject)(projectName)}.json`);
55
- }
56
- /** Serialise the payload to a temp file for later retry */
57
- save(data) {
58
- try {
59
- fs.writeFileSync(this.filePath, JSON.stringify(data), 'utf8');
60
- this.logger.info('Saved recovery data for later upload');
61
- }
62
- catch (error) {
63
- this.logger.error(`Failed to save recovery data: ${(0, errors_js_1.errorMessage)(error)}`);
64
- }
65
- }
66
- /** Read the saved payload from disk, or return `null` if none exists */
67
- load() {
68
- try {
69
- if (fs.existsSync(this.filePath))
70
- return JSON.parse(fs.readFileSync(this.filePath, 'utf8'));
71
- }
72
- catch {
73
- // Non-fatal
74
- }
75
- return null;
76
- }
77
- /** Delete the recovery file from disk */
78
- clear() {
79
- try {
80
- if (fs.existsSync(this.filePath))
81
- fs.unlinkSync(this.filePath);
82
- }
83
- catch {
84
- // Non-fatal
85
- }
86
- }
87
- /** Attempt to submit a previously saved payload. Clears the recovery file on success. */
88
- async tryUpload(httpClient, auth) {
89
- const data = this.load();
90
- if (!data)
91
- return;
92
- this.logger.info('Found saved test data from a previous run, uploading...');
93
- // Strip instanceId so /submit doesn't cancel the current run (which shares
94
- // the same instanceId for the same project + hostname combination).
95
- delete data.instanceId;
96
- try {
97
- await httpClient.postJSON('/api/test-runs/submit', data, auth);
98
- this.logger.info('Successfully uploaded saved test data');
99
- this.clear();
100
- }
101
- catch (error) {
102
- this.logger.warn(`Could not upload saved test data: ${(0, errors_js_1.errorMessage)(error)}`);
103
- }
104
- }
105
- }
106
- exports.CrashRecovery = CrashRecovery;
@@ -1,17 +0,0 @@
1
- import type { StreamEvent } from '../../types.js';
2
- /**
3
- * Persistent JSONL buffer on disk. Events are appended to a temp file so they
4
- * survive a crash and can be replayed when the reporter restarts.
5
- */
6
- export declare class StreamBuffer {
7
- private readonly filePath;
8
- constructor(projectName: string);
9
- /** Append one or more events to the on-disk buffer */
10
- append(events: StreamEvent[]): void;
11
- /** Load all buffered events from disk, clearing the file */
12
- load(): StreamEvent[];
13
- /** Delete the buffer file from disk */
14
- clear(): void;
15
- /** Remove the buffer file if it is older than `maxAgeMs` (default 2 hours). Used on startup to discard orphaned data. */
16
- clearStale(maxAgeMs?: number): void;
17
- }
@@ -1,102 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.StreamBuffer = void 0;
37
- const path = __importStar(require("node:path"));
38
- const os = __importStar(require("node:os"));
39
- const fs = __importStar(require("node:fs"));
40
- const instance_id_js_1 = require("../support/instance-id.js");
41
- /**
42
- * Persistent JSONL buffer on disk. Events are appended to a temp file so they
43
- * survive a crash and can be replayed when the reporter restarts.
44
- */
45
- class StreamBuffer {
46
- constructor(projectName) {
47
- this.filePath = path.join(os.tmpdir(), `piwi-dashboard-stream-${(0, instance_id_js_1.hashForProject)(projectName)}.jsonl`);
48
- }
49
- /** Append one or more events to the on-disk buffer */
50
- append(events) {
51
- if (events.length === 0)
52
- return;
53
- try {
54
- const lines = events.map((e) => JSON.stringify(e) + '\n').join('');
55
- fs.appendFileSync(this.filePath, lines, 'utf8');
56
- }
57
- catch {
58
- // Non-fatal
59
- }
60
- }
61
- /** Load all buffered events from disk, clearing the file */
62
- load() {
63
- try {
64
- if (fs.existsSync(this.filePath)) {
65
- const content = fs.readFileSync(this.filePath, 'utf8');
66
- return content
67
- .split('\n')
68
- .filter(Boolean)
69
- .map((line) => JSON.parse(line));
70
- }
71
- }
72
- catch {
73
- // Non-fatal
74
- }
75
- return [];
76
- }
77
- /** Delete the buffer file from disk */
78
- clear() {
79
- try {
80
- if (fs.existsSync(this.filePath))
81
- fs.unlinkSync(this.filePath);
82
- }
83
- catch {
84
- // Non-fatal
85
- }
86
- }
87
- /** Remove the buffer file if it is older than `maxAgeMs` (default 2 hours). Used on startup to discard orphaned data. */
88
- clearStale(maxAgeMs = 7200000) {
89
- try {
90
- if (fs.existsSync(this.filePath)) {
91
- const stats = fs.statSync(this.filePath);
92
- if (Date.now() - stats.mtimeMs > maxAgeMs) {
93
- fs.unlinkSync(this.filePath);
94
- }
95
- }
96
- }
97
- catch {
98
- // Non-fatal
99
- }
100
- }
101
- }
102
- exports.StreamBuffer = StreamBuffer;
@@ -1,88 +0,0 @@
1
- import type { PiwiDashboardOptions, ShardInfo } from '../../public/options.js';
2
- import type { HttpClient } from '../transport/http-client.js';
3
- import type { StreamBuffer } from './stream-buffer.js';
4
- import type { CrashRecovery } from './crash-recovery.js';
5
- import type { Uploader } from '../submit/uploader.js';
6
- import type { FileHandler } from '../files/file-handler.js';
7
- import { Logger } from '../support/logger.js';
8
- import type { CollectedTestCase, StreamEvent, FilterDetails } from '../../types.js';
9
- /**
10
- * Manages the streaming protocol: queues events (begin / complete), flushes
11
- * them in batches, schedules retries on failure, and handles per-test-case
12
- * live file uploads.
13
- */
14
- export declare class StreamManager {
15
- private readonly httpClient;
16
- private readonly streamBuffer;
17
- private readonly recovery;
18
- private readonly uploader;
19
- private readonly fileHandler;
20
- private readonly options;
21
- private readonly logger;
22
- private pendingEvents;
23
- private pendingBeginEvents;
24
- private flushTimer;
25
- private flushPromises;
26
- private liveUploadPromises;
27
- private readonly limitLiveUpload;
28
- private retryCount;
29
- private retryTimer;
30
- private readonly maxRetryDelay;
31
- /**
32
- * Idle heartbeat: while the run is open but no events are flowing (e.g. a single
33
- * long test, or `beforeAll` setup), a periodic ping keeps the server's activity
34
- * timestamp fresh so the stale-run reaper can tell a live run from a crashed one.
35
- * The interval must stay well below the server's stale timeout.
36
- */
37
- private heartbeatTimer;
38
- private heartbeatStopped;
39
- private lastActivityAt;
40
- private readonly heartbeatInterval;
41
- /** Tracks cases whose files have already been uploaded live, so `uploadRemaining` can skip them. */
42
- private readonly uploadedCaseFiles;
43
- private _enabled;
44
- private _runId;
45
- private _token;
46
- private _auth;
47
- private _startPromise;
48
- /** Whether the streaming session is active */
49
- get enabled(): boolean;
50
- /** Server-assigned run ID (`null` until the stream opens) */
51
- get runId(): number | null;
52
- /** Stream authentication token (`null` until the stream opens) */
53
- get token(): string | null;
54
- /** Resolved auth string (API key or session cookie) used in stream requests */
55
- get auth(): string | null;
56
- /** Promise that resolves when the stream has been fully initialised */
57
- get startPromise(): Promise<void> | null;
58
- /**
59
- * @param httpClient HTTP client for server communication.
60
- * @param streamBuffer On-disk buffer for crash-safe event persistence.
61
- * @param recovery Crash-recovery handler for uploading stale payloads on startup.
62
- * @param uploader Uploader for per-test-case file uploads.
63
- * @param fileHandler File-discovery helper for finding traces and attachments.
64
- * @param options Piwi Dashboard reporter options.
65
- * @param logger Prefixed logger.
66
- */
67
- constructor(httpClient: HttpClient, streamBuffer: StreamBuffer, recovery: CrashRecovery, uploader: Uploader, fileHandler: FileHandler, options: PiwiDashboardOptions, logger?: Logger);
68
- /** Begin the streaming session after `onBegin` fires. Non-blocking — the actual handshake runs asynchronously. */
69
- start(startTime: string, metadata: Record<string, any>, instanceId: string, playwrightVersion?: string | null, reporterVersion?: string | null, shardInfo?: ShardInfo | null, isFullRun?: boolean, filterDetails?: FilterDetails | null): void;
70
- private _doStart;
71
- /** Queue a test-case `begin` event. Held in a pre-start buffer if the stream is not yet open, then prepended so it arrives before the matching `complete` event. */
72
- queueBeginEvent(event: StreamEvent): void;
73
- /** Queue a test-case event. Triggers an immediate flush when the batch size is reached, otherwise schedules a timer-based flush. */
74
- queueEvent(event: StreamEvent): void;
75
- /** Flush all pending events to the server. Returns a promise that resolves to `true` on success or `false` on failure (events are re-queued for retry). */
76
- flush(): Promise<boolean> | null;
77
- private scheduleRetry;
78
- private scheduleHeartbeat;
79
- private sendHeartbeat;
80
- /** Stop the idle heartbeat permanently (called when the run is wrapping up). */
81
- private stopHeartbeat;
82
- /** Drain all pending and buffered events before the run finishes. Retries up to 10 times with exponential back-off. */
83
- drain(): Promise<void>;
84
- /** Schedule a live upload of trace and attachment files for a test case. Skips cases with no files. Concurrency is limited to 2 simultaneous uploads. */
85
- scheduleLiveUpload(tc: CollectedTestCase): void;
86
- /** Wait for all live uploads to settle, then upload files for any test cases that weren't uploaded live */
87
- uploadRemaining(testCases: CollectedTestCase[]): Promise<void>;
88
- }