@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 +1 -1
- package/dist/global-setup-module.js +26 -0
- package/dist/index.js +38 -12
- package/dist/internal/capture/capture-fixtures.js +8 -8
- package/dist/internal/capture/locator-healing.js +3 -3
- package/dist/internal/capture/pick-on-failure.js +6 -6
- package/package.json +1 -1
- package/templates/skills/investigate-failure/SKILL.md +1 -1
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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
|
-
// ../
|
|
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.
|
|
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
|
|