@piwitests/reporter 0.18.2 → 0.19.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.d.ts +1 -0
- package/dist/cli/index.js +205 -0
- package/dist/global-setup-module.js +59 -12
- package/dist/index.d.ts +10 -2
- package/dist/index.js +389 -162
- package/dist/internal/capture/capture-fixtures.js +115 -45
- package/dist/internal/capture/locator-healing.d.ts +29 -1
- package/dist/internal/capture/locator-healing.js +38 -0
- package/package.json +8 -3
package/README.md
CHANGED
|
@@ -168,7 +168,7 @@ Then import `test` from your fixtures file in every spec — a spec that imports
|
|
|
168
168
|
- **Console entries** — `warning`, `error`, and `assert` messages with their source location.
|
|
169
169
|
- **Browser Web Vitals** — TTFB, DOM Interactive, DOMContentLoaded, Load Complete, First Paint, First Contentful Paint — displayed with color-coded thresholds.
|
|
170
170
|
- **ARIA snapshot** — captured automatically when a test fails, shown as failure evidence and fed to the AI diagnosis.
|
|
171
|
-
- **Locator snapshots** — for each
|
|
171
|
+
- **Locator snapshots** — for each element a test proves resolvable (successful actions and passing `expect(locator)` assertions alike), its attributes plus ranked alternative locators, stamped with the call site. These power locator healing; when a failing locator matches nothing, a fresh suggestion is attached as a Playwright annotation.
|
|
172
172
|
|
|
173
173
|
Capture works for the `page` fixture, `browser.newPage()`, `browser.newContext().newPage()`, and popups. Everything is only collected when `collectPerformanceMetrics` is `true` (the default); locator snapshots can be disabled separately with `captureLocators: false`.
|
|
174
174
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/cli/gate.ts
|
|
27
|
+
var fs = __toESM(require("fs"));
|
|
28
|
+
|
|
29
|
+
// ../packages/core/src/gate.ts
|
|
30
|
+
function formatGateResult(result) {
|
|
31
|
+
const { facts } = result;
|
|
32
|
+
const lines = [
|
|
33
|
+
result.passed ? `\u2714 Piwi gate passed \u2014 ${facts.projectName} run #${facts.runId}` : `\u2716 Piwi gate failed \u2014 ${facts.projectName} run #${facts.runId}`,
|
|
34
|
+
` ${facts.totalTests} tests, ${facts.failedTests} failed, ${facts.newRegressions} new, ${facts.newFlaky} newly flaky`
|
|
35
|
+
];
|
|
36
|
+
if (facts.quarantinedFailures > 0) {
|
|
37
|
+
lines.push(
|
|
38
|
+
` ${facts.quarantinedFailures} failing ${facts.quarantinedFailures === 1 ? "test is" : "tests are"} quarantined and did not count`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
for (const violation of result.violations) lines.push(` \u2716 ${violation.message}`);
|
|
42
|
+
lines.push(` ${facts.runUrl}`);
|
|
43
|
+
return lines.join("\n");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/cli/gate.ts
|
|
47
|
+
var EXIT_OK = 0;
|
|
48
|
+
var EXIT_VIOLATED = 1;
|
|
49
|
+
var EXIT_ERROR = 2;
|
|
50
|
+
var USAGE = `
|
|
51
|
+
piwi gate \u2014 fail a CI job on the dashboard's analysis of a run
|
|
52
|
+
|
|
53
|
+
Usage:
|
|
54
|
+
npx piwi gate [options]
|
|
55
|
+
|
|
56
|
+
Where the run comes from (first match wins):
|
|
57
|
+
--run-id <id> Run id to evaluate
|
|
58
|
+
--from-file <path> Read runId from the reporter's output JSON
|
|
59
|
+
PIWI_OUTPUT_FILE Same, from the environment
|
|
60
|
+
(default) ./piwi-run.json, if it exists
|
|
61
|
+
|
|
62
|
+
Connection:
|
|
63
|
+
--server-url <url> Dashboard URL (env PIWI_DASHBOARD_URL)
|
|
64
|
+
--api-key <key> API key (env PIWI_API_KEY)
|
|
65
|
+
|
|
66
|
+
Policy (at least one is required):
|
|
67
|
+
--require-tag <tags> Comma-separated; every test carrying the tag must pass
|
|
68
|
+
--max-failed <n> Fail when more than n tests failed
|
|
69
|
+
--max-new-regressions <n> Fail when more than n tests newly started failing
|
|
70
|
+
--max-new-flaky <n> Fail when more than n tests newly became flaky
|
|
71
|
+
--max-quarantined <n> Fail when more than n tests are quarantined
|
|
72
|
+
--fail-on-new-cluster Fail when this run introduced a new failure cluster
|
|
73
|
+
|
|
74
|
+
Other:
|
|
75
|
+
--json Print the raw result as JSON instead of a summary
|
|
76
|
+
-h, --help Show this help
|
|
77
|
+
|
|
78
|
+
Exit codes: 0 satisfied, 1 violated, 2 could not evaluate.
|
|
79
|
+
`.trim();
|
|
80
|
+
function readOption(argv, name) {
|
|
81
|
+
const withEquals = argv.find((arg) => arg.startsWith(`${name}=`));
|
|
82
|
+
if (withEquals) return withEquals.slice(name.length + 1);
|
|
83
|
+
const index = argv.indexOf(name);
|
|
84
|
+
if (index === -1) return void 0;
|
|
85
|
+
const value = argv[index + 1];
|
|
86
|
+
return value && !value.startsWith("--") ? value : void 0;
|
|
87
|
+
}
|
|
88
|
+
function readCount(argv, name) {
|
|
89
|
+
const raw = readOption(argv, name);
|
|
90
|
+
if (raw === void 0) return void 0;
|
|
91
|
+
const n = Number(raw);
|
|
92
|
+
if (!Number.isFinite(n) || n < 0) throw new Error(`${name} expects a non-negative number, got "${raw}"`);
|
|
93
|
+
return Math.floor(n);
|
|
94
|
+
}
|
|
95
|
+
function readRunIdFromFile(path) {
|
|
96
|
+
try {
|
|
97
|
+
const parsed = JSON.parse(fs.readFileSync(path, "utf-8"));
|
|
98
|
+
const runId = Number(parsed.runId);
|
|
99
|
+
return Number.isFinite(runId) && runId > 0 ? runId : null;
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function parseGateArgs(argv, env) {
|
|
105
|
+
const serverUrl = (readOption(argv, "--server-url") ?? env.PIWI_DASHBOARD_URL ?? "").replace(/\/$/, "");
|
|
106
|
+
if (!serverUrl) throw new Error("No dashboard URL \u2014 pass --server-url or set PIWI_DASHBOARD_URL");
|
|
107
|
+
let runId = Number(readOption(argv, "--run-id") ?? NaN);
|
|
108
|
+
if (!Number.isFinite(runId) || runId <= 0) {
|
|
109
|
+
const candidate = readOption(argv, "--from-file") ?? env.PIWI_OUTPUT_FILE ?? "piwi-run.json";
|
|
110
|
+
runId = readRunIdFromFile(candidate) ?? NaN;
|
|
111
|
+
}
|
|
112
|
+
if (!Number.isFinite(runId) || runId <= 0) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
"No run to evaluate \u2014 pass --run-id, or set PIWI_OUTPUT_FILE so the reporter records the run it submitted"
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
const requireTags = (readOption(argv, "--require-tag") ?? "").split(",").map((tag) => tag.trim().replace(/^@+/, "")).filter(Boolean);
|
|
118
|
+
const policy = {
|
|
119
|
+
requireTags,
|
|
120
|
+
maxFailed: readCount(argv, "--max-failed"),
|
|
121
|
+
maxNewRegressions: readCount(argv, "--max-new-regressions"),
|
|
122
|
+
maxNewFlaky: readCount(argv, "--max-new-flaky"),
|
|
123
|
+
maxQuarantined: readCount(argv, "--max-quarantined"),
|
|
124
|
+
failOnNewCluster: argv.includes("--fail-on-new-cluster")
|
|
125
|
+
};
|
|
126
|
+
return { serverUrl, apiKey: readOption(argv, "--api-key") ?? env.PIWI_API_KEY ?? null, runId, policy };
|
|
127
|
+
}
|
|
128
|
+
async function requestGate(args) {
|
|
129
|
+
const headers = { "Content-Type": "application/json" };
|
|
130
|
+
if (args.apiKey) headers["X-API-Key"] = args.apiKey;
|
|
131
|
+
const res = await fetch(`${args.serverUrl}/api/test-runs/${args.runId}/gate`, {
|
|
132
|
+
method: "POST",
|
|
133
|
+
headers,
|
|
134
|
+
body: JSON.stringify(args.policy)
|
|
135
|
+
});
|
|
136
|
+
if (!res.ok) {
|
|
137
|
+
const body = await res.json().catch(() => ({}));
|
|
138
|
+
throw new Error(body.message || `Dashboard returned ${res.status} evaluating the gate`);
|
|
139
|
+
}
|
|
140
|
+
return await res.json();
|
|
141
|
+
}
|
|
142
|
+
async function runGate(argv, env = process.env) {
|
|
143
|
+
if (argv.includes("-h") || argv.includes("--help")) {
|
|
144
|
+
console.log(USAGE);
|
|
145
|
+
return EXIT_OK;
|
|
146
|
+
}
|
|
147
|
+
let args;
|
|
148
|
+
try {
|
|
149
|
+
args = parseGateArgs(argv, env);
|
|
150
|
+
} catch (e) {
|
|
151
|
+
console.error(`piwi gate: ${e.message}
|
|
152
|
+
`);
|
|
153
|
+
console.error(USAGE);
|
|
154
|
+
return EXIT_ERROR;
|
|
155
|
+
}
|
|
156
|
+
let result;
|
|
157
|
+
try {
|
|
158
|
+
result = await requestGate(args);
|
|
159
|
+
} catch (e) {
|
|
160
|
+
console.error(`piwi gate: ${e.message}`);
|
|
161
|
+
return EXIT_ERROR;
|
|
162
|
+
}
|
|
163
|
+
if (argv.includes("--json")) {
|
|
164
|
+
console.log(JSON.stringify(result, null, 2));
|
|
165
|
+
} else {
|
|
166
|
+
console.log(formatGateResult(result));
|
|
167
|
+
}
|
|
168
|
+
return result.passed ? EXIT_OK : EXIT_VIOLATED;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/cli/index.ts
|
|
172
|
+
var USAGE2 = `
|
|
173
|
+
piwi \u2014 companion commands for the Piwi Dashboard reporter
|
|
174
|
+
|
|
175
|
+
Usage:
|
|
176
|
+
npx piwi <command> [options]
|
|
177
|
+
|
|
178
|
+
Commands:
|
|
179
|
+
gate Fail a CI job on the dashboard's analysis of a run
|
|
180
|
+
|
|
181
|
+
Run \`npx piwi gate --help\` for that command's options.
|
|
182
|
+
`.trim();
|
|
183
|
+
async function main() {
|
|
184
|
+
const [command, ...rest] = process.argv.slice(2);
|
|
185
|
+
switch (command) {
|
|
186
|
+
case "gate":
|
|
187
|
+
return runGate(rest);
|
|
188
|
+
case void 0:
|
|
189
|
+
case "-h":
|
|
190
|
+
case "--help":
|
|
191
|
+
console.log(USAGE2);
|
|
192
|
+
return 0;
|
|
193
|
+
default:
|
|
194
|
+
console.error(`piwi: unknown command "${command}"
|
|
195
|
+
`);
|
|
196
|
+
console.error(USAGE2);
|
|
197
|
+
return 2;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
main().then((code) => {
|
|
201
|
+
process.exitCode = code;
|
|
202
|
+
}).catch((e) => {
|
|
203
|
+
console.error(`piwi: ${e.message}`);
|
|
204
|
+
process.exitCode = 2;
|
|
205
|
+
});
|
|
@@ -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 path3 = __toESM(require("path"));
|
|
39
39
|
|
|
40
40
|
// src/internal/support/errors.ts
|
|
41
41
|
function errorMessage(error) {
|
|
@@ -44,7 +44,27 @@ function errorMessage(error) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// src/public/global-setup.ts
|
|
47
|
-
var
|
|
47
|
+
var fs3 = __toESM(require("fs"));
|
|
48
|
+
|
|
49
|
+
// src/internal/config/desktop.ts
|
|
50
|
+
var fs = __toESM(require("fs"));
|
|
51
|
+
var os = __toESM(require("os"));
|
|
52
|
+
var path = __toESM(require("path"));
|
|
53
|
+
function defaultDesktopConfigPath() {
|
|
54
|
+
return path.join(os.homedir(), ".piwi", "desktop.json");
|
|
55
|
+
}
|
|
56
|
+
function readDesktopConfig(filePath) {
|
|
57
|
+
try {
|
|
58
|
+
const parsed = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
59
|
+
const url = parsed?.url;
|
|
60
|
+
const token = parsed?.token;
|
|
61
|
+
if (typeof url !== "string" || !url) return null;
|
|
62
|
+
if (typeof token !== "string" || !token) return null;
|
|
63
|
+
return { url, token };
|
|
64
|
+
} catch {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
48
68
|
|
|
49
69
|
// src/internal/config/env.ts
|
|
50
70
|
var DEFAULTS = {
|
|
@@ -89,6 +109,7 @@ var PIWI_ENV_KEYS = {
|
|
|
89
109
|
pickLocatorOnFailure: "PIWI_PICK_LOCATOR_ON_FAIL",
|
|
90
110
|
outputFile: "PIWI_OUTPUT_FILE"
|
|
91
111
|
};
|
|
112
|
+
var PIWI_DESKTOP_CONFIG_ENV = "PIWI_DESKTOP_CONFIG";
|
|
92
113
|
function readBool(val) {
|
|
93
114
|
if (val === void 0) return void 0;
|
|
94
115
|
return val === "true";
|
|
@@ -115,6 +136,7 @@ var ENV_FALLBACK_SPECS = [
|
|
|
115
136
|
{ option: "pickLocatorOnFailure", env: PIWI_ENV_KEYS.pickLocatorOnFailure, kind: "bool" },
|
|
116
137
|
{ option: "outputFile", env: PIWI_ENV_KEYS.outputFile, kind: "string" }
|
|
117
138
|
];
|
|
139
|
+
var desktopDiscovered = false;
|
|
118
140
|
function resolveOptions(raw) {
|
|
119
141
|
const env = process.env;
|
|
120
142
|
const mergedRaw = { ...raw };
|
|
@@ -127,6 +149,17 @@ function resolveOptions(raw) {
|
|
|
127
149
|
mergedRaw[spec.option] = spec.kind === "number" ? Number(value) : value;
|
|
128
150
|
}
|
|
129
151
|
}
|
|
152
|
+
desktopDiscovered = false;
|
|
153
|
+
const serverUrlUnset = mergedRaw.serverUrl === void 0;
|
|
154
|
+
const apiKeyUnset = mergedRaw.apiKey === void 0 || mergedRaw.apiKey === null;
|
|
155
|
+
if (serverUrlUnset && apiKeyUnset) {
|
|
156
|
+
const desktop = readDesktopConfig(env[PIWI_DESKTOP_CONFIG_ENV] || defaultDesktopConfigPath());
|
|
157
|
+
if (desktop) {
|
|
158
|
+
mergedRaw.serverUrl = desktop.url;
|
|
159
|
+
mergedRaw.apiKey = desktop.token;
|
|
160
|
+
desktopDiscovered = true;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
130
163
|
const opts = { ...DEFAULTS, ...mergedRaw };
|
|
131
164
|
if (env[PIWI_ENV_KEYS.verbose] !== void 0) opts.verbose = env[PIWI_ENV_KEYS.verbose] === "true";
|
|
132
165
|
return opts;
|
|
@@ -318,12 +351,12 @@ var HttpClient = class {
|
|
|
318
351
|
|
|
319
352
|
// src/internal/support/instance-id.ts
|
|
320
353
|
var crypto = __toESM(require("crypto"));
|
|
321
|
-
var
|
|
354
|
+
var os2 = __toESM(require("os"));
|
|
322
355
|
function hashForProject(projectName) {
|
|
323
356
|
return crypto.createHash("sha1").update(projectName).digest("hex").slice(0, 16);
|
|
324
357
|
}
|
|
325
358
|
function computeInstanceId(projectName, runLabel) {
|
|
326
|
-
const seed = runLabel ? `${projectName}|${runLabel}` : `${
|
|
359
|
+
const seed = runLabel ? `${projectName}|${runLabel}` : `${os2.hostname()}|${projectName}`;
|
|
327
360
|
return crypto.createHash("sha256").update(seed).digest("hex").slice(0, 16);
|
|
328
361
|
}
|
|
329
362
|
|
|
@@ -346,24 +379,33 @@ function detectCiRunLabel() {
|
|
|
346
379
|
}
|
|
347
380
|
|
|
348
381
|
// src/internal/support/setup-file.ts
|
|
349
|
-
var
|
|
350
|
-
var
|
|
351
|
-
var
|
|
382
|
+
var path2 = __toESM(require("path"));
|
|
383
|
+
var os3 = __toESM(require("os"));
|
|
384
|
+
var fs2 = __toESM(require("fs"));
|
|
352
385
|
function getSetupFilePath(projectName) {
|
|
353
|
-
return
|
|
386
|
+
return path2.join(os3.tmpdir(), `piwi-dashboard-setup-${hashForProject(projectName)}.json`);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// src/internal/support/run-mode.ts
|
|
390
|
+
var PW_UI_FLAGS = ["--ui", "--ui-host", "--ui-port"];
|
|
391
|
+
function isUiMode(argv = process.argv) {
|
|
392
|
+
const args = argv.slice(2);
|
|
393
|
+
const testIdx = args.indexOf("test");
|
|
394
|
+
const rest = testIdx >= 0 ? args.slice(testIdx + 1) : args;
|
|
395
|
+
return rest.some((tok) => PW_UI_FLAGS.some((flag) => tok === flag || tok.startsWith(`${flag}=`)));
|
|
354
396
|
}
|
|
355
397
|
|
|
356
398
|
// src/public/global-setup.ts
|
|
357
399
|
function createGlobalSetup(options, userSetup) {
|
|
358
400
|
return async function globalSetupFn(config) {
|
|
359
|
-
const piwiReporterPath =
|
|
401
|
+
const piwiReporterPath = path3.resolve(__dirname, "./index.js");
|
|
360
402
|
let inlineReporterOptions = {};
|
|
361
403
|
if (Array.isArray(config?.reporter)) {
|
|
362
404
|
for (const r of config.reporter) {
|
|
363
405
|
if (!Array.isArray(r) || typeof r[0] !== "string") continue;
|
|
364
406
|
const isPiwi = r[0].toLowerCase().includes("piwi") || (() => {
|
|
365
407
|
try {
|
|
366
|
-
return
|
|
408
|
+
return path3.resolve(require.resolve(r[0])) === piwiReporterPath;
|
|
367
409
|
} catch {
|
|
368
410
|
return false;
|
|
369
411
|
}
|
|
@@ -376,6 +418,11 @@ function createGlobalSetup(options, userSetup) {
|
|
|
376
418
|
}
|
|
377
419
|
const opts = resolveOptions({ ...inlineReporterOptions, ...options });
|
|
378
420
|
const logger = new Logger(opts.verbose ?? false);
|
|
421
|
+
if (isUiMode()) {
|
|
422
|
+
logger.debug("UI mode detected \u2014 skipping run registration.");
|
|
423
|
+
if (userSetup) return userSetup(config);
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
379
426
|
if (opts.enabled === false || !opts.serverUrl) {
|
|
380
427
|
logger.info("Not enabled \u2014 set PIWI_DASHBOARD_URL or serverUrl to enable.");
|
|
381
428
|
if (userSetup) return userSetup(config);
|
|
@@ -385,7 +432,7 @@ function createGlobalSetup(options, userSetup) {
|
|
|
385
432
|
if (!Array.isArray(r) || typeof r[0] !== "string") return false;
|
|
386
433
|
if (r[0].toLowerCase().includes("piwi")) return true;
|
|
387
434
|
try {
|
|
388
|
-
return
|
|
435
|
+
return path3.resolve(require.resolve(r[0])) === piwiReporterPath;
|
|
389
436
|
} catch {
|
|
390
437
|
return false;
|
|
391
438
|
}
|
|
@@ -417,7 +464,7 @@ function createGlobalSetup(options, userSetup) {
|
|
|
417
464
|
auth
|
|
418
465
|
);
|
|
419
466
|
if (response?.runId && response?.setupToken) {
|
|
420
|
-
|
|
467
|
+
fs3.writeFileSync(
|
|
421
468
|
getSetupFilePath(opts.projectName),
|
|
422
469
|
JSON.stringify({
|
|
423
470
|
runId: response.runId,
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,14 @@ import { PlaywrightTestConfig } from '@playwright/test';
|
|
|
3
3
|
export { PlaywrightTestConfig } from '@playwright/test';
|
|
4
4
|
export { PiwiFixtures, extendPiwiFixtures, piwiFixtures } from './internal/capture/capture-fixtures.js';
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Options for configuring the Piwi Dashboard reporter.
|
|
8
|
+
*
|
|
9
|
+
* Piwi options only — the Playwright config belongs in `defineConfig` (i.e. in
|
|
10
|
+
* `wrapConfig`'s first argument). These go in `wrapConfig`'s second argument or
|
|
11
|
+
* in the reporter entry's options (`['@piwitests/reporter', { … }]`).
|
|
12
|
+
*/
|
|
13
|
+
interface PiwiDashboardOptions {
|
|
8
14
|
/** Explicitly enable or disable the reporter. Defaults to `true` when `serverUrl` is set. Set to `false` to disable even if `serverUrl` is provided. */
|
|
9
15
|
enabled?: boolean;
|
|
10
16
|
/** URL of the Piwi Dashboard server */
|
|
@@ -182,6 +188,8 @@ declare class PiwiDashboardReporter {
|
|
|
182
188
|
private shardInfo;
|
|
183
189
|
private metadata;
|
|
184
190
|
private enabled;
|
|
191
|
+
/** True when the server URL and API key came from the desktop app, not from config. */
|
|
192
|
+
private viaDesktopApp;
|
|
185
193
|
private isFullRun;
|
|
186
194
|
private filterDetails;
|
|
187
195
|
private httpClient;
|