@piwitests/reporter 0.62.0 → 0.64.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 CHANGED
@@ -67,7 +67,7 @@ Most options can also be set via a `PIWI_*` environment variable (config wins ov
67
67
  ## Requirements
68
68
 
69
69
  - Node.js 20 or higher (the reporter runs inside your test project — the dashboard *server* itself targets Node 22+, or use its Docker image)
70
- - Playwright Test 1.61.1 or higher
70
+ - Playwright Test 1.59 or higher (a few features need a later version, named in their docs)
71
71
  - A running Piwi Dashboard server
72
72
 
73
73
  ## Contributing
package/dist/cli/index.js CHANGED
@@ -36,6 +36,7 @@ var path2 = __toESM(require("path"));
36
36
 
37
37
  // src/internal/capture/locator-healing.ts
38
38
  var path = __toESM(require("path"));
39
+ var import_node_url = require("url");
39
40
 
40
41
  // ../core/src/locator-chain.ts
41
42
  var LOCATING_METHODS = /* @__PURE__ */ new Set([
@@ -1191,9 +1192,9 @@ function realFolder(dir) {
1191
1192
  return path10.join(fs7.realpathSync(current), ...rest);
1192
1193
  }
1193
1194
  function folderInRepository(root, dir) {
1194
- const relative9 = path10.relative(realFolder(root), realFolder(dir));
1195
- if (relative9.startsWith("..") || path10.isAbsolute(relative9)) return void 0;
1196
- return relative9.split(path10.sep).join("/");
1195
+ const relative8 = path10.relative(realFolder(root), realFolder(dir));
1196
+ if (relative8.startsWith("..") || path10.isAbsolute(relative8)) return void 0;
1197
+ return relative8.split(path10.sep).join("/");
1197
1198
  }
1198
1199
  function fileFilter(file) {
1199
1200
  const escape = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -1843,13 +1844,27 @@ function safeLocator(expr) {
1843
1844
  if (!chain || !chainIsSafe(chain)) return null;
1844
1845
  return { chain, text: renderLocatorChain(chain) };
1845
1846
  }
1846
- function chooseLocator(target, options) {
1847
+ var TEXT_CHECKS = /* @__PURE__ */ new Set(["toHaveText", "toContainText"]);
1848
+ function checkedText(step) {
1849
+ const assertion = step?.action === "assert" ? step.assertion : null;
1850
+ if (!assertion || !TEXT_CHECKS.has(assertion.matcher)) return null;
1851
+ const text = assertion.expected?.replace(/\s+/g, " ").trim();
1852
+ return text ? text : null;
1853
+ }
1854
+ function findsByText(chain, text) {
1855
+ const holds = (arg) => arg?.type === "string" && arg.value.replace(/\s+/g, " ").trim().includes(text);
1856
+ const option = (arg, key) => arg?.type === "object" ? arg.entries.find(([name]) => name === key)?.[1] : void 0;
1857
+ return chain.calls.some(
1858
+ (call) => call.method === "getByText" && holds(call.args[0]) || call.method === "getByRole" && holds(option(call.args[1], "name")) || call.args.some((arg) => holds(option(arg, "hasText")))
1859
+ );
1860
+ }
1861
+ function chooseLocator(target, options, check) {
1847
1862
  if (!target) return null;
1848
- const candidates = target.alternatives.flatMap((alt) => {
1863
+ const recorded = target.alternatives.flatMap((alt) => {
1849
1864
  const safe = safeLocator(alt.locator);
1850
1865
  return safe ? [safe] : [];
1851
1866
  });
1852
- if (candidates.length === 0) return null;
1867
+ if (recorded.length === 0) return null;
1853
1868
  const levels = /* @__PURE__ */ new Map();
1854
1869
  const levelOf = (c) => {
1855
1870
  let level = levels.get(c.text);
@@ -1859,6 +1874,9 @@ function chooseLocator(target, options) {
1859
1874
  }
1860
1875
  return level;
1861
1876
  };
1877
+ const text = checkedText(check);
1878
+ const apart = text ? recorded.filter((c) => !findsByText(c.chain, text) && levelOf(c) !== "brittle") : [];
1879
+ const candidates = apart.length ? apart : recorded;
1862
1880
  const prefer = options.preferLocators;
1863
1881
  if (prefer && prefer.size > 0) {
1864
1882
  const known = candidates.find((c) => prefer.has(c.text) && levelOf(c) !== "brittle");
@@ -1939,7 +1957,7 @@ function urlForCode(url, ctx) {
1939
1957
  return isPath && ctx.origin ? `${ctx.origin}${url}` : url;
1940
1958
  }
1941
1959
  function locatorForStep(step, index, ctx) {
1942
- const chosen = chooseLocator(step.target, ctx.options);
1960
+ const chosen = chooseLocator(step.target, ctx.options, step);
1943
1961
  if (!chosen) {
1944
1962
  ctx.warnings.push({ step: index, code: "no-locator", message: "No locator was captured for this element." });
1945
1963
  return `${ctx.page}.locator(${quote("/* no locator captured */")})`;
@@ -2406,19 +2424,11 @@ function renderSpec(session, options = {}) {
2406
2424
  const needs = needsLines("");
2407
2425
  return result([...needs, ...testOpening, ...testBody, `});`, ``], needs.length + testOpening.length);
2408
2426
  }
2409
- const lines = [
2410
- // Package name interpolated rather than adjoining the preceding keyword
2411
- // directly, so this generated-code line does not itself look like a real
2412
- // import to the boundary test in tests/boundary.test.ts.
2413
- `import { test, expect } from ${quote(options.testImport?.trim() || "@playwright/test")};`,
2414
- ...importLines,
2415
- ``,
2416
- ...testOpening,
2417
- ...testBody,
2418
- `});`,
2419
- ``
2420
- ];
2421
- return result(lines, 1 + importLines.length + 1 + testOpening.length);
2427
+ const testModule = options.testImport?.trim() || "@playwright/test";
2428
+ const expectModule = options.expectImport?.trim() || testModule;
2429
+ const testImports = expectModule === testModule ? [`import { test, expect } from ${quote(testModule)};`] : [`import { test } from ${quote(testModule)};`, `import { expect } from ${quote(expectModule)};`];
2430
+ const lines = [...testImports, ...importLines, ``, ...testOpening, ...testBody, `});`, ``];
2431
+ return result(lines, testImports.length + importLines.length + 1 + testOpening.length);
2422
2432
  }
2423
2433
 
2424
2434
  // ../core/src/steps.ts
@@ -3177,14 +3187,23 @@ var DEFAULT_COVERAGE_EXCLUDES = [
3177
3187
  "tests/",
3178
3188
  "test/",
3179
3189
  "__tests__/",
3190
+ "playwright.config.*",
3180
3191
  "*Tests/",
3181
3192
  "*.Tests/",
3182
3193
  "obj/",
3183
3194
  "bin/",
3184
3195
  "node_modules/",
3196
+ "dist/",
3197
+ ".next/",
3198
+ ".nuxt/",
3199
+ ".output/",
3200
+ ".svelte-kit/",
3185
3201
  "*.g.cs",
3186
3202
  "*.Designer.cs",
3187
- "*.generated.*"
3203
+ "*.generated.*",
3204
+ "*.gen.*",
3205
+ "__generated__/",
3206
+ "*.d.ts"
3188
3207
  ];
3189
3208
  var DEFAULT_COVERAGE_SCOPE = { include: [], exclude: [...DEFAULT_COVERAGE_EXCLUDES] };
3190
3209
  var MAX_SCOPE_PATTERNS = 200;
@@ -3825,8 +3844,8 @@ function relativeTo(path25, root) {
3825
3844
  const head = path25.slice(0, base.length);
3826
3845
  const inside = drive ? head.toLowerCase() === base.toLowerCase() : head === base;
3827
3846
  if (!inside) return null;
3828
- const relative9 = path25.slice(base.length);
3829
- return relative9 || null;
3847
+ const relative8 = path25.slice(base.length);
3848
+ return relative8 || null;
3830
3849
  }
3831
3850
  function reportPathCandidates(raw, { repoRoot, bases }) {
3832
3851
  const slash = (value) => value.replace(/\\/g, "/");
@@ -3837,8 +3856,8 @@ function reportPathCandidates(raw, { repoRoot, bases }) {
3837
3856
  const add = (absolute) => {
3838
3857
  const normalized = normalizeSlashPath(absolute);
3839
3858
  if (!normalized) return;
3840
- const relative9 = normalized.startsWith("/_/") ? normalized.slice(3) : relativeTo(normalized, root);
3841
- if (relative9 && !out.includes(relative9)) out.push(relative9);
3859
+ const relative8 = normalized.startsWith("/_/") ? normalized.slice(3) : relativeTo(normalized, root);
3860
+ if (relative8 && !out.includes(relative8)) out.push(relative8);
3842
3861
  };
3843
3862
  if (isAbsolutePath(path25)) {
3844
3863
  add(path25);
@@ -6106,9 +6125,9 @@ function pathGlobToRegExp(glob) {
6106
6125
  return new RegExp(`^(?:${parts.join("|")})$`);
6107
6126
  }
6108
6127
  function isTranslationFile(path25, globs = DEFAULT_TRANSLATION_GLOBS) {
6109
- const posix = path25.replace(/\\/g, "/");
6110
- if (!TRANSLATION_EXTENSIONS.test(posix)) return false;
6111
- return globs.some((g) => pathGlobToRegExp(g).test(posix));
6128
+ const posix2 = path25.replace(/\\/g, "/");
6129
+ if (!TRANSLATION_EXTENSIONS.test(posix2)) return false;
6130
+ return globs.some((g) => pathGlobToRegExp(g).test(posix2));
6112
6131
  }
6113
6132
  function keepable(value) {
6114
6133
  const v = value.trim();
@@ -6824,15 +6843,22 @@ function diffImpactInput(files) {
6824
6843
  var fs17 = __toESM(require("fs"));
6825
6844
  var path21 = __toESM(require("path"));
6826
6845
  var import_node_module2 = require("module");
6846
+
6847
+ // src/internal/support/playwright-version.ts
6848
+ function playwrightVersionAtLeast(version, minimum) {
6849
+ const have = version ? /^(\d+)\.(\d+)/.exec(version) : null;
6850
+ const want = /^(\d+)\.(\d+)/.exec(minimum);
6851
+ if (!have || !want) return false;
6852
+ const major = Number(have[1]);
6853
+ const wantMajor = Number(want[1]);
6854
+ return major > wantMajor || major === wantMajor && Number(have[2]) >= Number(want[2]);
6855
+ }
6856
+
6857
+ // src/cli/add-reporter.ts
6827
6858
  var REPORTER_PACKAGE2 = "@piwitests/reporter";
6828
6859
  var ADD_REPORTER_FLAG = "--add-reporter";
6829
- var ADD_REPORTER_MIN = { major: 1, minor: 63 };
6830
6860
  function playwrightSupportsAddReporter(version) {
6831
- const match = version ? /^(\d+)\.(\d+)/.exec(version) : null;
6832
- if (!match) return false;
6833
- const major = Number(match[1]);
6834
- const minor = Number(match[2]);
6835
- return major > ADD_REPORTER_MIN.major || major === ADD_REPORTER_MIN.major && minor >= ADD_REPORTER_MIN.minor;
6861
+ return playwrightVersionAtLeast(version, "1.63");
6836
6862
  }
6837
6863
  function configHasPiwiReporter(source) {
6838
6864
  return new RegExp(REPORTER_PACKAGE2.replace(/[/\\]/g, "\\$&")).test(source);
@@ -7789,10 +7815,19 @@ function readOption9(argv, name) {
7789
7815
  function escapeRegex2(s) {
7790
7816
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
7791
7817
  }
7818
+ function probePlaywrightArgs(playwrightArgs2, testTitles) {
7819
+ const given = (...flags) => playwrightArgs2.some((arg) => flags.some((flag) => arg === flag || arg.startsWith(`${flag}=`)));
7820
+ const grep = testTitles.filter((t) => !!t).map(escapeRegex2).join("|");
7821
+ const args = [...playwrightArgs2];
7822
+ if (grep && !given("--grep", "-g")) args.push("--grep", grep);
7823
+ if (!given("--retries")) args.push("--retries=0");
7824
+ if (!given("--max-failures", "-x")) args.push("--max-failures=0");
7825
+ return args;
7826
+ }
7792
7827
  async function runProbe(argv) {
7793
- const sep7 = argv.indexOf("--");
7794
- const own = sep7 < 0 ? argv : argv.slice(0, sep7);
7795
- const playwrightArgs2 = sep7 < 0 ? [] : argv.slice(sep7 + 1);
7828
+ const sep6 = argv.indexOf("--");
7829
+ const own = sep6 < 0 ? argv : argv.slice(0, sep6);
7830
+ const playwrightArgs2 = sep6 < 0 ? [] : argv.slice(sep6 + 1);
7796
7831
  if (own.includes("-h") || own.includes("--help")) {
7797
7832
  console.log(USAGE11);
7798
7833
  return EXIT_OK8;
@@ -7843,9 +7878,10 @@ async function runProbe(argv) {
7843
7878
  const runOutputFile = path24.join(dir, "run.json");
7844
7879
  fs20.writeFileSync(planFile, JSON.stringify(plan));
7845
7880
  fs20.writeFileSync(resultsFile, "");
7846
- const grep = plan.items.map((i) => i.testTitle).filter(Boolean).map(escapeRegex2).join("|");
7847
- const pwArgs = [...playwrightArgs2];
7848
- if (grep && !playwrightArgs2.includes("--grep") && !playwrightArgs2.includes("-g")) pwArgs.push("--grep", grep);
7881
+ const pwArgs = probePlaywrightArgs(
7882
+ playwrightArgs2,
7883
+ plan.items.map((i) => i.testTitle)
7884
+ );
7849
7885
  const childEnv = {
7850
7886
  ...env,
7851
7887
  [PIWI_PROBE_ENV.flag]: "1",
@@ -43,9 +43,6 @@ function errorMessage(error) {
43
43
  return String(error);
44
44
  }
45
45
 
46
- // src/public/global-setup.ts
47
- var fs7 = __toESM(require("fs"));
48
-
49
46
  // src/internal/config/env.ts
50
47
  var path2 = __toESM(require("path"));
51
48
 
@@ -228,7 +225,7 @@ var PIWI_DESKTOP_CONFIG_ENV = "PIWI_DESKTOP_CONFIG";
228
225
  var PIWI_SHARD_ENV = "PIWI_SHARD";
229
226
  function readBool(val) {
230
227
  if (val === void 0) return void 0;
231
- return val === "true";
228
+ return ["true", "1", "yes", "on"].includes(val.trim().toLowerCase());
232
229
  }
233
230
  var ENV_FALLBACK_SPECS = [
234
231
  { option: "serverUrl", env: PIWI_ENV_KEYS.serverUrl, kind: "string" },
@@ -338,7 +335,7 @@ function resolveOptions(raw, configDir) {
338
335
  externalId.onDuplicate = externalIdOnDuplicate;
339
336
  if (Object.keys(externalId).length > 0) mergedRaw.externalId = externalId;
340
337
  const opts = { ...DEFAULTS, ...mergedRaw };
341
- if (env[PIWI_ENV_KEYS.verbose] !== void 0) opts.verbose = env[PIWI_ENV_KEYS.verbose] === "true";
338
+ if (env[PIWI_ENV_KEYS.verbose] !== void 0) opts.verbose = readBool(env[PIWI_ENV_KEYS.verbose]);
342
339
  return opts;
343
340
  }
344
341
 
@@ -693,6 +690,75 @@ function normalizeRef(ref) {
693
690
  return ref.replace(/^refs\/heads\//, "");
694
691
  }
695
692
 
693
+ // ../core/src/locator-chain.ts
694
+ var LOCATING_METHODS = /* @__PURE__ */ new Set([
695
+ "getByRole",
696
+ "getByTestId",
697
+ "getByText",
698
+ "getByLabel",
699
+ "getByPlaceholder",
700
+ "getByAltText",
701
+ "getByTitle",
702
+ "locator",
703
+ "frameLocator"
704
+ ]);
705
+ var NARROWING_METHODS = /* @__PURE__ */ new Set([
706
+ "filter",
707
+ "first",
708
+ "last",
709
+ "nth",
710
+ "and",
711
+ "or",
712
+ "visible",
713
+ "contentFrame",
714
+ "owner"
715
+ ]);
716
+ var LOCATOR_CHAIN_METHODS = /* @__PURE__ */ new Set([...LOCATING_METHODS, ...NARROWING_METHODS]);
717
+
718
+ // ../core/src/mask.ts
719
+ var SECRET_AUTOCOMPLETE = [
720
+ "current-password",
721
+ "new-password",
722
+ "one-time-code",
723
+ "cc-number",
724
+ "cc-csc"
725
+ ];
726
+ var SECRET_FIELD_SELECTOR = [
727
+ 'input[type="password" i]',
728
+ ...SECRET_AUTOCOMPLETE.flatMap((token) => [
729
+ `input[autocomplete~="${token}" i]`,
730
+ `textarea[autocomplete~="${token}" i]`
731
+ ])
732
+ ].join(", ");
733
+ var SECRET_SHAPES = [
734
+ /\beyJ[\w-]{5,}\.[\w-]{5,}\.[\w-]*/g,
735
+ /\bBearer(?:\s+|%20)[\w.~+/-]{8,}=*/gi,
736
+ /\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{8,}/g,
737
+ /\bwhsec_[A-Za-z0-9]{20,}/g,
738
+ /\bsk-(?=[\w-]{0,200}\d)(?=[\w-]{0,200}[A-Z])[\w-]{32,}/g,
739
+ /\bglpat-[\w-]{20,}/g,
740
+ /\bnpm_[A-Za-z0-9]{36}\b/g,
741
+ /\bSK[0-9a-f]{32}\b/g,
742
+ /\bAKIA[0-9A-Z]{16}\b/g,
743
+ /\bAIza[\w-]{30,}/g,
744
+ /\bgh[opsu]_[A-Za-z0-9]{20,}/g,
745
+ /\bgithub_pat_\w{20,}/g,
746
+ /\bxox[abprs]-[\w-]{8,}/g
747
+ ];
748
+ var WHOLE_SHAPES = SECRET_SHAPES.map(
749
+ (re) => new RegExp(`^(?:${re.source})$`, re.flags.replace("g", ""))
750
+ );
751
+
752
+ // ../core/src/live-view.ts
753
+ var LIVE_FRAME_BYTE_RATE = 256 * 1024;
754
+ var MAX_LIVE_FRAME = 192 * 1024;
755
+ var MAX_LIVE_FRAME_BASE64 = 4 * Math.ceil(MAX_LIVE_FRAME / 3);
756
+ var MAX_LIVE_MESSAGE = MAX_LIVE_FRAME_BASE64 + 8 * 1024;
757
+ var KEY = /^pdl_[A-Za-z0-9_-]{43}$/;
758
+ function isLiveViewKey(value) {
759
+ return typeof value === "string" && KEY.test(value);
760
+ }
761
+
696
762
  // src/internal/support/setup-file.ts
697
763
  var path4 = __toESM(require("path"));
698
764
  var os3 = __toESM(require("os"));
@@ -700,6 +766,10 @@ var fs3 = __toESM(require("fs"));
700
766
  function getSetupFilePath(projectName) {
701
767
  return path4.join(os3.tmpdir(), `piwi-dashboard-setup-${hashForProject(projectName)}.json`);
702
768
  }
769
+ function writeSetupInfo(info) {
770
+ const setupFile = getSetupFilePath(info.projectName);
771
+ fs3.writeFileSync(setupFile, JSON.stringify(info), { mode: 384 });
772
+ }
703
773
 
704
774
  // src/internal/support/aria-sampling.ts
705
775
  var path5 = __toESM(require("path"));
@@ -1101,19 +1171,23 @@ function createGlobalSetup(options, userSetup) {
1101
1171
  startTime: (/* @__PURE__ */ new Date()).toISOString(),
1102
1172
  instanceId: computeInstanceId(opts.projectName, resolveRunLabel(opts.runLabel, shard !== null)),
1103
1173
  shardIndex: shard?.current,
1104
- shardTotal: shard?.total
1174
+ shardTotal: shard?.total,
1175
+ // A key for the run's live view: the instance hands one out only for a project that turned it on, and the
1176
+ // desktop app none. A CI run's workers stream with it at once; a run outside CI sends nothing with it until a
1177
+ // person shares the run.
1178
+ liveView: true
1105
1179
  },
1106
1180
  auth
1107
1181
  );
1108
1182
  if (response?.runId && response?.setupToken) {
1109
- fs7.writeFileSync(
1110
- getSetupFilePath(opts.projectName),
1111
- JSON.stringify({
1112
- runId: response.runId,
1113
- setupToken: response.setupToken,
1114
- projectName: opts.projectName
1115
- })
1116
- );
1183
+ const liveViewKey = response.liveView?.key;
1184
+ writeSetupInfo({
1185
+ runId: response.runId,
1186
+ setupToken: response.setupToken,
1187
+ projectName: opts.projectName,
1188
+ ...isLiveViewKey(liveViewKey) ? { liveViewKey } : {},
1189
+ ...response.liveView?.refused === "local" ? { liveViewLocal: true } : {}
1190
+ });
1117
1191
  logger.debug(`Global setup: initializing run #${response.runId}`);
1118
1192
  }
1119
1193
  if (opts.projectName) clearAriaSampleFile(opts.projectName);
package/dist/index.d.ts CHANGED
@@ -2,11 +2,8 @@ import { FullConfig, Suite, TestCase, TestResult, FullResult } from '@playwright
2
2
  import { PlaywrightTestConfig, Locator, TestType, Page, Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions } from '@playwright/test';
3
3
  export { PlaywrightTestConfig } from '@playwright/test';
4
4
  export { PiwiFixtures, PiwiWorkerFixtures, extendPiwiFixtures, piwiFixtures } from './internal/capture/capture-fixtures.js';
5
- import '@piwitests/picker-dom';
6
- import '@piwitests/core/page-interaction';
7
- import '@piwitests/core/wire';
8
- import '@piwitests/core/run-channel';
9
- import '@piwitests/core/code-reach';
5
+ import './types-CGr9jJZ_.js';
6
+ import './wire-7zsqlBoY.js';
10
7
 
11
8
  /**
12
9
  * Options for configuring the Piwi Dashboard reporter.
@@ -59,8 +56,9 @@ interface PiwiDashboardOptions {
59
56
  collectPerformanceMetrics?: boolean;
60
57
  /**
61
58
  * Capture per-action locator snapshots that power failure-time healing
62
- * suggestions. Adds a small per-action cost (one DOM read, sometimes an ARIA
63
- * snapshot) in the test worker. Defaults to `true`; automatically disabled
59
+ * suggestions. Adds a small per-action cost (one DOM read, rarely two,
60
+ * sometimes an ARIA snapshot) and one DOM read per new document in the test
61
+ * worker. Defaults to `true`; automatically disabled
64
62
  * when `collectPerformanceMetrics` is `false` (the reporter discards the data
65
63
  * in that case anyway). Can also be forced off with `PIWI_CAPTURE_LOCATORS=false`.
66
64
  */
@@ -324,7 +322,7 @@ interface PiwiDashboardOptions {
324
322
  */
325
323
  maxStreamBufferBytes?: number;
326
324
  /**
327
- * Fail the run when any test was flaky (passed only after a retry). Forwarded
325
+ * Fail the run when any test was flaky (one attempt failed and another passed). Forwarded
328
326
  * to Playwright's native `failOnFlakyTests` config option (Playwright 1.52+)
329
327
  * when the reporter is installed via `wrapConfig`, so a flaky-only run exits
330
328
  * non-zero without any server round-trip. Defaults to `false`. Can also be
@@ -505,6 +503,10 @@ declare class PiwiDashboardReporter {
505
503
  private plannedTests;
506
504
  /** Ids of tests that actually reported via `onTestEnd`, to find the ones that never ran. */
507
505
  private reportedTestIds;
506
+ /** The spec files said to have run without the capture fixtures in a run that can stop (`noteUncaptured`). */
507
+ private uncapturedSpecs;
508
+ /** The live steps begun and not ended, by step, sent again when someone opens the run (`onWatched`). */
509
+ private openSteps;
508
510
  /** Per-test attempt history, keyed by `test.id`; snapshotted onto every attempt's payload. */
509
511
  private attemptsByTest;
510
512
  /** The folder test files are named relative to: the Playwright config's, set in `onBegin`. */
@@ -579,6 +581,14 @@ declare class PiwiDashboardReporter {
579
581
  printsToStdio(): boolean;
580
582
  /** Playwright reporter hook: called once at the start of the test run */
581
583
  onBegin(config: FullConfig, suite: Suite): void;
584
+ /**
585
+ * Name the run's live view on the instance for the workers, which Playwright starts after this hook with this
586
+ * environment: the key the instance gave the global setup, for a project that turned its live view on, for this run
587
+ * alone. A CI run's workers stream with it at once; a run outside CI's hold it until a person shares the run (Share
588
+ * live), and such a run without a key names why instead, for the surfaces that would share it; a CI run without one
589
+ * says why in its log (`ciLiveViewNote`). Read before the stream starts, which takes the setup file.
590
+ */
591
+ private shareLiveView;
582
592
  /**
583
593
  * Settle each planned test's external ID before any test runs, name the IDs two tests share, and record them on
584
594
  * the run so the dashboard lists them too.
@@ -599,6 +609,15 @@ declare class PiwiDashboardReporter {
599
609
  * human watches; the meaningful readout is the assertion around them.
600
610
  */
601
611
  private static isLiveStep;
612
+ /**
613
+ * In a run that can stop (breakpoints, or a run channel to an editor or the desktop app), say of the first spec file
614
+ * whose test made Playwright calls without the capture fixtures, where the stops happen, that it never stops at a
615
+ * breakpoint nor at Pause, which nothing else says; the others are named in one line as the run ends
616
+ * (`noteOtherUncaptured`).
617
+ */
618
+ private noteUncaptured;
619
+ /** The spec files past the first that ran without the capture fixtures, in one line (`noteUncaptured`). */
620
+ private noteOtherUncaptured;
602
621
  /** Playwright reporter hook: called when a step (including hook/fixture) begins */
603
622
  onStepBegin(test: TestCase | undefined, _result: TestResult | undefined, step: any): void;
604
623
  /** Playwright reporter hook: called when a step (including hook/fixture) ends */