@piwitests/reporter 0.36.0 → 0.39.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 +420 -8
- package/dist/global-setup-module.js +168 -15
- package/dist/index.d.ts +33 -4
- package/dist/index.js +1092 -153
- package/dist/internal/capture/attachments.d.ts +1 -0
- package/dist/internal/capture/attachments.js +1 -0
- package/dist/internal/capture/capture-fixtures.js +911 -85
- package/dist/internal/capture/pick-on-failure.d.ts +5 -1
- package/dist/internal/capture/pick-on-failure.js +284 -79
- package/package.json +2 -2
- package/templates/skills/write-the-missing-test/SKILL.md +69 -0
|
@@ -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 path5 = __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 fs5 = __toESM(require("fs"));
|
|
48
48
|
|
|
49
49
|
// src/internal/config/desktop.ts
|
|
50
50
|
var fs = __toESM(require("fs"));
|
|
@@ -77,8 +77,12 @@ var DEFAULTS = {
|
|
|
77
77
|
collectPerformanceMetrics: true,
|
|
78
78
|
captureLocators: true,
|
|
79
79
|
capturePageState: true,
|
|
80
|
+
// Off by default: the page inventory reads control and link names from every
|
|
81
|
+
// visited page, so it stays opt-in until a project turns it on.
|
|
82
|
+
capturePageInventory: false,
|
|
80
83
|
captureServerTraces: true,
|
|
81
84
|
sampleAriaOnPass: true,
|
|
85
|
+
uploadManifest: true,
|
|
82
86
|
defaultCapture: true,
|
|
83
87
|
streaming: true,
|
|
84
88
|
streamingBatchSize: 5,
|
|
@@ -100,6 +104,7 @@ var PIWI_ENV_KEYS = {
|
|
|
100
104
|
environment: "PIWI_ENVIRONMENT",
|
|
101
105
|
label: "PIWI_LABEL",
|
|
102
106
|
runLabel: "PIWI_RUN_LABEL",
|
|
107
|
+
keep: "PIWI_KEEP",
|
|
103
108
|
streaming: "PIWI_STREAMING",
|
|
104
109
|
streamingBatchSize: "PIWI_STREAMING_BATCH_SIZE",
|
|
105
110
|
streamingBatchDelay: "PIWI_STREAMING_BATCH_DELAY",
|
|
@@ -110,8 +115,10 @@ var PIWI_ENV_KEYS = {
|
|
|
110
115
|
uploadReport: "PIWI_UPLOAD_REPORT",
|
|
111
116
|
captureLocators: "PIWI_CAPTURE_LOCATORS",
|
|
112
117
|
capturePageState: "PIWI_CAPTURE_PAGE_STATE",
|
|
118
|
+
capturePageInventory: "PIWI_CAPTURE_PAGE_INVENTORY",
|
|
113
119
|
captureServerTraces: "PIWI_CAPTURE_SERVER_TRACES",
|
|
114
120
|
sampleAriaOnPass: "PIWI_SAMPLE_ARIA_ON_PASS",
|
|
121
|
+
uploadManifest: "PIWI_UPLOAD_MANIFEST",
|
|
115
122
|
defaultCapture: "PIWI_DEFAULT_CAPTURE",
|
|
116
123
|
inspectOnFailure: "PIWI_INSPECT_ON_FAIL",
|
|
117
124
|
pickLocatorOnFailure: "PIWI_PICK_LOCATOR_ON_FAIL",
|
|
@@ -139,6 +146,7 @@ var ENV_FALLBACK_SPECS = [
|
|
|
139
146
|
{ option: "environment", env: PIWI_ENV_KEYS.environment, kind: "string" },
|
|
140
147
|
{ option: "label", env: PIWI_ENV_KEYS.label, kind: "string" },
|
|
141
148
|
{ option: "runLabel", env: PIWI_ENV_KEYS.runLabel, kind: "string" },
|
|
149
|
+
{ option: "keep", env: PIWI_ENV_KEYS.keep, kind: "bool" },
|
|
142
150
|
{ option: "streaming", env: PIWI_ENV_KEYS.streaming, kind: "bool" },
|
|
143
151
|
{ option: "streamingBatchSize", env: PIWI_ENV_KEYS.streamingBatchSize, kind: "number" },
|
|
144
152
|
{ option: "streamingBatchDelay", env: PIWI_ENV_KEYS.streamingBatchDelay, kind: "number" },
|
|
@@ -149,8 +157,10 @@ var ENV_FALLBACK_SPECS = [
|
|
|
149
157
|
{ option: "uploadReport", env: PIWI_ENV_KEYS.uploadReport, kind: "bool" },
|
|
150
158
|
{ option: "captureLocators", env: PIWI_ENV_KEYS.captureLocators, kind: "bool" },
|
|
151
159
|
{ option: "capturePageState", env: PIWI_ENV_KEYS.capturePageState, kind: "bool" },
|
|
160
|
+
{ option: "capturePageInventory", env: PIWI_ENV_KEYS.capturePageInventory, kind: "bool" },
|
|
152
161
|
{ option: "captureServerTraces", env: PIWI_ENV_KEYS.captureServerTraces, kind: "bool" },
|
|
153
162
|
{ option: "sampleAriaOnPass", env: PIWI_ENV_KEYS.sampleAriaOnPass, kind: "bool" },
|
|
163
|
+
{ option: "uploadManifest", env: PIWI_ENV_KEYS.uploadManifest, kind: "bool" },
|
|
154
164
|
{ option: "defaultCapture", env: PIWI_ENV_KEYS.defaultCapture, kind: "bool" },
|
|
155
165
|
{ option: "inspectOnFailure", env: PIWI_ENV_KEYS.inspectOnFailure, kind: "bool" },
|
|
156
166
|
{ option: "pickLocatorOnFailure", env: PIWI_ENV_KEYS.pickLocatorOnFailure, kind: "bool" },
|
|
@@ -351,6 +361,27 @@ var HttpClient = class {
|
|
|
351
361
|
return {};
|
|
352
362
|
}
|
|
353
363
|
}
|
|
364
|
+
/** Send a JSON PUT request. Used for the declared-surface manifest upload. */
|
|
365
|
+
async putJSON(pathname, payload, auth) {
|
|
366
|
+
const body = JSON.stringify(payload);
|
|
367
|
+
const res = await this.request("PUT", pathname, {
|
|
368
|
+
headers: {
|
|
369
|
+
"Content-Type": "application/json",
|
|
370
|
+
"Content-Length": Buffer.byteLength(body)
|
|
371
|
+
},
|
|
372
|
+
body,
|
|
373
|
+
auth
|
|
374
|
+
});
|
|
375
|
+
if (res.status < 200 || res.status >= 300) {
|
|
376
|
+
this.logger.debugError(`Response: ${res.text}`);
|
|
377
|
+
throw new HttpError(res.status);
|
|
378
|
+
}
|
|
379
|
+
try {
|
|
380
|
+
return JSON.parse(res.text);
|
|
381
|
+
} catch {
|
|
382
|
+
return {};
|
|
383
|
+
}
|
|
384
|
+
}
|
|
354
385
|
/** Send a multipart form-data POST request. Used for report and trace uploads. */
|
|
355
386
|
async postFormData(pathname, form, auth) {
|
|
356
387
|
const headers = form.getHeaders();
|
|
@@ -453,6 +484,36 @@ function detectCiRunLabel() {
|
|
|
453
484
|
return null;
|
|
454
485
|
}
|
|
455
486
|
|
|
487
|
+
// src/internal/collect/metadata-collector.ts
|
|
488
|
+
var import_node_child_process = require("child_process");
|
|
489
|
+
var DETACHED_HEAD = "HEAD";
|
|
490
|
+
function resolveScmBranch(env, gitBranch) {
|
|
491
|
+
const pick = (...names) => {
|
|
492
|
+
for (const name of names) {
|
|
493
|
+
const value = env[name]?.trim();
|
|
494
|
+
if (value) return value;
|
|
495
|
+
}
|
|
496
|
+
return void 0;
|
|
497
|
+
};
|
|
498
|
+
const override = env.PIWI_BRANCH?.trim();
|
|
499
|
+
if (override) return override;
|
|
500
|
+
let provider;
|
|
501
|
+
if (env.JENKINS_URL) provider = pick("CHANGE_BRANCH", "BRANCH_NAME");
|
|
502
|
+
else if (env.GITHUB_ACTIONS) provider = pick("GITHUB_HEAD_REF", "GITHUB_REF_NAME");
|
|
503
|
+
else if (env.GITLAB_CI) provider = pick("CI_MERGE_REQUEST_SOURCE_BRANCH_NAME", "CI_COMMIT_REF_NAME");
|
|
504
|
+
else if (env.CIRCLECI) provider = pick("CIRCLE_BRANCH");
|
|
505
|
+
else if (env.TRAVIS) provider = pick("TRAVIS_PULL_REQUEST_BRANCH", "TRAVIS_BRANCH");
|
|
506
|
+
else if (env.TF_BUILD) provider = pick("SYSTEM_PULLREQUEST_SOURCEBRANCH", "BUILD_SOURCEBRANCHNAME");
|
|
507
|
+
else if (env.BITBUCKET_BUILD_NUMBER) provider = pick("BITBUCKET_BRANCH");
|
|
508
|
+
if (provider) return normalizeRef(provider);
|
|
509
|
+
const trimmed = gitBranch?.trim();
|
|
510
|
+
if (trimmed && trimmed !== DETACHED_HEAD) return trimmed;
|
|
511
|
+
return void 0;
|
|
512
|
+
}
|
|
513
|
+
function normalizeRef(ref) {
|
|
514
|
+
return ref.replace(/^refs\/heads\//, "");
|
|
515
|
+
}
|
|
516
|
+
|
|
456
517
|
// src/internal/support/setup-file.ts
|
|
457
518
|
var path2 = __toESM(require("path"));
|
|
458
519
|
var os3 = __toESM(require("os"));
|
|
@@ -500,17 +561,83 @@ function isListMode(argv = process.argv) {
|
|
|
500
561
|
return rest.some((tok) => PW_LIST_FLAGS.some((flag) => tok === flag || tok.startsWith(`${flag}=`)));
|
|
501
562
|
}
|
|
502
563
|
|
|
564
|
+
// src/internal/manifest/collect.ts
|
|
565
|
+
var fs4 = __toESM(require("fs"));
|
|
566
|
+
var path4 = __toESM(require("path"));
|
|
567
|
+
var COMMITTED_MANIFEST_FILE = "piwi.manifest.json";
|
|
568
|
+
var INSTRUMENTATION_MANIFEST_PATH = "/__piwi/manifest";
|
|
569
|
+
var INSTRUMENTATION_HEADERS = ["x-piwi-logs", "x-piwi-trace"];
|
|
570
|
+
function parseManifestJson(text) {
|
|
571
|
+
let parsed;
|
|
572
|
+
try {
|
|
573
|
+
parsed = JSON.parse(text);
|
|
574
|
+
} catch {
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
578
|
+
const obj = parsed;
|
|
579
|
+
const routes = Array.isArray(obj.routes) ? obj.routes.filter((r) => !!r && typeof r === "object").filter((r) => typeof r.method === "string" && typeof r.pattern === "string").map((r) => ({
|
|
580
|
+
method: r.method,
|
|
581
|
+
pattern: r.pattern,
|
|
582
|
+
handler: typeof r.handler === "string" ? r.handler : void 0,
|
|
583
|
+
responses: Array.isArray(r.responses) ? r.responses.filter((c) => Number.isInteger(c)) : void 0
|
|
584
|
+
})) : void 0;
|
|
585
|
+
const pages = Array.isArray(obj.pages) ? obj.pages.filter((p) => !!p && typeof p === "object").filter((p) => typeof p.pattern === "string").map((p) => ({ pattern: p.pattern, name: typeof p.name === "string" ? p.name : void 0 })) : void 0;
|
|
586
|
+
if (!routes && !pages) return null;
|
|
587
|
+
return { routes, pages };
|
|
588
|
+
}
|
|
589
|
+
function hasInstrumentationHeader(headers) {
|
|
590
|
+
const get = (name) => typeof headers.get === "function" ? headers.get(name) : headers[name];
|
|
591
|
+
return INSTRUMENTATION_HEADERS.some((h) => get(h) != null && get(h) !== "");
|
|
592
|
+
}
|
|
593
|
+
function readCommittedManifest(configDir) {
|
|
594
|
+
const file = path4.join(configDir, COMMITTED_MANIFEST_FILE);
|
|
595
|
+
try {
|
|
596
|
+
if (!fs4.existsSync(file)) return null;
|
|
597
|
+
return parseManifestJson(fs4.readFileSync(file, "utf8"));
|
|
598
|
+
} catch {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
function configDirFromConfig(config) {
|
|
603
|
+
if (config?.configFile) return path4.dirname(config.configFile);
|
|
604
|
+
if (config?.rootDir) return config.rootDir;
|
|
605
|
+
return process.cwd();
|
|
606
|
+
}
|
|
607
|
+
function baseUrlFromConfig(config) {
|
|
608
|
+
const top = config?.use?.baseURL;
|
|
609
|
+
if (typeof top === "string" && top) return top;
|
|
610
|
+
for (const project of config?.projects ?? []) {
|
|
611
|
+
const url = project?.use?.baseURL;
|
|
612
|
+
if (typeof url === "string" && url) return url;
|
|
613
|
+
}
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
var INSTRUMENTATION_FETCH_TIMEOUT_MS = 5e3;
|
|
617
|
+
async function fetchInstrumentationManifest(baseUrl, fetchImpl = fetch) {
|
|
618
|
+
try {
|
|
619
|
+
const probe = await fetchImpl(baseUrl, { signal: AbortSignal.timeout(INSTRUMENTATION_FETCH_TIMEOUT_MS) });
|
|
620
|
+
if (!hasInstrumentationHeader(probe.headers)) return null;
|
|
621
|
+
const manifestUrl = new URL(INSTRUMENTATION_MANIFEST_PATH, baseUrl).toString();
|
|
622
|
+
const res = await fetchImpl(manifestUrl, { signal: AbortSignal.timeout(INSTRUMENTATION_FETCH_TIMEOUT_MS) });
|
|
623
|
+
if (!res.ok) return null;
|
|
624
|
+
return parseManifestJson(await res.text());
|
|
625
|
+
} catch {
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
503
630
|
// src/public/global-setup.ts
|
|
504
631
|
function createGlobalSetup(options, userSetup) {
|
|
505
632
|
return async function globalSetupFn(config) {
|
|
506
|
-
const piwiReporterPath =
|
|
633
|
+
const piwiReporterPath = path5.resolve(__dirname, "./index.js");
|
|
507
634
|
let inlineReporterOptions = {};
|
|
508
635
|
if (Array.isArray(config?.reporter)) {
|
|
509
636
|
for (const r of config.reporter) {
|
|
510
637
|
if (!Array.isArray(r) || typeof r[0] !== "string") continue;
|
|
511
638
|
const isPiwi = r[0].toLowerCase().includes("piwi") || (() => {
|
|
512
639
|
try {
|
|
513
|
-
return
|
|
640
|
+
return path5.resolve(require.resolve(r[0])) === piwiReporterPath;
|
|
514
641
|
} catch {
|
|
515
642
|
return false;
|
|
516
643
|
}
|
|
@@ -542,7 +669,7 @@ function createGlobalSetup(options, userSetup) {
|
|
|
542
669
|
if (!Array.isArray(r) || typeof r[0] !== "string") return false;
|
|
543
670
|
if (r[0].toLowerCase().includes("piwi")) return true;
|
|
544
671
|
try {
|
|
545
|
-
return
|
|
672
|
+
return path5.resolve(require.resolve(r[0])) === piwiReporterPath;
|
|
546
673
|
} catch {
|
|
547
674
|
return false;
|
|
548
675
|
}
|
|
@@ -566,6 +693,7 @@ function createGlobalSetup(options, userSetup) {
|
|
|
566
693
|
projectDescription: opts.projectDescription,
|
|
567
694
|
environment: opts.environment || null,
|
|
568
695
|
label: opts.label || null,
|
|
696
|
+
keep: opts.keep === true,
|
|
569
697
|
startTime: (/* @__PURE__ */ new Date()).toISOString(),
|
|
570
698
|
instanceId: computeInstanceId(opts.projectName, runLabel),
|
|
571
699
|
shardIndex,
|
|
@@ -574,7 +702,7 @@ function createGlobalSetup(options, userSetup) {
|
|
|
574
702
|
auth
|
|
575
703
|
);
|
|
576
704
|
if (response?.runId && response?.setupToken) {
|
|
577
|
-
|
|
705
|
+
fs5.writeFileSync(
|
|
578
706
|
getSetupFilePath(opts.projectName),
|
|
579
707
|
JSON.stringify({
|
|
580
708
|
runId: response.runId,
|
|
@@ -585,18 +713,43 @@ function createGlobalSetup(options, userSetup) {
|
|
|
585
713
|
logger.debug(`Global setup: initializing run #${response.runId}`);
|
|
586
714
|
}
|
|
587
715
|
if (opts.projectName) clearAriaSampleFile(opts.projectName);
|
|
588
|
-
|
|
716
|
+
const wantsAria = opts.sampleAriaOnPass !== false && !!opts.projectName;
|
|
717
|
+
const committedManifest = opts.uploadManifest !== false ? readCommittedManifest(configDirFromConfig(config)) : null;
|
|
718
|
+
const manifestBaseUrl = opts.uploadManifest !== false ? baseUrlFromConfig(config) : null;
|
|
719
|
+
const wantsManifest = opts.uploadManifest !== false && !!opts.projectName && (!!committedManifest || !!manifestBaseUrl);
|
|
720
|
+
let projectId;
|
|
721
|
+
if ((wantsAria || wantsManifest) && opts.projectName) {
|
|
589
722
|
const menu = await httpClient.getJSON("/api/projects/menu", auth);
|
|
590
|
-
|
|
723
|
+
projectId = menu?.items?.find(
|
|
591
724
|
(p) => p.name.toLowerCase() === opts.projectName.toLowerCase()
|
|
592
725
|
)?.id;
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
726
|
+
}
|
|
727
|
+
if (wantsAria && projectId != null) {
|
|
728
|
+
const sampling = await httpClient.getJSON(`/api/projects/${projectId}/aria-sampling`, auth);
|
|
729
|
+
const tests = Array.isArray(sampling?.tests) ? sampling.tests : null;
|
|
730
|
+
if (tests) {
|
|
731
|
+
const identities = tests.filter((t) => typeof t.filePath === "string" && typeof t.title === "string").map((t) => ariaSampleIdentity(t.filePath, t.title));
|
|
732
|
+
writeAriaSampleFile(opts.projectName, identities);
|
|
733
|
+
logger.debug(`Green ARIA sampling: ${identities.length} test(s) due a sample.`);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
if (wantsManifest && projectId != null) {
|
|
737
|
+
const branch = resolveScmBranch(process.env) ?? null;
|
|
738
|
+
if (committedManifest) {
|
|
739
|
+
await httpClient.putJSON(
|
|
740
|
+
`/api/projects/${projectId}/surface/manifest`,
|
|
741
|
+
{ source: "committed", branch, manifest: committedManifest },
|
|
742
|
+
auth
|
|
743
|
+
).catch((e) => logger.debug(`Manifest upload (committed) skipped: ${errorMessage(e)}`));
|
|
744
|
+
}
|
|
745
|
+
if (manifestBaseUrl) {
|
|
746
|
+
const declared = await fetchInstrumentationManifest(manifestBaseUrl);
|
|
747
|
+
if (declared) {
|
|
748
|
+
await httpClient.putJSON(
|
|
749
|
+
`/api/projects/${projectId}/surface/manifest`,
|
|
750
|
+
{ source: "instrumentation", branch, manifest: declared },
|
|
751
|
+
auth
|
|
752
|
+
).catch((e) => logger.debug(`Manifest upload (instrumentation) skipped: ${errorMessage(e)}`));
|
|
600
753
|
}
|
|
601
754
|
}
|
|
602
755
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -48,6 +48,17 @@ interface PiwiDashboardOptions {
|
|
|
48
48
|
* also be forced off with `PIWI_CAPTURE_PAGE_STATE=false`.
|
|
49
49
|
*/
|
|
50
50
|
capturePageState?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Capture a lightweight inventory of each visited page on *passing* runs — the
|
|
53
|
+
* interactive controls (role + accessible name) and links (name, with the query
|
|
54
|
+
* and hash stripped from the href) present as the test navigates — so the
|
|
55
|
+
* dashboard can tell which controls and links the suite exposes but never
|
|
56
|
+
* exercises. Only names and hrefs are captured, never field values; a page's
|
|
57
|
+
* controls are read at most once per worker per run. **Defaults to `false`** —
|
|
58
|
+
* opt in with `capturePageInventory: true` (or `PIWI_CAPTURE_PAGE_INVENTORY=true`).
|
|
59
|
+
* Automatically disabled when `collectPerformanceMetrics` is `false`.
|
|
60
|
+
*/
|
|
61
|
+
capturePageInventory?: boolean;
|
|
51
62
|
/**
|
|
52
63
|
* Capture server-side spans for each API/document request the test makes,
|
|
53
64
|
* read from the `X-Piwi-Trace` response header emitted by a Piwi
|
|
@@ -68,6 +79,15 @@ interface PiwiDashboardOptions {
|
|
|
68
79
|
* Set to `false` (or `PIWI_SAMPLE_ARIA_ON_PASS=false`) to never sample on pass.
|
|
69
80
|
*/
|
|
70
81
|
sampleAriaOnPass?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Upload the application's declared surface at run start: a committed
|
|
84
|
+
* `piwi.manifest.json` next to the Playwright config, and — when the base URL's
|
|
85
|
+
* first response carries an instrumentation header — the instrumentation
|
|
86
|
+
* package's `/__piwi/manifest`. A route or page the manifest declares that no
|
|
87
|
+
* test reaches becomes a "declared, never hit" gap. Defaults to `true`; set to
|
|
88
|
+
* `false` (or `PIWI_UPLOAD_MANIFEST=false`) to never upload.
|
|
89
|
+
*/
|
|
90
|
+
uploadManifest?: boolean;
|
|
71
91
|
/**
|
|
72
92
|
* When installed via `wrapConfig`, default Playwright's own `screenshot` and
|
|
73
93
|
* `trace` options on the top-level `use` block so a failing test keeps a
|
|
@@ -153,6 +173,12 @@ interface PiwiDashboardOptions {
|
|
|
153
173
|
environment?: string;
|
|
154
174
|
/** Optional display label for the test run (e.g. "v2.3.1 release") */
|
|
155
175
|
label?: string;
|
|
176
|
+
/**
|
|
177
|
+
* Keep this run forever: the dashboard's retention never deletes it. Set it
|
|
178
|
+
* for the runs worth keeping, such as release or tag builds. Can also be set
|
|
179
|
+
* with `PIWI_KEEP=true`.
|
|
180
|
+
*/
|
|
181
|
+
keep?: boolean;
|
|
156
182
|
/** Related issue reference, e.g. `"JIRA-123"` */
|
|
157
183
|
relatedIssue?: string;
|
|
158
184
|
/** CI job information */
|
|
@@ -332,12 +358,15 @@ declare class PiwiDashboardReporter {
|
|
|
332
358
|
onTestBegin(test: TestCase, result: TestResult): void;
|
|
333
359
|
/** Track suite-level setup steps (beforeAll/afterAll) not tied to any test */
|
|
334
360
|
private setupSteps;
|
|
361
|
+
/** Step categories streamed live while the run executes. */
|
|
362
|
+
private static readonly LIVE_STEP_CATEGORIES;
|
|
335
363
|
/**
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
364
|
+
* Whether a step streams live: its category is in `LIVE_STEP_CATEGORIES`
|
|
365
|
+
* and no ancestor is an `expect` step. The steps inside an assertion are its
|
|
366
|
+
* polling — every `expect.poll` attempt and `toPass` retry — not a step a
|
|
367
|
+
* human watches; the meaningful readout is the assertion around them.
|
|
339
368
|
*/
|
|
340
|
-
private static
|
|
369
|
+
private static isLiveStep;
|
|
341
370
|
/** Playwright reporter hook: called when a step (including hook/fixture) begins */
|
|
342
371
|
onStepBegin(test: TestCase | undefined, _result: TestResult | undefined, step: any): void;
|
|
343
372
|
/** Playwright reporter hook: called when a step (including hook/fixture) ends */
|