@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,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MetadataCollector = void 0;
4
- const child_process_1 = require("child_process");
5
- const logger_js_1 = require("./logger.js");
4
+ const node_child_process_1 = require("node:child_process");
5
+ const errors_js_1 = require("../support/errors.js");
6
+ const logger_js_1 = require("../support/logger.js");
6
7
  /**
7
8
  * Collects CI/CD environment metadata, SCM (git) information, and Playwright
8
9
  * config metadata to attach to each test run submission.
@@ -47,7 +48,7 @@ class MetadataCollector {
47
48
  const all = suite.allTests();
48
49
  if (all.length > 0) {
49
50
  const first = all[0];
50
- if (first?.parent?.project) {
51
+ if (first?.parent) {
51
52
  const proj = first.parent.project();
52
53
  if (proj?.metadata)
53
54
  metadata.playwrightProject = proj.metadata;
@@ -146,23 +147,25 @@ class MetadataCollector {
146
147
  const scm = {};
147
148
  try {
148
149
  const execOpts = { encoding: 'utf8', timeout: 5000, maxBuffer: 1024 * 1024 };
149
- scm.commit = (0, child_process_1.execSync)('git rev-parse HEAD', execOpts).trim();
150
- scm.branch = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', execOpts).trim();
151
- scm.author = (0, child_process_1.execSync)('git log -1 --pretty=format:"%an"', execOpts).trim();
152
- scm.commitMessage = (0, child_process_1.execSync)('git log -1 --pretty=format:"%s"', execOpts).trim();
150
+ scm.commit = (0, node_child_process_1.execSync)('git rev-parse HEAD', execOpts).trim();
151
+ scm.branch = (0, node_child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', execOpts).trim();
152
+ scm.author = (0, node_child_process_1.execSync)('git log -1 --pretty=format:"%an"', execOpts).trim();
153
+ scm.commitMessage = (0, node_child_process_1.execSync)('git log -1 --pretty=format:"%s"', execOpts).trim();
153
154
  try {
154
- scm.remoteUrl = (0, child_process_1.execSync)('git config --get remote.origin.url', execOpts).trim();
155
+ scm.remoteUrl = (0, node_child_process_1.execSync)('git config --get remote.origin.url', execOpts).trim();
155
156
  }
156
157
  catch {
157
158
  /* optional */
158
159
  }
159
160
  }
160
161
  catch (error) {
161
- this.logger.debug(`Git info not available: ${error.message}`);
162
+ this.logger.debug(`Git info not available: ${(0, errors_js_1.errorMessage)(error)}`);
162
163
  }
163
164
  return Object.keys(scm).length > 0 ? scm : undefined;
164
165
  }
