@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 +1 -1
- package/dist/cli/index.js +76 -40
- package/dist/global-setup-module.js +88 -14
- package/dist/index.d.ts +27 -8
- package/dist/index.js +17501 -13740
- package/dist/internal/capture/attachments.d.ts +1 -1
- package/dist/internal/capture/capture-fixtures.d.ts +142 -7
- package/dist/internal/capture/capture-fixtures.js +4394 -1039
- package/dist/internal/capture/locator-healing.d.ts +222 -4
- package/dist/internal/capture/locator-healing.js +22 -10
- package/dist/internal/capture/pick-on-failure.d.ts +184 -6
- package/dist/internal/capture/pick-on-failure.js +29 -5
- package/dist/types-CGr9jJZ_.d.ts +87 -0
- package/dist/wire-7zsqlBoY.d.ts +48 -0
- package/package.json +2 -2
- package/templates/skills/write-the-missing-test/SKILL.md +6 -4
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.
|
|
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
|
|
1195
|
-
if (
|
|
1196
|
-
return
|
|
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
|
-
|
|
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
|
|
1863
|
+
const recorded = target.alternatives.flatMap((alt) => {
|
|
1849
1864
|
const safe = safeLocator(alt.locator);
|
|
1850
1865
|
return safe ? [safe] : [];
|
|
1851
1866
|
});
|
|
1852
|
-
if (
|
|
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
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
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
|
|
3829
|
-
return
|
|
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
|
|
3841
|
-
if (
|
|
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
|
|
6110
|
-
if (!TRANSLATION_EXTENSIONS.test(
|
|
6111
|
-
return globs.some((g) => pathGlobToRegExp(g).test(
|
|
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
|
-
|
|
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
|
|
7794
|
-
const own =
|
|
7795
|
-
const playwrightArgs2 =
|
|
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
|
|
7847
|
-
|
|
7848
|
-
|
|
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
|
|
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]
|
|
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
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
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 '
|
|
6
|
-
import '
|
|
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,
|
|
63
|
-
* snapshot)
|
|
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 (
|
|
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 */
|