@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.
@@ -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 path3 = __toESM(require("path"));
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 fs3 = __toESM(require("fs"));
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 = path3.resolve(__dirname, "./index.js");
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 path3.resolve(require.resolve(r[0])) === piwiReporterPath;
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 path3.resolve(require.resolve(r[0])) === piwiReporterPath;
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
- fs3.writeFileSync(
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;