@piwitests/reporter 0.26.0 → 0.27.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/README.md +20 -286
- package/dist/cli/index.js +95 -22
- package/dist/global-setup-module.js +75 -7
- package/dist/index.d.ts +35 -0
- package/dist/index.js +1473 -153
- package/dist/internal/capture/attachments.d.ts +2 -0
- package/dist/internal/capture/attachments.js +2 -0
- package/dist/internal/capture/capture-fixtures.d.ts +8 -1
- package/dist/internal/capture/capture-fixtures.js +156 -4
- package/dist/internal/capture/locator-healing.js +1 -0
- package/package.json +1 -1
|
@@ -35,7 +35,7 @@ __export(global_setup_module_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(global_setup_module_exports);
|
|
36
36
|
|
|
37
37
|
// src/public/global-setup.ts
|
|
38
|
-
var
|
|
38
|
+
var path4 = __toESM(require("path"));
|
|
39
39
|
|
|
40
40
|
// src/internal/support/errors.ts
|
|
41
41
|
function errorMessage(error) {
|
|
@@ -44,7 +44,7 @@ function errorMessage(error) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// src/public/global-setup.ts
|
|
47
|
-
var
|
|
47
|
+
var fs4 = __toESM(require("fs"));
|
|
48
48
|
|
|
49
49
|
// src/internal/config/desktop.ts
|
|
50
50
|
var fs = __toESM(require("fs"));
|
|
@@ -78,6 +78,8 @@ var DEFAULTS = {
|
|
|
78
78
|
captureLocators: true,
|
|
79
79
|
capturePageState: true,
|
|
80
80
|
captureServerTraces: true,
|
|
81
|
+
sampleAriaOnPass: true,
|
|
82
|
+
defaultCapture: true,
|
|
81
83
|
streaming: true,
|
|
82
84
|
streamingBatchSize: 5,
|
|
83
85
|
streamingBatchDelay: 2e3,
|
|
@@ -107,6 +109,8 @@ var PIWI_ENV_KEYS = {
|
|
|
107
109
|
captureLocators: "PIWI_CAPTURE_LOCATORS",
|
|
108
110
|
capturePageState: "PIWI_CAPTURE_PAGE_STATE",
|
|
109
111
|
captureServerTraces: "PIWI_CAPTURE_SERVER_TRACES",
|
|
112
|
+
sampleAriaOnPass: "PIWI_SAMPLE_ARIA_ON_PASS",
|
|
113
|
+
defaultCapture: "PIWI_DEFAULT_CAPTURE",
|
|
110
114
|
inspectOnFailure: "PIWI_INSPECT_ON_FAIL",
|
|
111
115
|
pickLocatorOnFailure: "PIWI_PICK_LOCATOR_ON_FAIL",
|
|
112
116
|
outputFile: "PIWI_OUTPUT_FILE",
|
|
@@ -143,6 +147,8 @@ var ENV_FALLBACK_SPECS = [
|
|
|
143
147
|
{ option: "captureLocators", env: PIWI_ENV_KEYS.captureLocators, kind: "bool" },
|
|
144
148
|
{ option: "capturePageState", env: PIWI_ENV_KEYS.capturePageState, kind: "bool" },
|
|
145
149
|
{ option: "captureServerTraces", env: PIWI_ENV_KEYS.captureServerTraces, kind: "bool" },
|
|
150
|
+
{ option: "sampleAriaOnPass", env: PIWI_ENV_KEYS.sampleAriaOnPass, kind: "bool" },
|
|
151
|
+
{ option: "defaultCapture", env: PIWI_ENV_KEYS.defaultCapture, kind: "bool" },
|
|
146
152
|
{ option: "inspectOnFailure", env: PIWI_ENV_KEYS.inspectOnFailure, kind: "bool" },
|
|
147
153
|
{ option: "pickLocatorOnFailure", env: PIWI_ENV_KEYS.pickLocatorOnFailure, kind: "bool" },
|
|
148
154
|
{ option: "outputFile", env: PIWI_ENV_KEYS.outputFile, kind: "string" }
|
|
@@ -298,6 +304,29 @@ var HttpClient = class {
|
|
|
298
304
|
this.logger.debug("Logged in successfully");
|
|
299
305
|
return cookie;
|
|
300
306
|
}
|
|
307
|
+
/**
|
|
308
|
+
* Send a JSON GET request, returning the parsed body, or `null` on any non-2xx
|
|
309
|
+
* status or parse failure. Unlike `postJSON` this never throws — its callers
|
|
310
|
+
* treat a missing or unreachable endpoint as "feature unavailable".
|
|
311
|
+
*/
|
|
312
|
+
async getJSON(pathname, auth) {
|
|
313
|
+
let res;
|
|
314
|
+
try {
|
|
315
|
+
res = await this.request("GET", pathname, { auth });
|
|
316
|
+
} catch (error) {
|
|
317
|
+
this.logger.debug(`GET ${pathname} failed: ${error.message}`);
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
if (res.status < 200 || res.status >= 300) {
|
|
321
|
+
this.logger.debug(`GET ${pathname} returned ${res.status}`);
|
|
322
|
+
return null;
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
return JSON.parse(res.text);
|
|
326
|
+
} catch {
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
301
330
|
/** Send a JSON POST request. `auth` can be an API key (prefix `pd_`) or a session cookie string. */
|
|
302
331
|
async postJSON(pathname, payload, auth) {
|
|
303
332
|
const body = JSON.stringify(payload);
|
|
@@ -348,7 +377,7 @@ var HttpClient = class {
|
|
|
348
377
|
{
|
|
349
378
|
hostname: url.hostname,
|
|
350
379
|
port: url.port || (url.protocol === "https:" ? 443 : 80),
|
|
351
|
-
path: url.pathname,
|
|
380
|
+
path: url.pathname + url.search,
|
|
352
381
|
method,
|
|
353
382
|
headers
|
|
354
383
|
},
|
|
@@ -423,6 +452,29 @@ function getSetupFilePath(projectName) {
|
|
|
423
452
|
return path2.join(os3.tmpdir(), `piwi-dashboard-setup-${hashForProject(projectName)}.json`);
|
|
424
453
|
}
|
|
425
454
|
|
|
455
|
+
// src/internal/support/aria-sampling.ts
|
|
456
|
+
var path3 = __toESM(require("path"));
|
|
457
|
+
var os4 = __toESM(require("os"));
|
|
458
|
+
var fs3 = __toESM(require("fs"));
|
|
459
|
+
function ariaSampleIdentity(filePath, title) {
|
|
460
|
+
return `${filePath}\0${title}`;
|
|
461
|
+
}
|
|
462
|
+
function getAriaSampleFilePath(projectName) {
|
|
463
|
+
return path3.join(os4.tmpdir(), `piwi-dashboard-aria-sample-${hashForProject(projectName)}.json`);
|
|
464
|
+
}
|
|
465
|
+
function writeAriaSampleFile(projectName, identities) {
|
|
466
|
+
try {
|
|
467
|
+
fs3.writeFileSync(getAriaSampleFilePath(projectName), JSON.stringify({ projectName, identities }));
|
|
468
|
+
} catch {
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
function clearAriaSampleFile(projectName) {
|
|
472
|
+
try {
|
|
473
|
+
fs3.rmSync(getAriaSampleFilePath(projectName), { force: true });
|
|
474
|
+
} catch {
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
426
478
|
// src/internal/support/run-mode.ts
|
|
427
479
|
var PW_UI_FLAGS = ["--ui", "--ui-host", "--ui-port"];
|
|
428
480
|
function isUiMode(argv = process.argv) {
|
|
@@ -435,14 +487,14 @@ function isUiMode(argv = process.argv) {
|
|
|
435
487
|
// src/public/global-setup.ts
|
|
436
488
|
function createGlobalSetup(options, userSetup) {
|
|
437
489
|
return async function globalSetupFn(config) {
|
|
438
|
-
const piwiReporterPath =
|
|
490
|
+
const piwiReporterPath = path4.resolve(__dirname, "./index.js");
|
|
439
491
|
let inlineReporterOptions = {};
|
|
440
492
|
if (Array.isArray(config?.reporter)) {
|
|
441
493
|
for (const r of config.reporter) {
|
|
442
494
|
if (!Array.isArray(r) || typeof r[0] !== "string") continue;
|
|
443
495
|
const isPiwi = r[0].toLowerCase().includes("piwi") || (() => {
|
|
444
496
|
try {
|
|
445
|
-
return
|
|
497
|
+
return path4.resolve(require.resolve(r[0])) === piwiReporterPath;
|
|
446
498
|
} catch {
|
|
447
499
|
return false;
|
|
448
500
|
}
|
|
@@ -469,7 +521,7 @@ function createGlobalSetup(options, userSetup) {
|
|
|
469
521
|
if (!Array.isArray(r) || typeof r[0] !== "string") return false;
|
|
470
522
|
if (r[0].toLowerCase().includes("piwi")) return true;
|
|
471
523
|
try {
|
|
472
|
-
return
|
|
524
|
+
return path4.resolve(require.resolve(r[0])) === piwiReporterPath;
|
|
473
525
|
} catch {
|
|
474
526
|
return false;
|
|
475
527
|
}
|
|
@@ -501,7 +553,7 @@ function createGlobalSetup(options, userSetup) {
|
|
|
501
553
|
auth
|
|
502
554
|
);
|
|
503
555
|
if (response?.runId && response?.setupToken) {
|
|
504
|
-
|
|
556
|
+
fs4.writeFileSync(
|
|
505
557
|
getSetupFilePath(opts.projectName),
|
|
506
558
|
JSON.stringify({
|
|
507
559
|
runId: response.runId,
|
|
@@ -511,6 +563,22 @@ function createGlobalSetup(options, userSetup) {
|
|
|
511
563
|
);
|
|
512
564
|
logger.debug(`Global setup: initializing run #${response.runId}`);
|
|
513
565
|
}
|
|
566
|
+
if (opts.projectName) clearAriaSampleFile(opts.projectName);
|
|
567
|
+
if (opts.sampleAriaOnPass !== false && opts.projectName) {
|
|
568
|
+
const menu = await httpClient.getJSON("/api/projects/menu", auth);
|
|
569
|
+
const projectId = menu?.items?.find(
|
|
570
|
+
(p) => p.name.toLowerCase() === opts.projectName.toLowerCase()
|
|
571
|
+
)?.id;
|
|
572
|
+
if (projectId != null) {
|
|
573
|
+
const sampling = await httpClient.getJSON(`/api/projects/${projectId}/aria-sampling`, auth);
|
|
574
|
+
const tests = Array.isArray(sampling?.tests) ? sampling.tests : null;
|
|
575
|
+
if (tests) {
|
|
576
|
+
const identities = tests.filter((t) => typeof t.filePath === "string" && typeof t.title === "string").map((t) => ariaSampleIdentity(t.filePath, t.title));
|
|
577
|
+
writeAriaSampleFile(opts.projectName, identities);
|
|
578
|
+
logger.debug(`Green ARIA sampling: ${identities.length} test(s) due a sample.`);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
514
582
|
} catch (error) {
|
|
515
583
|
logger.warn(`Could not register global setup: ${errorMessage(error)}`);
|
|
516
584
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -58,6 +58,33 @@ interface PiwiDashboardOptions {
|
|
|
58
58
|
* `false`. Can also be forced off with `PIWI_CAPTURE_SERVER_TRACES=false`.
|
|
59
59
|
*/
|
|
60
60
|
captureServerTraces?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Sample the ARIA snapshot at the end of a *passing* test, so a later failure
|
|
63
|
+
* can be diffed against the page as it last looked when green. Rate-limited by
|
|
64
|
+
* the server: at run start the reporter asks which tests are due a fresh
|
|
65
|
+
* sample (their newest green snapshot is older than a day, or missing) and
|
|
66
|
+
* captures only those, so steady-state runs pay nothing. Rides the existing
|
|
67
|
+
* capture fixtures — no snapshot is taken without them. Defaults to `true`.
|
|
68
|
+
* Set to `false` (or `PIWI_SAMPLE_ARIA_ON_PASS=false`) to never sample on pass.
|
|
69
|
+
*/
|
|
70
|
+
sampleAriaOnPass?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* When installed via `wrapConfig`, default Playwright's own `screenshot` and
|
|
73
|
+
* `trace` options on the top-level `use` block so a failing test keeps a
|
|
74
|
+
* screenshot (`'only-on-failure'`) and a trace (`'retain-on-failure'`) even
|
|
75
|
+
* without the capture fixtures — the trace alone unlocks the DOM snapshot,
|
|
76
|
+
* full call stack, full network with bodies and the visual diff. On Playwright
|
|
77
|
+
* 1.63 or later the trace default also turns on the per-action aria tree
|
|
78
|
+
* (`snapshots: { dom: true, aria: true }`), which adds the accessibility tree
|
|
79
|
+
* before and after each action at negligible size. The `screen` snapshot kind
|
|
80
|
+
* (a PNG per action, the trace's biggest cost) stays opt-in — set it yourself
|
|
81
|
+
* with `use: { trace: { mode: 'retain-on-failure', snapshots: { dom: true,
|
|
82
|
+
* aria: true, screen: true } } }`. Only fills options the config leaves unset;
|
|
83
|
+
* an explicit value (including `'off'`) and per-project `use` blocks are never
|
|
84
|
+
* touched. Defaults to `true`. Set to `false` (or `PIWI_DEFAULT_CAPTURE=false`)
|
|
85
|
+
* to opt out and let Playwright's own defaults stand.
|
|
86
|
+
*/
|
|
87
|
+
defaultCapture?: boolean;
|
|
61
88
|
/**
|
|
62
89
|
* Open Piwi's own failure-time overlay on the failing page — for inspecting
|
|
63
90
|
* the page and picking a locator for any element (click an element → confirm
|
|
@@ -218,6 +245,13 @@ declare function createGlobalSetup(options?: PiwiDashboardOptions, userSetup?: (
|
|
|
218
245
|
* supported set — `serverUrl`, `projectName`, `verbose`, `apiKey`,
|
|
219
246
|
* `username`, `password`, `environment`, `label`, `runLabel`).
|
|
220
247
|
*
|
|
248
|
+
* The top-level `use` block's `screenshot` and `trace` are defaulted to
|
|
249
|
+
* `'only-on-failure'` / `'retain-on-failure'` when unset so failure evidence is
|
|
250
|
+
* captured without the fixtures; on Playwright 1.63 or later `trace` also turns
|
|
251
|
+
* on the per-action aria tree (`snapshots: { dom: true, aria: true }`). An
|
|
252
|
+
* explicit value (including `'off'`) is kept. Opt out with `defaultCapture:
|
|
253
|
+
* false` (or `PIWI_DEFAULT_CAPTURE=false`).
|
|
254
|
+
*
|
|
221
255
|
* @param config The user's Playwright config.
|
|
222
256
|
* @param piwiOptions Optional Piwi Dashboard options (serverUrl, projectName, …).
|
|
223
257
|
*/
|
|
@@ -267,6 +301,7 @@ declare class PiwiDashboardReporter {
|
|
|
267
301
|
private streamManager;
|
|
268
302
|
private recovery;
|
|
269
303
|
private submitter;
|
|
304
|
+
private readonly failureLinks;
|
|
270
305
|
private readonly logger;
|
|
271
306
|
static wrapConfig: typeof wrapConfig;
|
|
272
307
|
static createGlobalSetup: typeof createGlobalSetup;
|