165
166
  collectCiInfo() {
167
+ // Env vars are `string | undefined`; undefined values are dropped on JSON
168
+ // serialization, so collecting them directly preserves the prior behavior.
166
169
  const ci = {};
167
170
  const env = process.env;
168
171
  if (env.JENKINS_URL) {
@@ -1,4 +1,4 @@
1
- import type { TestAnnotation } from './types.js';
1
+ import type { TestAnnotation } from '../../types.js';
2
2
  /** Minimal structural shape for the annotation carriers (avoids importing Playwright types). */
3
3
  interface AnnotationCarrier {
4
4
  annotations?: ReadonlyArray<{
@@ -14,6 +14,12 @@ export interface FlatStep {
14
14
  title: string;
15
15
  duration: number;
16
16
  category: string;
17
+ /** Error message when the step failed (undefined when the step passed). */
18
+ error?: {
19
+ message: string;
20
+ };
21
+ /** True when the step carried an error — the signal the server needs for inline failure markers. */
22
+ failed?: boolean;
17
23
  }
18
24
  /** Step-event category restricted to the values `extractTestStepEvents` emits. */
19
25
  export type StepEventCategory = 'hook' | 'fixture' | 'test.step' | 'expect' | 'wait';
@@ -84,11 +84,16 @@ function categorizeStep(title, pwCategory) {
84
84
  function flattenSteps(steps) {
85
85
  const result = [];
86
86
  for (const step of steps) {
87
- result.push({
87
+ const flat = {
88
88
  title: step.title,
89
89
  duration: step.duration,
90
90
  category: categorizeStep(step.title, step.category),
91
- });
91
+ };
92
+ if (step.error?.message) {
93
+ flat.error = { message: step.error.message };
94
+ flat.failed = true;
95
+ }
96
+ result.push(flat);
92
97
  if (step.steps?.length > 0)
93
98
  result.push(...flattenSteps(step.steps));
94
99
  }
@@ -0,0 +1,45 @@
1
+ import type { PiwiDashboardOptions } from '../../public/options.js';
2
+ /**
3
+ * Single source of truth for the `PIWI_*` env-var → option mapping. Both
4
+ * `resolveOptions` (env → option) and `applyOptionsToEnv` (option → env, used
5
+ * by `wrapConfig` to bridge into the global-setup process) read these names so
6
+ * the mapping lives in exactly one place.
7
+ */
8
+ export declare const PIWI_ENV_KEYS: {
9
+ readonly serverUrl: "PIWI_DASHBOARD_URL";
10
+ readonly projectName: "PIWI_PROJECT_NAME";
11
+ readonly verbose: "PIWI_VERBOSE";
12
+ readonly apiKey: "PIWI_API_KEY";
13
+ readonly username: "PIWI_USERNAME";
14
+ readonly password: "PIWI_PASSWORD";
15
+ readonly environment: "PIWI_ENVIRONMENT";
16
+ readonly label: "PIWI_LABEL";
17
+ readonly runLabel: "PIWI_RUN_LABEL";
18
+ readonly streaming: "PIWI_STREAMING";
19
+ readonly streamingBatchSize: "PIWI_STREAMING_BATCH_SIZE";
20
+ readonly streamingBatchDelay: "PIWI_STREAMING_BATCH_DELAY";
21
+ readonly liveFileUploads: "PIWI_LIVE_FILE_UPLOADS";
22
+ readonly uploadTraces: "PIWI_UPLOAD_TRACES";
23
+ readonly uploadReport: "PIWI_UPLOAD_REPORT";
24
+ readonly captureLocators: "PIWI_CAPTURE_LOCATORS";
25
+ };
26
+ /**
27
+ * Merge raw user options with defaults, reading from `PIWI_*` env vars when
28
+ * options are not provided.
29
+ *
30
+ * Env semantics: env vars fill in values the caller didn't provide (fallback).
31
+ * They're applied to `raw` *before* the `DEFAULTS` merge so a built-in default
32
+ * never masks an env var (`PIWI_PROJECT_NAME` would otherwise be masked by the
33
+ * `default-project` default).
34
+ *
35
+ * One preserved quirk: `PIWI_VERBOSE` wins over both the default *and* an
36
+ * explicit user option.
37
+ */
38
+ export declare function resolveOptions(raw: Record<string, any>): PiwiDashboardOptions;
39
+ /**
40
+ * Write the options that the isolated `global-setup-module` process needs into
41
+ * `PIWI_*` env vars. `wrapConfig` calls this so the global setup (which runs
42
+ * `resolveOptions({})` in a separate module) picks up the same server/auth
43
+ * config the reporter instance uses. Only writes values that are actually set.
44
+ */
45
+ export declare function applyOptionsToEnv(options: PiwiDashboardOptions): void;
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PIWI_ENV_KEYS = void 0;
4
4
  exports.resolveOptions = resolveOptions;
5
5
  exports.applyOptionsToEnv = applyOptionsToEnv;
6
+ /**
7
+ * Built-in option defaults, merged *under* any user-provided or env-derived
8
+ * value (see `resolveOptions`).
9
+ */
6
10
  const DEFAULTS = {
7
11
  projectName: 'default-project',
8
12
  uploadTraces: true,
@@ -49,53 +53,64 @@ function readBool(val) {
49
53
  return undefined;
50
54
  return val === 'true';
51
55
  }
56
+ /**
57
+ * Declarative env → option fallback table. `resolveOptions` walks this instead
58
+ * of repeating ~15 near-identical merge lines, so adding an env-backed option
59
+ * means adding one row.
60
+ *
61
+ * Guard semantics (preserved from the original hand-written merges):
62
+ * - `string` / `number`: a *truthy* env value fills the option, so an empty
63
+ * string is ignored.
64
+ * - `bool`: any *defined* env value fills it (via `readBool`), so
65
+ * `PIWI_STREAMING=false` actually disables streaming.
66
+ *
67
+ * `verbose` is intentionally absent — it *overrides* rather than falls back, and
68
+ * is applied after the `DEFAULTS` merge in `resolveOptions`.
69
+ */
70
+ const ENV_FALLBACK_SPECS = [
71
+ { option: 'serverUrl', env: exports.PIWI_ENV_KEYS.serverUrl, kind: 'string' },
72
+ { option: 'projectName', env: exports.PIWI_ENV_KEYS.projectName, kind: 'string' },
73
+ { option: 'apiKey', env: exports.PIWI_ENV_KEYS.apiKey, kind: 'string' },
74
+ { option: 'username', env: exports.PIWI_ENV_KEYS.username, kind: 'string' },
75
+ { option: 'password', env: exports.PIWI_ENV_KEYS.password, kind: 'string' },
76
+ { option: 'environment', env: exports.PIWI_ENV_KEYS.environment, kind: 'string' },
77
+ { option: 'label', env: exports.PIWI_ENV_KEYS.label, kind: 'string' },
78
+ { option: 'runLabel', env: exports.PIWI_ENV_KEYS.runLabel, kind: 'string' },
79
+ { option: 'streaming', env: exports.PIWI_ENV_KEYS.streaming, kind: 'bool' },
80
+ { option: 'streamingBatchSize', env: exports.PIWI_ENV_KEYS.streamingBatchSize, kind: 'number' },
81
+ { option: 'streamingBatchDelay', env: exports.PIWI_ENV_KEYS.streamingBatchDelay, kind: 'number' },
82
+ { option: 'liveFileUploads', env: exports.PIWI_ENV_KEYS.liveFileUploads, kind: 'bool' },
83
+ { option: 'uploadTraces', env: exports.PIWI_ENV_KEYS.uploadTraces, kind: 'bool' },
84
+ { option: 'uploadReport', env: exports.PIWI_ENV_KEYS.uploadReport, kind: 'bool' },
85
+ { option: 'captureLocators', env: exports.PIWI_ENV_KEYS.captureLocators, kind: 'bool' },
86
+ ];
52
87
  /**
53
88
  * Merge raw user options with defaults, reading from `PIWI_*` env vars when
54
89
  * options are not provided.
55
90
  *
56
91
  * Env semantics: env vars fill in values the caller didn't provide (fallback).
57
92
  * They're applied to `raw` *before* the `DEFAULTS` merge so a built-in default
58
- * never masks an env var (the pre-Phase-4 `PIWI_PROJECT_NAME` was masked by the
59
- * `default-project` default — that quirk is now fixed).
93
+ * never masks an env var (`PIWI_PROJECT_NAME` would otherwise be masked by the
94
+ * `default-project` default).
60
95
  *
61
96
  * One preserved quirk: `PIWI_VERBOSE` wins over both the default *and* an
62
- * explicit user option, matching the pre-Phase-4 behavior.
97
+ * explicit user option.
63
98
  */
64
99
  function resolveOptions(raw) {
65
100
  const env = process.env;
66
101
  const mergedRaw = { ...raw };
67
- // String options: env fills in when the caller didn't provide one.
68
- if (mergedRaw.serverUrl === undefined && env[exports.PIWI_ENV_KEYS.serverUrl])
69
- mergedRaw.serverUrl = env[exports.PIWI_ENV_KEYS.serverUrl];
70
- if (mergedRaw.projectName === undefined && env[exports.PIWI_ENV_KEYS.projectName])
71
- mergedRaw.projectName = env[exports.PIWI_ENV_KEYS.projectName];
72
- if (mergedRaw.apiKey === undefined && env[exports.PIWI_ENV_KEYS.apiKey])
73
- mergedRaw.apiKey = env[exports.PIWI_ENV_KEYS.apiKey];
74
- if (mergedRaw.username === undefined && env[exports.PIWI_ENV_KEYS.username])
75
- mergedRaw.username = env[exports.PIWI_ENV_KEYS.username];
76
- if (mergedRaw.password === undefined && env[exports.PIWI_ENV_KEYS.password])
77
- mergedRaw.password = env[exports.PIWI_ENV_KEYS.password];
78
- if (mergedRaw.environment === undefined && env[exports.PIWI_ENV_KEYS.environment])
79
- mergedRaw.environment = env[exports.PIWI_ENV_KEYS.environment];
80
- if (mergedRaw.label === undefined && env[exports.PIWI_ENV_KEYS.label])
81
- mergedRaw.label = env[exports.PIWI_ENV_KEYS.label];
82
- if (mergedRaw.runLabel === undefined && env[exports.PIWI_ENV_KEYS.runLabel])
83
- mergedRaw.runLabel = env[exports.PIWI_ENV_KEYS.runLabel];
84
- // Boolean / numeric options: env fills in when the caller didn't provide one.
85
- if (mergedRaw.streaming === undefined && env[exports.PIWI_ENV_KEYS.streaming] !== undefined)
86
- mergedRaw.streaming = readBool(env[exports.PIWI_ENV_KEYS.streaming]);
87
- if (mergedRaw.streamingBatchSize === undefined && env[exports.PIWI_ENV_KEYS.streamingBatchSize])
88
- mergedRaw.streamingBatchSize = Number(env[exports.PIWI_ENV_KEYS.streamingBatchSize]);
89
- if (mergedRaw.streamingBatchDelay === undefined && env[exports.PIWI_ENV_KEYS.streamingBatchDelay])
90
- mergedRaw.streamingBatchDelay = Number(env[exports.PIWI_ENV_KEYS.streamingBatchDelay]);
91
- if (mergedRaw.liveFileUploads === undefined && env[exports.PIWI_ENV_KEYS.liveFileUploads] !== undefined)
92
- mergedRaw.liveFileUploads = readBool(env[exports.PIWI_ENV_KEYS.liveFileUploads]);
93
- if (mergedRaw.uploadTraces === undefined && env[exports.PIWI_ENV_KEYS.uploadTraces] !== undefined)
94
- mergedRaw.uploadTraces = readBool(env[exports.PIWI_ENV_KEYS.uploadTraces]);
95
- if (mergedRaw.uploadReport === undefined && env[exports.PIWI_ENV_KEYS.uploadReport] !== undefined)
96
- mergedRaw.uploadReport = readBool(env[exports.PIWI_ENV_KEYS.uploadReport]);
97
- if (mergedRaw.captureLocators === undefined && env[exports.PIWI_ENV_KEYS.captureLocators] !== undefined)
98
- mergedRaw.captureLocators = readBool(env[exports.PIWI_ENV_KEYS.captureLocators]);
102
+ for (const spec of ENV_FALLBACK_SPECS) {
103
+ if (mergedRaw[spec.option] !== undefined)
104
+ continue;
105
+ const value = env[spec.env];
106
+ if (spec.kind === 'bool') {
107
+ if (value !== undefined)
108
+ mergedRaw[spec.option] = readBool(value);
109
+ }
110
+ else if (value) {
111
+ mergedRaw[spec.option] = spec.kind === 'number' ? Number(value) : value;
112
+ }
113
+ }
99
114
  const opts = { ...DEFAULTS, ...mergedRaw };
100
115
  // Preserved quirk: PIWI_VERBOSE wins over both default and user option.
101
116
  if (env[exports.PIWI_ENV_KEYS.verbose] !== undefined)
@@ -0,0 +1,69 @@
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.compressDirectory = compressDirectory;
37
+ const fs = __importStar(require("node:fs"));
38
+ const path = __importStar(require("node:path"));
39
+ const zlib = __importStar(require("node:zlib"));
40
+ const node_util_1 = require("node:util");
41
+ const gzipAsync = (0, node_util_1.promisify)(zlib.gzip);
42
+ /**
43
+ * Recursively read a directory and pack all files into a gzip-compressed
44
+ * archive (a concatenation of length-prefixed path+content pairs).
45
+ */
46
+ async function compressDirectory(sourceDir) {
47
+ const files = [];
48
+ function collect(dir, baseDir = '') {
49
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
50
+ const full = path.join(dir, entry.name);
51
+ const rel = path.join(baseDir, entry.name);
52
+ if (entry.isDirectory())
53
+ collect(full, rel);
54
+ else if (entry.isFile())
55
+ files.push({ path: rel, content: fs.readFileSync(full) });
56
+ }
57
+ }
58
+ collect(sourceDir);
59
+ const parts = [];
60
+ for (const f of files) {
61
+ const pb = Buffer.from(f.path, 'utf8');
62
+ const plb = Buffer.allocUnsafe(4);
63
+ plb.writeUInt32LE(pb.length, 0);
64
+ const clb = Buffer.allocUnsafe(4);
65
+ clb.writeUInt32LE(f.content.length, 0);
66
+ parts.push(plb, pb, clb, f.content);
67
+ }
68
+ return await gzipAsync(Buffer.concat(parts), { level: 5 });
69
+ }
@@ -1,5 +1,5 @@
1
- import { Logger } from './logger.js';
2
- import type { CollectedTestCase, RawAttachment, TraceHashInfo } from './types.js';
1
+ import { Logger } from '../support/logger.js';
2
+ import type { CollectedTestCase, RawAttachment, TraceHashInfo } from '../../types.js';
3
3
  /**
4
4
  * File-system helpers for discovering report directories, trace files, and
5
5
  * test attachments, as well as compressing report directories and computing
@@ -16,7 +16,7 @@ export declare class FileHandler {
16
16
  compressReportDirectory(reportDir: string): Promise<Buffer | null>;
17
17
  /** Return resolved paths for all trace attachments on a test case */
18
18
  findTraceFiles(testCase: CollectedTestCase): string[];
19
- /** Return all non-trace, non-internal attachments from a test case. Skips `trace` and `piwi-dashboard-*` attachments. */
19
+ /** Return all non-trace, non-internal attachments from a test case. Skips `trace` and `piwi-*` attachments. */
20
20
  findAllAttachments(testCase: CollectedTestCase): Array<{
21
21
  name: string;
22
22
  path: string;
@@ -25,7 +25,7 @@ export declare class FileHandler {
25
25
  }>;
26
26
  /** Mapping of well-known report type names to their default output directories */
27
27
  getDefaultReportDirs(): Record<string, string>;
28
- /** Parse Piwi-internal attachment bodies (`piwi-dashboard-network`, `-web-vitals`, `-console`, `-aria-snapshot`) into structured fields on the test case */
28
+ /** Parse Piwi-internal attachment bodies (`piwi-network`, `piwi-web-vitals`, `piwi-console`, `piwi-aria-snapshot`) into structured fields on the test case */
29
29
  parsePerformanceAttachments(testCase: CollectedTestCase, attachments: RawAttachment[]): void;
30
30
  /** Compute SHA-256 hash and size for a single test case's trace file. Returns `null` when the case has no trace on disk. */
31
31
  computeSingleTraceHash(testCase: CollectedTestCase): Promise<TraceHashInfo | null>;
@@ -1,14 +1,46 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
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
+ })();
5
35
  Object.defineProperty(exports, "__esModule", { value: true });
6
36
  exports.FileHandler = void 0;
7
- const fs_1 = __importDefault(require("fs"));
8
- const path_1 = __importDefault(require("path"));
9
- const crypto_1 = __importDefault(require("crypto"));
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"));
10
41
  const compression_js_1 = require("./compression.js");
11
- const logger_js_1 = require("./logger.js");
42
+ const logger_js_1 = require("../support/logger.js");
43
+ const attachments_js_1 = require("../capture/attachments.js");
12
44
  /**
13
45
  * File-system helpers for discovering report directories, trace files, and
14
46
  * test attachments, as well as compressing report directories and computing
@@ -21,11 +53,11 @@ class FileHandler {
21
53
  /** Locate a Playwright HTML report directory containing `index.html`. Optionally override the search path. */
22
54
  findHTMLReportDirectory(customDir) {
23
55
  const possibleDirs = customDir
24
- ? [customDir, path_1.default.join(process.cwd(), customDir)]
25
- : ['playwright-report', './playwright-report', path_1.default.join(process.cwd(), 'playwright-report')];
56
+ ? [customDir, path.join(process.cwd(), customDir)]
57
+ : ['playwright-report', './playwright-report', path.join(process.cwd(), 'playwright-report')];
26
58
  for (const reportDir of possibleDirs) {
27
- if (fs_1.default.existsSync(reportDir) && fs_1.default.statSync(reportDir).isDirectory()) {
28
- if (fs_1.default.existsSync(path_1.default.join(reportDir, 'index.html')))
59
+ if (fs.existsSync(reportDir) && fs.statSync(reportDir).isDirectory()) {
60
+ if (fs.existsSync(path.join(reportDir, 'index.html')))
29
61
  return reportDir;
30
62
  }
31
63
  }
@@ -33,9 +65,9 @@ class FileHandler {
33
65
  }
34
66
  /** Check if `dir` exists and is a directory; also checks `path.join(process.cwd(), dir)` */
35
67
  findReportDirectory(dir) {
36
- const candidates = [dir, path_1.default.join(process.cwd(), dir)];
68
+ const candidates = [dir, path.join(process.cwd(), dir)];
37
69
  for (const candidate of candidates) {
38
- if (fs_1.default.existsSync(candidate) && fs_1.default.statSync(candidate).isDirectory())
70
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory())
39
71
  return candidate;
40
72
  }
41
73
  return null;
@@ -46,7 +78,7 @@ class FileHandler {
46
78
  return (await (0, compression_js_1.compressDirectory)(reportDir)) || null;
47
79
  }
48
80
  catch (error) {
49
- this.logger.warn(`Failed to compress report directory ${reportDir}: ${error.message}`);
81
+ this.logger.warn(`Failed to compress report directory ${reportDir}: ${(0, errors_js_1.errorMessage)(error)}`);
50
82
  return null;
51
83
  }
52
84
  }
@@ -56,26 +88,26 @@ class FileHandler {
56
88
  if (testCase.attachments) {
57
89
  for (const a of testCase.attachments) {
58
90
  if (a.name === 'trace' && a.path)
59
- set.add(path_1.default.resolve(a.path));
91
+ set.add(path.resolve(a.path));
60
92
  }
61
93
  }
62
94
  return Array.from(set);
63
95
  }
64
- /** Return all non-trace, non-internal attachments from a test case. Skips `trace` and `piwi-dashboard-*` attachments. */
96
+ /** Return all non-trace, non-internal attachments from a test case. Skips `trace` and `piwi-*` attachments. */
65
97
  findAllAttachments(testCase) {
66
98
  const result = [];
67
99
  if (testCase.attachments) {
68
100
  for (const a of testCase.attachments) {
69
101
  if (a.name === 'trace')
70
102
  continue;
71
- if (a.name?.startsWith('piwi-dashboard-'))
103
+ if (a.name && attachments_js_1.INTERNAL_ATTACHMENT_NAMES.has(a.name))
72
104
  continue;
73
- if (a.path && fs_1.default.existsSync(a.path)) {
105
+ if (a.path && fs.existsSync(a.path)) {
74
106
  result.push({
75
107
  name: a.name || 'attachment',
76
- path: path_1.default.resolve(a.path),
108
+ path: path.resolve(a.path),
77
109
  contentType: a.contentType || 'application/octet-stream',
78
- originalName: path_1.default.basename(a.path),
110
+ originalName: path.basename(a.path),
79
111
  });
80
112
  }
81
113
  }
@@ -91,10 +123,10 @@ class FileHandler {
91
123
  blob: 'blob-report',
92
124
  };
93
125
  }
94
- /** Parse Piwi-internal attachment bodies (`piwi-dashboard-network`, `-web-vitals`, `-console`, `-aria-snapshot`) into structured fields on the test case */
126
+ /** Parse Piwi-internal attachment bodies (`piwi-network`, `piwi-web-vitals`, `piwi-console`, `piwi-aria-snapshot`) into structured fields on the test case */
95
127
  parsePerformanceAttachments(testCase, attachments) {
96
128
  const find = (name) => attachments.find((a) => a.name === name);
97
- const net = find('piwi-dashboard-network');
129
+ const net = find(attachments_js_1.ATTACHMENT_NAMES.network);
98
130
  if (net?.body) {
99
131
  try {
100
132
  testCase.networkRequests = JSON.parse(net.body.toString());
@@ -103,7 +135,7 @@ class FileHandler {
103
135
  /* ignore */
104
136
  }
105
137
  }
106
- const vitals = find('piwi-dashboard-web-vitals');
138
+ const vitals = find(attachments_js_1.ATTACHMENT_NAMES.webVitals);
107
139
  if (vitals?.body) {
108
140
  try {
109
141
  testCase.webVitals = JSON.parse(vitals.body.toString());
@@ -112,7 +144,7 @@ class FileHandler {
112
144
  /* ignore */
113
145
  }
114
146
  }
115
- const consoleLog = find('piwi-dashboard-console');
147
+ const consoleLog = find(attachments_js_1.ATTACHMENT_NAMES.console);
116
148
  if (consoleLog?.body) {
117
149
  try {
118
150
  testCase.consoleLogs = JSON.parse(consoleLog.body.toString());
@@ -121,7 +153,7 @@ class FileHandler {
121
153
  /* ignore */
122
154
  }
123
155
  }
124
- const aria = find('piwi-dashboard-aria-snapshot');
156
+ const aria = find(attachments_js_1.ATTACHMENT_NAMES.ariaSnapshot);
125
157
  if (aria?.body)
126
158
  testCase.ariaSnapshot = aria.body.toString();
127
159
  }
@@ -130,14 +162,14 @@ class FileHandler {
130
162
  const tracePaths = this.findTraceFiles(testCase);
131
163
  let lastPath = null;
132
164
  for (const tp of tracePaths) {
133
- if (fs_1.default.existsSync(tp))
165
+ if (fs.existsSync(tp))
134
166
  lastPath = tp;
135
167
  }
136
168
  if (!lastPath)
137
169
  return null;
138
- const hash = crypto_1.default.createHash('sha256');
170
+ const hash = crypto.createHash('sha256');
139
171
  await new Promise((resolve, reject) => {
140
- fs_1.default.createReadStream(lastPath)
172
+ fs.createReadStream(lastPath)
141
173
  .on('data', (chunk) => hash.update(chunk))
142
174
  .on('end', resolve)
143
175
  .on('error', reject);
@@ -145,7 +177,7 @@ class FileHandler {
145
177
  return {
146
178
  tracePath: lastPath,
147
179
  hash: hash.digest('hex'),
148
- size: fs_1.default.statSync(lastPath).size,
180
+ size: fs.statSync(lastPath).size,
149
181
  };
150
182
  }
151
183
  /** Ask the server which trace hashes it already has, returning the set of hashes that are missing */
@@ -1,21 +1,21 @@
1
- import { HttpClient } from './http-client.js';
2
- import { Logger } from './logger.js';
1
+ import type { HttpClient } from '../transport/http-client.js';
2
+ import { Logger } from '../support/logger.js';
3
3
  /**
4
4
  * Persists a test-run payload to disk when all upload strategies fail, so the
5
5
  * data can be retried on the next run.
6
6
  */
7
7
  export declare class CrashRecovery {
8
- private filePath;
9
8
  private readonly logger;
9
+ private readonly filePath;
10
10
  /**
11
11
  * @param projectName Used to derive the temp-file name so recovery data is project-scoped.
12
12
  * @param logger Prefixed logger.
13
13
  */
14
14
  constructor(projectName: string, logger?: Logger);
15
15
  /** Serialise the payload to a temp file for later retry */
16
- save(data: Record<string, any>): void;
16
+ save(data: Record<string, unknown>): void;
17
17
  /** Read the saved payload from disk, or return `null` if none exists */
18
- load(): Record<string, any> | null;
18
+ load(): Record<string, unknown> | null;
19
19
  /** Delete the recovery file from disk */
20
20
  clear(): void;
21
21
  /** Attempt to submit a previously saved payload. Clears the recovery file on success. */
@@ -34,11 +34,12 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.CrashRecovery = void 0;
37
- const path = __importStar(require("path"));
38
- const os = __importStar(require("os"));
39
- const fs = __importStar(require("fs"));
40
- const helpers_js_1 = require("./helpers.js");
41
- const logger_js_1 = require("./logger.js");
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");
42
43
  /**
43
44
  * Persists a test-run payload to disk when all upload strategies fail, so the
44
45
  * data can be retried on the next run.
@@ -50,7 +51,7 @@ class CrashRecovery {
50
51
  */
51
52
  constructor(projectName, logger = new logger_js_1.Logger()) {
52
53
  this.logger = logger;
53
- this.filePath = path.join(os.tmpdir(), `piwi-dashboard-recovery-${(0, helpers_js_1.hashForProject)(projectName)}.json`);
54
+ this.filePath = path.join(os.tmpdir(), `piwi-dashboard-recovery-${(0, instance_id_js_1.hashForProject)(projectName)}.json`);
54
55
  }
55
56
  /** Serialise the payload to a temp file for later retry */
56
57
  save(data) {
@@ -59,7 +60,7 @@ class CrashRecovery {
59
60
  this.logger.info('Saved recovery data for later upload');
60
61
  }
61
62
  catch (error) {
62
- this.logger.error(`Failed to save recovery data: ${error.message}`);
63
+ this.logger.error(`Failed to save recovery data: ${(0, errors_js_1.errorMessage)(error)}`);
63
64
  }
64
65
  }
65
66
  /** Read the saved payload from disk, or return `null` if none exists */
@@ -98,7 +99,7 @@ class CrashRecovery {
98
99
  this.clear();
99
100
  }
100
101
  catch (error) {
101
- this.logger.warn(`Could not upload saved test data: ${error.message}`);
102
+ this.logger.warn(`Could not upload saved test data: ${(0, errors_js_1.errorMessage)(error)}`);
102
103
  }
103
104
  }
104
105
  }
@@ -1,10 +1,10 @@
1
- import type { StreamEvent } from './types.js';
1
+ import type { StreamEvent } from '../../types.js';
2
2
  /**
3
3
  * Persistent JSONL buffer on disk. Events are appended to a temp file so they
4
4
  * survive a crash and can be replayed when the reporter restarts.
5
5
  */
6
6
  export declare class StreamBuffer {
7
- private filePath;
7
+ private readonly filePath;
8
8
  constructor(projectName: string);
9
9
  /** Append one or more events to the on-disk buffer */
10
10
  append(events: StreamEvent[]): void;
@@ -34,17 +34,17 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.StreamBuffer = void 0;
37
- const path = __importStar(require("path"));
38
- const os = __importStar(require("os"));
39
- const fs = __importStar(require("fs"));
40
- const helpers_js_1 = require("./helpers.js");
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
41
  /**
42
42
  * Persistent JSONL buffer on disk. Events are appended to a temp file so they
43
43
  * survive a crash and can be replayed when the reporter restarts.
44
44
  */
45
45
  class StreamBuffer {
46
46
  constructor(projectName) {
47
- this.filePath = path.join(os.tmpdir(), `piwi-dashboard-stream-${(0, helpers_js_1.hashForProject)(projectName)}.jsonl`);
47
+ this.filePath = path.join(os.tmpdir(), `piwi-dashboard-stream-${(0, instance_id_js_1.hashForProject)(projectName)}.jsonl`);
48
48
  }
49
49
  /** Append one or more events to the on-disk buffer */
50
50
  append(events) {