@piwitests/reporter 0.5.0 → 0.6.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 (75) hide show
  1. package/README.md +37 -26
  2. package/dist/global-setup-module.js +2 -2
  3. package/dist/index.d.ts +13 -4
  4. package/dist/index.js +19 -5
  5. package/dist/internal/capture/attachments.d.ts +19 -0
  6. package/dist/internal/capture/attachments.js +22 -0
  7. package/dist/{fixtures.d.ts → internal/capture/capture-fixtures.d.ts} +5 -3
  8. package/dist/internal/capture/capture-fixtures.js +495 -0
  9. package/dist/{locator-healing.js → internal/capture/locator-healing.js} +11 -6
  10. package/dist/internal/collect/error-text.d.ts +18 -0
  11. package/dist/internal/collect/error-text.js +79 -0
  12. package/dist/{metadata-collector.d.ts → internal/collect/metadata-collector.d.ts} +2 -2
  13. package/dist/{metadata-collector.js → internal/collect/metadata-collector.js} +12 -9
  14. package/dist/{skip-classify.d.ts → internal/collect/skip-classify.d.ts} +1 -1
  15. package/dist/{step-analyzer.d.ts → internal/collect/step-analyzer.d.ts} +6 -0
  16. package/dist/{step-analyzer.js → internal/collect/step-analyzer.js} +7 -2
  17. package/dist/internal/config/env.d.ts +45 -0
  18. package/dist/{config.js → internal/config/env.js} +50 -35
  19. package/dist/internal/files/compression.js +69 -0
  20. package/dist/{file-handler.d.ts → internal/files/file-handler.d.ts} +4 -4
  21. package/dist/{file-handler.js → internal/files/file-handler.js} +61 -29
  22. package/dist/{crash-recovery.d.ts → internal/streaming/crash-recovery.d.ts} +5 -5
  23. package/dist/{crash-recovery.js → internal/streaming/crash-recovery.js} +9 -8
  24. package/dist/{stream-buffer.d.ts → internal/streaming/stream-buffer.d.ts} +2 -2
  25. package/dist/{stream-buffer.js → internal/streaming/stream-buffer.js} +5 -5
  26. package/dist/{stream-manager.d.ts → internal/streaming/stream-manager.d.ts} +8 -8
  27. package/dist/{stream-manager.js → internal/streaming/stream-manager.js} +16 -13
  28. package/dist/{run-submitter.d.ts → internal/submit/run-submitter.d.ts} +7 -7
  29. package/dist/{run-submitter.js → internal/submit/run-submitter.js} +11 -9
  30. package/dist/{serializer.d.ts → internal/submit/serializer.d.ts} +1 -1
  31. package/dist/{uploader.d.ts → internal/submit/uploader.d.ts} +8 -8
  32. package/dist/{uploader.js → internal/submit/uploader.js} +45 -10
  33. package/dist/internal/support/ci.d.ts +2 -0
  34. package/dist/internal/support/ci.js +32 -0
  35. package/dist/internal/support/cli-filters.d.ts +1 -0
  36. package/dist/internal/support/cli-filters.js +51 -0
  37. package/dist/internal/support/errors.d.ts +8 -0
  38. package/dist/internal/support/errors.js +15 -0
  39. package/dist/internal/support/instance-id.d.ts +4 -0
  40. package/dist/internal/support/instance-id.js +48 -0
  41. package/dist/internal/support/limiter.d.ts +2 -0
  42. package/dist/internal/support/limiter.js +27 -0
  43. package/dist/internal/support/setup-file.d.ts +13 -0
  44. package/dist/internal/support/setup-file.js +61 -0
  45. package/dist/internal/support/source-snippet.d.ts +12 -0
  46. package/dist/internal/support/source-snippet.js +97 -0
  47. package/dist/internal/support/worker-index.d.ts +7 -0
  48. package/dist/internal/support/worker-index.js +14 -0
  49. package/dist/{http-client.d.ts → internal/transport/http-client.d.ts} +11 -1
  50. package/dist/{http-client.js → internal/transport/http-client.js} +57 -10
  51. package/dist/{config-wrapper.d.ts → public/config-wrapper.d.ts} +1 -1
  52. package/dist/{config-wrapper.js → public/config-wrapper.js} +4 -4
  53. package/dist/public/global-setup.d.ts +13 -0
  54. package/dist/public/global-setup.js +146 -0
  55. package/dist/{config.d.ts → public/options.d.ts} +7 -45
  56. package/dist/public/options.js +2 -0
  57. package/dist/{reporter.d.ts → public/reporter.d.ts} +1 -1
  58. package/dist/{reporter.js → public/reporter.js} +44 -54
  59. package/dist/types/collected.d.ts +96 -0
  60. package/dist/types/collected.js +10 -0
  61. package/dist/types/wire.d.ts +174 -0
  62. package/dist/types/wire.js +14 -0
  63. package/dist/types.d.ts +8 -253
  64. package/dist/types.js +23 -10
  65. package/package.json +1 -6
  66. package/dist/compression.js +0 -39
  67. package/dist/fixtures.js +0 -336
  68. package/dist/helpers.d.ts +0 -44
  69. package/dist/helpers.js +0 -312
  70. /package/dist/{locator-healing.d.ts → internal/capture/locator-healing.d.ts} +0 -0
  71. /package/dist/{skip-classify.js → internal/collect/skip-classify.js} +0 -0
  72. /package/dist/{compression.d.ts → internal/files/compression.d.ts} +0 -0
  73. /package/dist/{serializer.js → internal/submit/serializer.js} +0 -0
  74. /package/dist/{logger.d.ts → internal/support/logger.d.ts} +0 -0
  75. /package/dist/{logger.js → internal/support/logger.js} +0 -0
