@piwitests/reporter 0.21.0 → 0.22.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.
package/dist/cli/index.js CHANGED
@@ -26,7 +26,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
26
26
  // src/cli/gate.ts
27
27
  var fs = __toESM(require("fs"));
28
28
 
29
- // ../packages/core/src/gate.ts
29
+ // ../core/src/gate.ts
30
30
  function formatGateResult(result) {
31
31
  const { facts } = result;
32
32
  const lines = [
@@ -222,6 +222,32 @@ var HttpClient = class {
222
222
  this.serverUrl = serverUrl;
223
223
  this.logger = logger;
224
224
  this.timeout = timeout;
225
+ this.warnIfInsecureTransport();
226
+ }
227
+ /**
228
+ * Warn once when the dashboard URL is plaintext `http://` to a non-loopback
229
+ * host. The reporter sends the API key as a Bearer token — and uploads
230
+ * captured test data, including request URLs whose query strings the server
231
+ * only strips after receipt — over this connection, so a non-TLS remote
232
+ * endpoint exposes the key and payloads on the wire. Loopback (localhost /
233
+ * 127.0.0.1 / ::1) is exempt: that traffic never leaves the machine. This
234
+ * warns rather than refuses so internal HTTP deployments (e.g. behind a VPN)
235
+ * still work, but the exposure is no longer silent.
236
+ */
237
+ warnIfInsecureTransport() {
238
+ let url;
239
+ try {
240
+ url = new import_node_url.URL(this.serverUrl);
241
+ } catch {
242
+ return;
243
+ }
244
+ if (url.protocol !== "http:") return;
245
+ const host = url.hostname.toLowerCase();
246
+ const isLoopback = host === "localhost" || host.endsWith(".localhost") || host === "127.0.0.1" || host === "::1" || host === "[::1]";
247
+ if (isLoopback) return;
248
+ this.logger.warn(
249
+ `Dashboard URL ${this.serverUrl} uses plaintext http:// \u2014 the API key and captured test data are transmitted unencrypted. Use https:// for any non-local server.`
250
+ );
225
251
  }
226
252
  /** Base URL of the Piwi Dashboard server this client talks to (used to print run links). */
227
253
  get baseUrl() {
package/dist/index.js CHANGED
@@ -245,6 +245,32 @@ var HttpClient = class {
245
245
  this.serverUrl = serverUrl;
246
246
  this.logger = logger;
247
247
  this.timeout = timeout;
248
+ this.warnIfInsecureTransport();
249
+ }
250
+ /**
251
+ * Warn once when the dashboard URL is plaintext `http://` to a non-loopback
252
+ * host. The reporter sends the API key as a Bearer token — and uploads
253
+ * captured test data, including request URLs whose query strings the server
254
+ * only strips after receipt — over this connection, so a non-TLS remote
255
+ * endpoint exposes the key and payloads on the wire. Loopback (localhost /
256
+ * 127.0.0.1 / ::1) is exempt: that traffic never leaves the machine. This
257
+ * warns rather than refuses so internal HTTP deployments (e.g. behind a VPN)
258
+ * still work, but the exposure is no longer silent.
259
+ */
260
+ warnIfInsecureTransport() {
261
+ let url;
262
+ try {
263
+ url = new import_node_url.URL(this.serverUrl);
264
+ } catch {
265
+ return;
266
+ }
267
+ if (url.protocol !== "http:") return;
268
+ const host = url.hostname.toLowerCase();
269
+ const isLoopback = host === "localhost" || host.endsWith(".localhost") || host === "127.0.0.1" || host === "::1" || host === "[::1]";
270
+ if (isLoopback) return;
271
+ this.logger.warn(
272
+ `Dashboard URL ${this.serverUrl} uses plaintext http:// \u2014 the API key and captured test data are transmitted unencrypted. Use https:// for any non-local server.`
273
+ );
248
274
  }
249
275
  /** Base URL of the Piwi Dashboard server this client talks to (used to print run links). */
250
276
  get baseUrl() {
@@ -1572,7 +1598,7 @@ var StreamManager = class {
1572
1598
  }
1573
1599
  };
1574
1600
 
1575
- // ../packages/core/src/step-analysis.ts
1601
+ // ../core/src/step-analysis.ts
1576
1602
  function categorizeStep(title, pwCategory) {
1577
1603
  if (!title) return "other";
1578
1604
  if (pwCategory === "hook" || pwCategory === "fixture") return pwCategory;
@@ -2016,7 +2042,7 @@ function wrapConfig(config, piwiOptions) {
2016
2042
  };
2017
2043
  }
2018
2044
 
2019
- // ../packages/core/src/status-classify.ts
2045
+ // ../core/src/status-classify.ts
2020
2046
  function mergeAnnotations(test, result) {
2021
2047
  const out = [];
2022
2048
  const seen = /* @__PURE__ */ new Set();
@@ -2036,7 +2062,7 @@ function classifyStatus(rawStatus, annotations) {
2036
2062
  return intentional ? "skipped" : "didnotrun";
2037
2063
  }
2038
2064
 
2039
- // ../packages/core/src/test-meta.ts
2065
+ // ../core/src/test-meta.ts
2040
2066
  var PIWI_ANNOTATION_PREFIX = "piwi:";
2041
2067
  var TEST_PRIORITIES = ["critical", "high", "medium", "low"];
2042
2068
  var MAX_TEST_TAGS = 20;
@@ -2123,7 +2149,7 @@ function collectTestMetadata(annotations) {
2123
2149
  // src/internal/collect/error-text.ts
2124
2150
  var path12 = __toESM(require("path"));
2125
2151
 
2126
- // ../packages/core/src/error-text.ts
2152
+ // ../core/src/error-text.ts
2127
2153
  function joinErrorMessages(errors) {
2128
2154
  if (!errors || errors.length === 0) return null;
2129
2155
  const seen = /* @__PURE__ */ new Set();
@@ -2766,7 +2792,7 @@ var PiwiDashboardReporter = class {
2766
2792
  // src/internal/capture/capture-fixtures.ts
2767
2793
  var import_node_zlib = require("zlib");
2768
2794
 
2769
- // ../packages/picker-dom/src/probe.ts
2795
+ // ../picker-dom/src/probe.ts
2770
2796
  function probeElementAttrs(el, arg) {
2771
2797
  const { keep, tagRoles, inputRoles, roleSources, includeStructural, includeLabelText } = arg;
2772
2798
  const attrMap = {};
@@ -3052,7 +3078,7 @@ function probeElementAttrs(el, arg) {
3052
3078
  };
3053
3079
  }
3054
3080
 
3055
- // ../packages/picker-dom/src/overlay-element.ts
3081
+ // ../picker-dom/src/overlay-element.ts
3056
3082
  function installPickerOverlay(arg) {
3057
3083
  const g = globalThis;
3058
3084
  const doc = g.document;
@@ -3329,7 +3355,7 @@ function installPickerOverlay(arg) {
3329
3355
  }
3330
3356
  }
3331
3357
 
3332
- // ../packages/picker-dom/src/overlay-anchors.ts
3358
+ // ../picker-dom/src/overlay-anchors.ts
3333
3359
  function showAnchorPicker(arg) {
3334
3360
  const g = globalThis;
3335
3361
  const doc = g.document;
@@ -3592,7 +3618,7 @@ function showAnchorPicker(arg) {
3592
3618
  doc.body.appendChild(panel);
3593
3619
  }
3594
3620
 
3595
- // ../packages/picker-dom/src/overlay-confirm.ts
3621
+ // ../picker-dom/src/overlay-confirm.ts
3596
3622
  function showPickerChoices(arg) {
3597
3623
  const g = globalThis;
3598
3624
  const doc = g.document;
@@ -3675,7 +3701,7 @@ function showPickerChoices(arg) {
3675
3701
  doc.body.appendChild(wrap);
3676
3702
  }
3677
3703
 
3678
- // ../packages/core/src/locator-generation.ts
3704
+ // ../core/src/locator-generation.ts
3679
3705
  var TAG_TO_ROLE = {
3680
3706
  a: "link",
3681
3707
  button: "button",
@@ -4026,7 +4052,7 @@ function approximateAccessibleName(attrs) {
4026
4052
  return null;
4027
4053
  }
4028
4054
 
4029
- // ../packages/picker-dom/src/anchor-alternatives.ts
4055
+ // ../picker-dom/src/anchor-alternatives.ts
4030
4056
  var escStr = (s) => s.replaceAll("\\", "\\\\").replaceAll("'", "\\'");
4031
4057
  var cssIdSelector = (id) => /^[A-Za-z_][A-Za-z0-9_-]*$/.test(id) ? `#${id}` : `[id="${id.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"]`;
4032
4058
  var ANCHOR_KIND_SCORES = { testid: 80, id: 74, labeledRole: 68, role: 62 };
@@ -4105,7 +4131,7 @@ function mergeCandidates(base, extra) {
4105
4131
  // src/internal/capture/locator-healing.ts
4106
4132
  var path15 = __toESM(require("path"));
4107
4133
 
4108
- // ../packages/core/src/locator-fingerprint.ts
4134
+ // ../core/src/locator-fingerprint.ts
4109
4135
  var PRESENT_SIMILARITY = 0.8;
4110
4136
  var MATCH_SIMILARITY = 0.2;
4111
4137
  function parseAriaCandidates(ariaSnapshot) {
@@ -4171,7 +4197,7 @@ function matchRenamedElement(fp, candidates) {
4171
4197
  return null;
4172
4198
  }
4173
4199
 
4174
- // ../packages/core/src/locator-methods.ts
4200
+ // ../core/src/locator-methods.ts
4175
4201
  var LOCATOR_BUILDER_METHODS = [
4176
4202
  "getByRole",
4177
4203
  "getByTestId",
@@ -41,7 +41,7 @@ __export(capture_fixtures_exports, {
41
41
  module.exports = __toCommonJS(capture_fixtures_exports);
42
42
  var import_node_zlib = require("zlib");
43
43
 
44
- // ../packages/picker-dom/src/probe.ts
44
+ // ../picker-dom/src/probe.ts
45
45
  function probeElementAttrs(el, arg) {
46
46
  const { keep, tagRoles, inputRoles, roleSources, includeStructural, includeLabelText } = arg;
47
47
  const attrMap = {};
@@ -327,7 +327,7 @@ function probeElementAttrs(el, arg) {
327
327
  };
328
328
  }
329
329
 
330
- // ../packages/picker-dom/src/overlay-element.ts
330
+ // ../picker-dom/src/overlay-element.ts
331
331
  function installPickerOverlay(arg) {
332
332
  const g = globalThis;
333
333
  const doc = g.document;
@@ -604,7 +604,7 @@ function installPickerOverlay(arg) {
604
604
  }
605
605
  }
606
606
 
607
- // ../packages/picker-dom/src/overlay-anchors.ts
607
+ // ../picker-dom/src/overlay-anchors.ts
608
608
  function showAnchorPicker(arg) {
609
609
  const g = globalThis;
610
610
  const doc = g.document;
@@ -867,7 +867,7 @@ function showAnchorPicker(arg) {
867
867
  doc.body.appendChild(panel);
868
868
  }
869
869
 
870
- // ../packages/picker-dom/src/overlay-confirm.ts
870
+ // ../picker-dom/src/overlay-confirm.ts
871
871
  function showPickerChoices(arg) {
872
872
  const g = globalThis;
873
873
  const doc = g.document;
@@ -950,7 +950,7 @@ function showPickerChoices(arg) {
950
950
  doc.body.appendChild(wrap);
951
951
  }
952
952
 
953
- // ../packages/core/src/locator-generation.ts
953
+ // ../core/src/locator-generation.ts
954
954
  var TAG_TO_ROLE = {
955
955
  a: "link",
956
956
  button: "button",
@@ -1301,7 +1301,7 @@ function approximateAccessibleName(attrs) {
1301
1301
  return null;
1302
1302
  }
1303
1303
 
1304
- // ../packages/picker-dom/src/anchor-alternatives.ts
1304
+ // ../picker-dom/src/anchor-alternatives.ts
1305
1305
  var escStr = (s) => s.replaceAll("\\", "\\\\").replaceAll("'", "\\'");
1306
1306
  var cssIdSelector = (id) => /^[A-Za-z_][A-Za-z0-9_-]*$/.test(id) ? `#${id}` : `[id="${id.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"]`;
1307
1307
  var ANCHOR_KIND_SCORES = { testid: 80, id: 74, labeledRole: 68, role: 62 };
@@ -1380,7 +1380,7 @@ function mergeCandidates(base, extra) {
1380
1380
  // src/internal/capture/locator-healing.ts
1381
1381
  var path = __toESM(require("path"));
1382
1382
 
1383
- // ../packages/core/src/locator-fingerprint.ts
1383
+ // ../core/src/locator-fingerprint.ts
1384
1384
  var PRESENT_SIMILARITY = 0.8;
1385
1385
  var MATCH_SIMILARITY = 0.2;
1386
1386
  function parseAriaCandidates(ariaSnapshot) {
@@ -1446,7 +1446,7 @@ function matchRenamedElement(fp, candidates) {
1446
1446
  return null;
1447
1447
  }
1448
1448
 
1449
- // ../packages/core/src/locator-methods.ts
1449
+ // ../core/src/locator-methods.ts
1450
1450
  var LOCATOR_BUILDER_METHODS = [
1451
1451
  "getByRole",
1452
1452
  "getByTestId",
@@ -54,7 +54,7 @@ __export(locator_healing_exports, {
54
54
  module.exports = __toCommonJS(locator_healing_exports);
55
55
  var path = __toESM(require("path"));
56
56
 
57
- // ../packages/core/src/locator-fingerprint.ts
57
+ // ../core/src/locator-fingerprint.ts
58
58
  var PRESENT_SIMILARITY = 0.8;
59
59
  var MATCH_SIMILARITY = 0.2;
60
60
  function parseAriaCandidates(ariaSnapshot) {
@@ -120,7 +120,7 @@ function matchRenamedElement(fp, candidates) {
120
120
  return null;
121
121
  }
122
122
 
123
- // ../packages/core/src/locator-methods.ts
123
+ // ../core/src/locator-methods.ts
124
124
  var LOCATOR_BUILDER_METHODS = [
125
125
  "getByRole",
126
126
  "getByTestId",
@@ -132,7 +132,7 @@ var LOCATOR_BUILDER_METHODS = [
132
132
  "locator"
133
133
  ];
134
134
 
135
- // ../packages/core/src/locator-generation.ts
135
+ // ../core/src/locator-generation.ts
136
136
  var TAG_TO_ROLE = {
137
137
  a: "link",
138
138
  button: "button",
@@ -43,7 +43,7 @@ __export(pick_on_failure_exports, {
43
43
  module.exports = __toCommonJS(pick_on_failure_exports);
44
44
  var path2 = __toESM(require("path"));
45
45
 
46
- // ../packages/picker-dom/src/overlay-element.ts
46
+ // ../picker-dom/src/overlay-element.ts
47
47
  function installPickerOverlay(arg) {
48
48
  const g = globalThis;
49
49
  const doc = g.document;
@@ -320,7 +320,7 @@ function installPickerOverlay(arg) {
320
320
  }
321
321
  }
322
322
 
323
- // ../packages/picker-dom/src/overlay-anchors.ts
323
+ // ../picker-dom/src/overlay-anchors.ts
324
324
  function showAnchorPicker(arg) {
325
325
  const g = globalThis;
326
326
  const doc = g.document;
@@ -583,7 +583,7 @@ function showAnchorPicker(arg) {
583
583
  doc.body.appendChild(panel);
584
584
  }
585
585
 
586
- // ../packages/picker-dom/src/overlay-confirm.ts
586
+ // ../picker-dom/src/overlay-confirm.ts
587
587
  function showPickerChoices(arg) {
588
588
  const g = globalThis;
589
589
  const doc = g.document;
@@ -666,7 +666,7 @@ function showPickerChoices(arg) {
666
666
  doc.body.appendChild(wrap);
667
667
  }
668
668
 
669
- // ../packages/core/src/locator-generation.ts
669
+ // ../core/src/locator-generation.ts
670
670
  var TAG_TO_ROLE = {
671
671
  a: "link",
672
672
  button: "button",
@@ -1002,7 +1002,7 @@ function approximateAccessibleName(attrs) {
1002
1002
  return null;
1003
1003
  }
1004
1004
 
1005
- // ../packages/picker-dom/src/anchor-alternatives.ts
1005
+ // ../picker-dom/src/anchor-alternatives.ts
1006
1006
  var escStr = (s) => s.replaceAll("\\", "\\\\").replaceAll("'", "\\'");
1007
1007
  var cssIdSelector = (id) => /^[A-Za-z_][A-Za-z0-9_-]*$/.test(id) ? `#${id}` : `[id="${id.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"]`;
1008
1008
  var ANCHOR_KIND_SCORES = { testid: 80, id: 74, labeledRole: 68, role: 62 };
@@ -1081,7 +1081,7 @@ function mergeCandidates(base, extra) {
1081
1081
  // src/internal/capture/locator-healing.ts
1082
1082
  var path = __toESM(require("path"));
1083
1083
 
1084
- // ../packages/core/src/locator-methods.ts
1084
+ // ../core/src/locator-methods.ts
1085
1085
  var LOCATOR_BUILDER_METHODS = [
1086
1086
  "getByRole",
1087
1087
  "getByTestId",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@piwitests/reporter",
3
- "version": "0.21.0",
3
+ "version": "0.22.0",
4
4
  "description": "Playwright reporter that streams results, traces and HTML reports to a Piwi Dashboard instance",
5
5
  "url": "https://github.com/PiwiTests/platform",
6
6
  "homepage": "https://piwitests.github.io",
@@ -27,7 +27,7 @@ Prefer the **Piwi MCP server** if it is connected to this agent (tools named `li
27
27
 
28
28
  7. **Verify.** Re-run the affected spec(s): `npx playwright test <file>`. Confirm they pass and the new run is green in the dashboard (`get_run_insights` compares against the last green run: regressions cleared, nothing new broken).
29
29
 
30
- 8. **Close the loop (optional).** With reporter/admin access, `set_cluster_status` marks the cluster resolved with a note so it drops off the triage queue.
30
+ 8. **Close the loop (optional).** With packages/reporter/admin access, `set_cluster_status` marks the cluster resolved with a note so it drops off the triage queue.
31
31
 
32
32
  ## Guardrails
33
33