@@ -1,11 +1,11 @@
1
- import type { PiwiDashboardOptions, ShardInfo } from './config.js';
2
- import { HttpClient } from './http-client.js';
3
- import { StreamBuffer } from './stream-buffer.js';
4
- import { CrashRecovery } from './crash-recovery.js';
5
- import { Uploader } from './uploader.js';
6
- import { FileHandler } from './file-handler.js';
7
- import { Logger } from './logger.js';
8
- import type { CollectedTestCase, StreamEvent, FilterDetails } from './types.js';
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
9
  /**
10
10
  * Manages the streaming protocol: queues events (begin / complete), flushes
11
11
  * them in batches, schedules retries on failure, and handles per-test-case
@@ -34,9 +34,12 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.StreamManager = void 0;
37
- const fs = __importStar(require("fs"));
38
- const logger_js_1 = require("./logger.js");
39
- const helpers_js_1 = require("./helpers.js");
37
+ const fs = __importStar(require("node:fs"));
38
+ const errors_js_1 = require("../support/errors.js");
39
+ const http_client_js_1 = require("../transport/http-client.js");
40
+ const logger_js_1 = require("../support/logger.js");
41
+ const limiter_js_1 = require("../support/limiter.js");
42
+ const setup_file_js_1 = require("../support/setup-file.js");
40
43
  /**
41
44
  * Manages the streaming protocol: queues events (begin / complete), flushes
42
45
  * them in batches, schedules retries on failure, and handles per-test-case
@@ -85,7 +88,7 @@ class StreamManager {
85
88
  this.flushTimer = null;
86
89
  this.flushPromises = [];
87
90
  this.liveUploadPromises = [];
88
- this.limitLiveUpload = (0, helpers_js_1.createLimiter)(2);
91
+ this.limitLiveUpload = (0, limiter_js_1.createLimiter)(2);
89
92
  this.retryCount = 0;
90
93
  this.retryTimer = null;
91
94
  this.maxRetryDelay = 30000;
@@ -112,7 +115,7 @@ class StreamManager {
112
115
  this._startPromise = this._doStart(startTime, metadata, instanceId, playwrightVersion, shardInfo, isFullRun, filterDetails);
113
116
  }
114
117
  async _doStart(startTime, metadata, instanceId, playwrightVersion, shardInfo, isFullRun, filterDetails) {
115
- const setupInfo = (0, helpers_js_1.readSetupInfo)(this.options.projectName);
118
+ const setupInfo = (0, setup_file_js_1.readSetupInfo)(this.options.projectName);
116
119
  try {
117
120
  this._auth = await this.httpClient.resolveAuth(this.options);
118
121
  await this.recovery.tryUpload(this.httpClient, this._auth);
@@ -132,11 +135,11 @@ class StreamManager {
132
135
  filterDetails,
133
136
  }, this._auth);
134
137
  }
135
- catch (beginError) {
138
+ catch (error) {
136
139
  // Stale setupInfo — the run was cancelled (e.g. by crash recovery submit).
137
140
  // Fall back to creating a fresh run instead of silently disabling streaming.
138
- if (!beginError.message?.includes('409'))
139
- throw beginError;
141
+ if (!(error instanceof http_client_js_1.HttpError && error.status === 409))
142
+ throw error;
140
143
  this.logger.debug(`Setup info expired, creating fresh run...`);
141
144
  response = await this.httpClient.postJSON('/api/test-runs/start', {
142
145
  projectName: this.options.projectName,
@@ -185,7 +188,7 @@ class StreamManager {
185
188
  }
186
189
  }
187
190
  catch (error) {
188
- this.logger.debug(`Streaming not available: ${error.message}. Will use batch mode.`);
191
+ this.logger.debug(`Streaming not available: ${(0, errors_js_1.errorMessage)(error)}. Will use batch mode.`);
189
192
  this._enabled = false;
190
193
  }
191
194
  }
@@ -277,7 +280,7 @@ class StreamManager {
277
280
  this.lastActivityAt = Date.now();
278
281
  }
279
282
  catch (error) {
280
- this.logger.debug(`Heartbeat failed: ${error.message}`);
283
+ this.logger.debug(`Heartbeat failed: ${(0, errors_js_1.errorMessage)(error)}`);
281
284
  }
282
285
  this.scheduleHeartbeat();
283
286
  }
@@ -354,9 +357,9 @@ class StreamManager {
354
357
  return;
355
358
  }
356
359
  catch (error) {
357
- const retryable = error.message?.includes('404');
360
+ const retryable = error instanceof http_client_js_1.HttpError && error.status === 404;
358
361
  if (!retryable || attempt === delays.length - 1) {
359
- this.logger.debug(`Live file upload failed for "${tc.title}": ${error.message}`);
362
+ this.logger.debug(`Live file upload failed for "${tc.title}": ${(0, errors_js_1.errorMessage)(error)}`);
360
363
  return; // The end-of-run pass retries cases that failed here
361
364
  }
362
365
  }
@@ -381,7 +384,7 @@ class StreamManager {
381
384
  this.uploadedCaseFiles.add(tc);
382
385
  }
383
386
  catch (error) {
384
- this.logger.warn(`Failed to upload files for "${tc.title}": ${error.message}`);
387
+ this.logger.warn(`Failed to upload files for "${tc.title}": ${(0, errors_js_1.errorMessage)(error)}`);
385
388
  }
386
389
  }
387
390
  }
@@ -1,11 +1,11 @@
1
1
  import type { FullResult } from '@playwright/test/reporter';
2
- import type { PiwiDashboardOptions, ShardInfo } from './config.js';
3
- import { HttpClient } from './http-client.js';
4
- import { Uploader } from './uploader.js';
5
- import { CrashRecovery } from './crash-recovery.js';
6
- import { StreamManager } from './stream-manager.js';
7
- import { Logger } from './logger.js';
8
- import type { CollectedTestCase, SetupStep, FilterDetails } from './types.js';
2
+ import type { PiwiDashboardOptions, ShardInfo } from '../../public/options.js';
3
+ import type { HttpClient } from '../transport/http-client.js';
4
+ import type { Uploader } from './uploader.js';
5
+ import type { CrashRecovery } from '../streaming/crash-recovery.js';
6
+ import type { StreamManager } from '../streaming/stream-manager.js';
7
+ import { Logger } from '../support/logger.js';
8
+ import type { CollectedTestCase, SetupStep, FilterDetails } from '../../types.js';
9
9
  /**
10
10
  * Snapshot of everything the reporter has collected by `onEnd`, handed off to
11
11
  * the `RunSubmitter` so the reporter itself stays a thin collect-and-hand-off
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RunSubmitter = void 0;
4
- const logger_js_1 = require("./logger.js");
5
- const step_analyzer_js_1 = require("./step-analyzer.js");
4
+ const errors_js_1 = require("../support/errors.js");
5
+ const http_client_js_1 = require("../transport/http-client.js");
6
+ const logger_js_1 = require("../support/logger.js");
7
+ const step_analyzer_js_1 = require("../collect/step-analyzer.js");
6
8
  const serializer_js_1 = require("./serializer.js");
7
9
  /**
8
10
  * Owns the three-tier submit/fallback ladder:
@@ -54,7 +56,7 @@ class RunSubmitter {
54
56
  auth = sm?.auth ?? (await this.httpClient.resolveAuth(run.options));
55
57
  }
56
58
  catch (error) {
57
- this.logger.error(`Authentication failed: ${error.message}`);
59
+ this.logger.error(`Authentication failed: ${(0, errors_js_1.errorMessage)(error)}`);
58
60
  throw error;
59
61
  }
60
62
  if (sm?.enabled && sm?.runId != null) {
@@ -140,13 +142,13 @@ class RunSubmitter {
140
142
  await this.uploader.uploadReportsForStreamingRun(run.options.projectName, sm.runId, this.reportOptions(run), run.startTime, auth);
141
143
  }
142
144
  catch (error) {
143
- this.logger.warn(`Failed to upload reports for streaming run: ${error.message}`);
145
+ this.logger.warn(`Failed to upload reports for streaming run: ${(0, errors_js_1.errorMessage)(error)}`);
144
146
  }
145
147
  }
146
148
  return true;
147
149
  }
148
150
  catch (error) {
149
- this.logger.warn(`Failed to finalize streaming run: ${error.message}`);
151
+ this.logger.warn(`Failed to finalize streaming run: ${(0, errors_js_1.errorMessage)(error)}`);
150
152
  this.logger.info('Falling back to batch upload...');
151
153
  return false;
152
154
  }
@@ -158,9 +160,9 @@ class RunSubmitter {
158
160
  return true;
159
161
  }
160
162
  catch (error) {
161
- if (error.message?.includes('401') && !auth)
163
+ if (error instanceof http_client_js_1.HttpError && error.status === 401 && !auth)
162
164
  throw error;
163
- this.logger.warn(`Failed to upload with files: ${error.message}`);
165
+ this.logger.warn(`Failed to upload with files: ${(0, errors_js_1.errorMessage)(error)}`);
164
166
  this.logger.info('Falling back to JSON upload...');
165
167
  return false;
166
168
  }
@@ -174,9 +176,9 @@ class RunSubmitter {
174
176
  catch (error) {
175
177
  // If the server returned 401 and no auth was configured, this is a
176
178
  // configuration error — throw so the caller knows it's fatal.
177
- if (error.message?.includes('401') && !auth)
179
+ if (error instanceof http_client_js_1.HttpError && error.status === 401 && !auth)
178
180
  throw error;
179
- this.logger.error(`All upload methods failed: ${error.message}`);
181
+ this.logger.error(`All upload methods failed: ${(0, errors_js_1.errorMessage)(error)}`);
180
182
  // Save the wire-serialized form so the recovery file matches the
181
183
  // original submit payload (no raw attachments / internal fields).
182
184
  this.recovery.save((0, serializer_js_1.serializeRun)(payload, { includeTestCases: true }));
@@ -1,5 +1,5 @@
1
1
  import type { FullResult } from '@playwright/test/reporter';
2
- import type { CollectedTestCase, WireTestCase } from './types.js';
2
+ import type { CollectedTestCase, WireTestCase } from '../../types.js';
3
3
  import type { RunPayload } from './uploader.js';
4
4
  /**
5
5
  * Resolve the overall Piwi run status from Playwright's `FullResult.status`
@@ -1,7 +1,7 @@
1
- import { HttpClient } from './http-client.js';
2
- import { FileHandler } from './file-handler.js';
3
- import { Logger } from './logger.js';
4
- import type { CollectedTestCase, FilterDetails } from './types.js';
1
+ import type { HttpClient } from '../transport/http-client.js';
2
+ import type { FileHandler } from '../files/file-handler.js';
3
+ import { Logger } from '../support/logger.js';
4
+ import type { CollectedTestCase, FilterDetails } from '../../types.js';
5
5
  /** Payload for a batch test-run submission */
6
6
  export interface RunPayload {
7
7
  /** Name of the project the run belongs to */
@@ -61,15 +61,15 @@ export interface ReportOptions {
61
61
  * uploads for already-submitted streaming runs.
62
62
  */
63
63
  export declare class Uploader {
64
- private httpClient;
65
- private fileHandler;
66
- private logger;
64
+ private readonly httpClient;
65
+ private readonly fileHandler;
66
+ private readonly logger;
67
67
  /**
68
68
  * @param httpClient HTTP client for server communication.
69
69
  * @param fileHandler File discovery and compression helper.
70
70
  * @param logger Prefixed logger.
71
71
  */
72
- constructor(httpClient: HttpClient, fileHandler: FileHandler, logger: Logger);
72
+ constructor(httpClient: HttpClient, fileHandler: FileHandler, logger?: Logger);
73
73
  /** Submit test results as a plain JSON payload (no file attachments) */
74
74
  uploadJSON(payload: RunPayload, auth: string | null): Promise<any>;
75
75
  /** Submit test results as a multipart form with trace files and compressed report directories */
@@ -1,12 +1,47 @@
1
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
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
5
38
  Object.defineProperty(exports, "__esModule", { value: true });
6
39
  exports.Uploader = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- const path_1 = __importDefault(require("path"));
40
+ const fs = __importStar(require("node:fs"));
41
+ const path = __importStar(require("node:path"));
9
42
  const form_data_1 = __importDefault(require("form-data"));
43
+ const http_client_js_1 = require("../transport/http-client.js");
44
+ const logger_js_1 = require("../support/logger.js");
10
45
  const serializer_js_1 = require("./serializer.js");
11
46
  /**
12
47
  * Handles all upload strategies: plain JSON, multipart (with reports and
@@ -19,7 +54,7 @@ class Uploader {
19
54
  * @param fileHandler File discovery and compression helper.
20
55
  * @param logger Prefixed logger.
21
56
  */
22
- constructor(httpClient, fileHandler, logger) {
57
+ constructor(httpClient, fileHandler, logger = new logger_js_1.Logger()) {
23
58
  this.httpClient = httpClient;
24
59
  this.fileHandler = fileHandler;
25
60
  this.logger = logger;
@@ -104,15 +139,15 @@ class Uploader {
104
139
  if (traceInfo) {
105
140
  form.append('trace_hash', traceInfo.hash);
106
141
  if (includeTraceFile) {
107
- form.append('trace', fs_1.default.createReadStream(traceInfo.tracePath), {
108
- filename: path_1.default.basename(traceInfo.tracePath),
142
+ form.append('trace', fs.createReadStream(traceInfo.tracePath), {
143
+ filename: path.basename(traceInfo.tracePath),
109
144
  });
110
145
  }
111
146
  }
112
147
  if (attachments.length > 0) {
113
148
  form.append('attach_meta', JSON.stringify(attachments.map((a) => ({ name: a.name, contentType: a.contentType, originalName: a.originalName }))));
114
149
  for (const a of attachments) {
115
- form.append('attach_file', fs_1.default.createReadStream(a.path), { filename: a.originalName });
150
+ form.append('attach_file', fs.createReadStream(a.path), { filename: a.originalName });
116
151
  }
117
152
  }
118
153
  return form;
@@ -123,7 +158,7 @@ class Uploader {
123
158
  }
124
159
  catch (error) {
125
160
  // 422: the server doesn't have the blob after all — resend with the file
126
- if (traceInfo && !includeTraceFile && error.message?.includes('422')) {
161
+ if (traceInfo && !includeTraceFile && error instanceof http_client_js_1.HttpError && error.status === 422) {
127
162
  await this.httpClient.postFormData(`/api/test-runs/${runId}/case-files`, buildForm(true), auth);
128
163
  }
129
164
  else {
@@ -168,7 +203,7 @@ class Uploader {
168
203
  originalName: a.originalName,
169
204
  }))));
170
205
  for (const a of attachments) {
171
- form.append(`attach_file_${i}`, fs_1.default.createReadStream(a.path), { filename: a.originalName });
206
+ form.append(`attach_file_${i}`, fs.createReadStream(a.path), { filename: a.originalName });
172
207
  attachmentCount++;
173
208
  }
174
209
  }
@@ -179,8 +214,8 @@ class Uploader {
179
214
  let traceCount = 0;
180
215
  for (const [i, tc] of testCases.entries()) {
181
216
  for (const tp of this.fileHandler.findTraceFiles(tc)) {
182
- if (fs_1.default.existsSync(tp)) {
183
- form.append(`trace_${i}`, fs_1.default.createReadStream(tp), { filename: path_1.default.basename(tp) });
217
+ if (fs.existsSync(tp)) {
218
+ form.append(`trace_${i}`, fs.createReadStream(tp), { filename: path.basename(tp) });
184
219
  traceCount++;
185
220
  }
186
221
  }
@@ -0,0 +1,2 @@
1
+ /** Detect a stable CI run label from well-known environment variables. Returns null outside CI. */
2
+ export declare function detectCiRunLabel(): string | null;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectCiRunLabel = detectCiRunLabel;
4
+ /** Detect a stable CI run label from well-known environment variables. Returns null outside CI. */
5
+ function detectCiRunLabel() {
6
+ const env = process.env;
7
+ if (env.GITHUB_ACTIONS && env.GITHUB_RUN_ID)
8
+ return env.GITHUB_RUN_ID;
9
+ if (env.GITLAB_CI && env.CI_PIPELINE_ID)
10
+ return env.CI_PIPELINE_ID;
11
+ if (env.CIRCLECI && env.CIRCLE_WORKFLOW_ID)
12
+ return env.CIRCLE_WORKFLOW_ID;
13
+ if (env.TRAVIS && env.TRAVIS_BUILD_ID)
14
+ return env.TRAVIS_BUILD_ID;
15
+ if (env.TF_BUILD && env.BUILD_BUILDID)
16
+ return env.BUILD_BUILDID;
17
+ if (env.JENKINS_URL && env.BUILD_ID)
18
+ return env.BUILD_ID;
19
+ if (env.BUILDKITE_BUILD_ID)
20
+ return env.BUILDKITE_BUILD_ID;
21
+ if (env.TEAMCITY_BUILD_ID)
22
+ return env.TEAMCITY_BUILD_ID;
23
+ if (env.BITBUCKET_BUILD_NUMBER)
24
+ return env.BITBUCKET_BUILD_NUMBER;
25
+ if (env.SEMAPHORE_WORKFLOW_ID)
26
+ return env.SEMAPHORE_WORKFLOW_ID;
27
+ if (env.APPVEYOR_BUILD_ID)
28
+ return env.APPVEYOR_BUILD_ID;
29
+ if (env.DRONE_BUILD_NUMBER)
30
+ return env.DRONE_BUILD_NUMBER;
31
+ return null;
32
+ }
@@ -0,0 +1 @@
1
+ export declare function detectCliFileFilters(argv?: string[]): string[];
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectCliFileFilters = detectCliFileFilters;
4
+ /**
5
+ * Extract positional file/path filters from a Playwright CLI invocation
6
+ * (e.g. `playwright test tests/login.spec.ts auth/`). These narrow a run to
7
+ * specific files and are NOT reflected in `config.grep`, so they're the only
8
+ * signal that a file-filtered run is partial. Returns an empty array when the
9
+ * full suite was requested.
10
+ *
11
+ * Best-effort: skips the values of known value-taking options so they aren't
12
+ * mistaken for file filters. `--flag=value` forms consume no extra token.
13
+ */
14
+ const PW_VALUE_FLAGS = new Set([
15
+ '-c',
16
+ '--config',
17
+ '-j',
18
+ '--workers',
19
+ '-g',
20
+ '--grep',
21
+ '--grep-invert',
22
+ '--global-timeout',
23
+ '--max-failures',
24
+ '--output',
25
+ '-p',
26
+ '--project',
27
+ '--repeat-each',
28
+ '--reporter',
29
+ '--retries',
30
+ '--shard',
31
+ '--timeout',
32
+ '--trace',
33
+ ]);
34
+ function detectCliFileFilters(argv = process.argv) {
35
+ // Drop the node executable + script path, then start after the `test` subcommand.
36
+ const args = argv.slice(2);
37
+ const testIdx = args.indexOf('test');
38
+ const rest = testIdx >= 0 ? args.slice(testIdx + 1) : args;
39
+ const files = [];
40
+ for (let i = 0; i < rest.length; i++) {
41
+ const tok = rest[i];
42
+ if (tok.startsWith('-')) {
43
+ // A bare value-taking flag (no `=`) consumes the next token as its value.
44
+ if (!tok.includes('=') && PW_VALUE_FLAGS.has(tok))
45
+ i++;
46
+ continue;
47
+ }
48
+ files.push(tok);
49
+ }
50
+ return files;
51
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Extract a human-readable message from an unknown thrown value.
3
+ *
4
+ * `catch` bindings are typed `unknown` (TypeScript strict mode), so reach for
5
+ * this instead of `(error as any).message` — it returns `error.message` for any
6
+ * `Error` (including `HttpError`) and a stringified form otherwise.
7
+ */
8
+ export declare function errorMessage(error: unknown): string;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.errorMessage = errorMessage;
4
+ /**
5
+ * Extract a human-readable message from an unknown thrown value.
6
+ *
7
+ * `catch` bindings are typed `unknown` (TypeScript strict mode), so reach for
8
+ * this instead of `(error as any).message` — it returns `error.message` for any
9
+ * `Error` (including `HttpError`) and a stringified form otherwise.
10
+ */
11
+ function errorMessage(error) {
12
+ if (error instanceof Error)
13
+ return error.message;
14
+ return String(error);
15
+ }
@@ -0,0 +1,4 @@
1
+ /** Produce a deterministic short SHA-1 hex hash for a project name (used in temp file names) */
2
+ export declare function hashForProject(projectName: string): string;
3
+ /** Derive a unique instance identifier by hashing hostname + projectName (or runLabel + projectName when sharding) */
4
+ export declare function computeInstanceId(projectName: string, runLabel?: string | null): string;
@@ -0,0 +1,48 @@
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.hashForProject = hashForProject;
37
+ exports.computeInstanceId = computeInstanceId;
38
+ const crypto = __importStar(require("node:crypto"));
39
+ const os = __importStar(require("node:os"));
40
+ /** Produce a deterministic short SHA-1 hex hash for a project name (used in temp file names) */
41
+ function hashForProject(projectName) {
42
+ return crypto.createHash('sha1').update(projectName).digest('hex').slice(0, 16);
43
+ }
44
+ /** Derive a unique instance identifier by hashing hostname + projectName (or runLabel + projectName when sharding) */
45
+ function computeInstanceId(projectName, runLabel) {
46
+ const seed = runLabel ? `${projectName}|${runLabel}` : `${os.hostname()}|${projectName}`;
47
+ return crypto.createHash('sha256').update(seed).digest('hex').slice(0, 16);
48
+ }
@@ -0,0 +1,2 @@
1
+ /** Create a concurrency limiter that ensures at most `maxConcurrent` async operations run simultaneously */
2
+ export declare function createLimiter(maxConcurrent: number): <T>(fn: () => Promise<T>) => Promise<T>;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createLimiter = createLimiter;
4
+ /** Create a concurrency limiter that ensures at most `maxConcurrent` async operations run simultaneously */
5
+ function createLimiter(maxConcurrent) {
6
+ const limitValue = Math.max(1, Math.floor(maxConcurrent));
7
+ let active = 0;
8
+ const queue = [];
9
+ const next = () => {
10
+ active--;
11
+ const run = queue.shift();
12
+ if (run)
13
+ run();
14
+ };
15
+ return function limit(fn) {
16
+ return new Promise((resolve, reject) => {
17
+ const run = () => {
18
+ active++;
19
+ Promise.resolve().then(fn).then(resolve, reject).finally(next);
20
+ };
21
+ if (active < limitValue)
22
+ run();
23
+ else
24
+ queue.push(run);
25
+ });
26
+ };
27
+ }
@@ -0,0 +1,13 @@
1
+ /** Return the temp-file path used to exchange setup info between globalSetup and the reporter instance */
2
+ export declare function getSetupFilePath(projectName: string): string;
3
+ /** Information saved by the global setup for the reporter instance to consume */
4
+ export interface SetupInfo {
5
+ /** Server-assigned run ID */
6
+ runId: number;
7
+ /** One-time token used to authenticate the /begin call */
8
+ setupToken: string;
9
+ /** Project name this run belongs to */
10
+ projectName: string;
11
+ }
12
+ /** Read and delete the setup info file for the given project. Returns `null` when no file exists. */
13
+ export declare function readSetupInfo(projectName: string): SetupInfo | null;
@@ -0,0 +1,61 @@
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.getSetupFilePath = getSetupFilePath;
37
+ exports.readSetupInfo = readSetupInfo;
38
+ const path = __importStar(require("node:path"));
39
+ const os = __importStar(require("node:os"));
40
+ const fs = __importStar(require("node:fs"));
41
+ const instance_id_js_1 = require("./instance-id.js");
42
+ /** Return the temp-file path used to exchange setup info between globalSetup and the reporter instance */
43
+ function getSetupFilePath(projectName) {
44
+ return path.join(os.tmpdir(), `piwi-dashboard-setup-${(0, instance_id_js_1.hashForProject)(projectName)}.json`);
45
+ }
46
+ /** Read and delete the setup info file for the given project. Returns `null` when no file exists. */
47
+ function readSetupInfo(projectName) {
48
+ const setupFile = getSetupFilePath(projectName);
49
+ try {
50
+ if (fs.existsSync(setupFile)) {
51
+ const info = JSON.parse(fs.readFileSync(setupFile, 'utf8'));
52
+ fs.unlinkSync(setupFile);
53
+ if (info.projectName === projectName)
54
+ return info;
55
+ }
56
+ }
57
+ catch {
58
+ /* ignore */
59
+ }
60
+ return null;
61
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Read a snippet of source code surrounding the declaration line, optionally
3
+ * highlighting a separate failing line. Returns a formatted string with line
4
+ * numbers and markers (`>` for the failing line, `*` for the declaration), or
5
+ * `null` on error.
6
+ */
7
+ export declare function readSourceSnippet(file: string, declLine: number, context: number, failingLine?: number): string | null;
8
+ /**
9
+ * Extract the line number of the first stack frame inside `testFile` from an
10
+ * error's stack trace. Falls back to `declarationLine`.
11
+ */
12
+ export declare function extractFailingLine(errorText: string | null | undefined, testFile: string, declarationLine: number): number;