@piwitests/reporter 0.38.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 +142 -3
- package/dist/index.js +7 -2
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1945,8 +1945,144 @@ async function runProbe(argv) {
|
|
|
1945
1945
|
return EXIT_OK4;
|
|
1946
1946
|
}
|
|
1947
1947
|
|
|
1948
|
-
// src/cli/
|
|
1948
|
+
// src/cli/quality-report.ts
|
|
1949
|
+
var fs11 = __toESM(require("fs"));
|
|
1950
|
+
var EXIT_OK5 = 0;
|
|
1951
|
+
var EXIT_VERDICT = 1;
|
|
1952
|
+
var EXIT_ERROR5 = 2;
|
|
1953
|
+
var FORMATS = ["md", "json", "html", "pdf", "csv"];
|
|
1954
|
+
var DASHBOARDS = ["executive", "engineering", "overview"];
|
|
1949
1955
|
var USAGE7 = `
|
|
1956
|
+
piwi report \u2014 print a quality report from the dashboard
|
|
1957
|
+
|
|
1958
|
+
Usage:
|
|
1959
|
+
npx @piwitests/reporter report [options]
|
|
1960
|
+
|
|
1961
|
+
Connection:
|
|
1962
|
+
--server-url <url> Dashboard URL (env PIWI_DASHBOARD_URL)
|
|
1963
|
+
--api-key <key> API key (env PIWI_API_KEY)
|
|
1964
|
+
|
|
1965
|
+
What to report:
|
|
1966
|
+
--project <names|ids> Comma-separated projects (default: every project you can see)
|
|
1967
|
+
--period <period> 7d, 30d (default), last-month, this-quarter, 2026-08-01..2026-08-31, \u2026
|
|
1968
|
+
--compare <mode> previous (default), year, none
|
|
1969
|
+
--dashboard <name> executive (default), engineering, overview
|
|
1970
|
+
--branch <names> Comma-separated branches (default: each project's default branch)
|
|
1971
|
+
--environment <names> Comma-separated environments
|
|
1972
|
+
--selection <key> Only the tests of this selection
|
|
1973
|
+
--lang <code> Report language, such as en or fr (default: the dashboard's default)
|
|
1974
|
+
|
|
1975
|
+
Output:
|
|
1976
|
+
--format <fmt> md (default), json, html, pdf, csv
|
|
1977
|
+
--output <file> Write to a file instead of stdout (required for pdf)
|
|
1978
|
+
--fail-on <tone> Exit 1 when the verdict is bad, or mixed or bad (--fail-on mixed)
|
|
1979
|
+
-h, --help Show this help
|
|
1980
|
+
|
|
1981
|
+
Exit codes: 0 written, 1 written and the verdict is at or below --fail-on, 2 no report.
|
|
1982
|
+
`.trim();
|
|
1983
|
+
function readOption7(argv, name) {
|
|
1984
|
+
const withEquals = argv.find((arg) => arg.startsWith(`${name}=`));
|
|
1985
|
+
if (withEquals) return withEquals.slice(name.length + 1);
|
|
1986
|
+
const index = argv.indexOf(name);
|
|
1987
|
+
if (index === -1) return void 0;
|
|
1988
|
+
const value = argv[index + 1];
|
|
1989
|
+
return value && !value.startsWith("--") ? value : void 0;
|
|
1990
|
+
}
|
|
1991
|
+
function normalizePeriod(raw) {
|
|
1992
|
+
const days = raw.trim().match(/^(\d+)d$/);
|
|
1993
|
+
return days ? `last-${days[1]}d` : raw.trim();
|
|
1994
|
+
}
|
|
1995
|
+
function parseReportArgs(argv, env) {
|
|
1996
|
+
const serverUrl = (readOption7(argv, "--server-url") ?? env.PIWI_DASHBOARD_URL ?? "").replace(/\/$/, "");
|
|
1997
|
+
if (!serverUrl) throw new Error("No dashboard URL \u2014 pass --server-url or set PIWI_DASHBOARD_URL");
|
|
1998
|
+
const format = (readOption7(argv, "--format") ?? "md").toLowerCase();
|
|
1999
|
+
if (!FORMATS.includes(format)) {
|
|
2000
|
+
throw new Error(`--format must be one of ${FORMATS.join(", ")}, got "${format}"`);
|
|
2001
|
+
}
|
|
2002
|
+
const dashboard = readOption7(argv, "--dashboard") ?? "executive";
|
|
2003
|
+
if (!DASHBOARDS.includes(dashboard)) {
|
|
2004
|
+
throw new Error(`--dashboard must be one of ${DASHBOARDS.join(", ")}, got "${dashboard}"`);
|
|
2005
|
+
}
|
|
2006
|
+
const lang = readOption7(argv, "--lang") ?? null;
|
|
2007
|
+
if (lang !== null && !/^[a-z]{2,3}(-[a-z0-9]{2,8})*$/i.test(lang)) {
|
|
2008
|
+
throw new Error(`--lang must be a language code such as en or fr, got "${lang}"`);
|
|
2009
|
+
}
|
|
2010
|
+
const failOn = readOption7(argv, "--fail-on") ?? null;
|
|
2011
|
+
if (failOn !== null && failOn !== "bad" && failOn !== "mixed") {
|
|
2012
|
+
throw new Error(`--fail-on must be bad or mixed, got "${failOn}"`);
|
|
2013
|
+
}
|
|
2014
|
+
const output = readOption7(argv, "--output") ?? null;
|
|
2015
|
+
if (format === "pdf" && !output) throw new Error("A PDF needs --output <file>");
|
|
2016
|
+
return {
|
|
2017
|
+
serverUrl,
|
|
2018
|
+
apiKey: readOption7(argv, "--api-key") ?? env.PIWI_API_KEY ?? null,
|
|
2019
|
+
projects: (readOption7(argv, "--project") ?? "").split(",").map((p) => p.trim()).filter(Boolean),
|
|
2020
|
+
period: normalizePeriod(readOption7(argv, "--period") ?? "30d"),
|
|
2021
|
+
compare: readOption7(argv, "--compare") ?? null,
|
|
2022
|
+
dashboard,
|
|
2023
|
+
branches: readOption7(argv, "--branch") ?? null,
|
|
2024
|
+
environments: readOption7(argv, "--environment") ?? null,
|
|
2025
|
+
selection: readOption7(argv, "--selection") ?? null,
|
|
2026
|
+
lang,
|
|
2027
|
+
format,
|
|
2028
|
+
output,
|
|
2029
|
+
failOn
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
function reportQuery(args, projectIds, format) {
|
|
2033
|
+
const q = new URLSearchParams({ dashboard: args.dashboard, format, period: args.period });
|
|
2034
|
+
if (projectIds.length > 0) q.set("projects", projectIds.join(","));
|
|
2035
|
+
if (args.compare) q.set("compare", args.compare);
|
|
2036
|
+
if (args.branches) q.set("branches", args.branches);
|
|
2037
|
+
if (args.environments) q.set("environments", args.environments);
|
|
2038
|
+
if (args.selection) q.set("sel", args.selection);
|
|
2039
|
+
if (args.lang) q.set("lang", args.lang);
|
|
2040
|
+
return q;
|
|
2041
|
+
}
|
|
2042
|
+
function failsOn(tone, failOn) {
|
|
2043
|
+
if (!failOn) return false;
|
|
2044
|
+
return failOn === "mixed" ? tone !== "good" : tone === "bad";
|
|
2045
|
+
}
|
|
2046
|
+
async function fetchReport(args, query) {
|
|
2047
|
+
const res = await fetch(`${args.serverUrl}/api/reports/preview?${query}`, {
|
|
2048
|
+
headers: args.apiKey ? { "X-API-Key": args.apiKey } : {}
|
|
2049
|
+
});
|
|
2050
|
+
if (!res.ok) {
|
|
2051
|
+
const body = await res.json().catch(() => ({}));
|
|
2052
|
+
throw new Error(body.message || `Dashboard returned ${res.status}`);
|
|
2053
|
+
}
|
|
2054
|
+
return res;
|
|
2055
|
+
}
|
|
2056
|
+
async function runQualityReport(argv) {
|
|
2057
|
+
if (argv.includes("-h") || argv.includes("--help")) {
|
|
2058
|
+
console.log(USAGE7);
|
|
2059
|
+
return EXIT_OK5;
|
|
2060
|
+
}
|
|
2061
|
+
try {
|
|
2062
|
+
const args = parseReportArgs(argv, process.env);
|
|
2063
|
+
const projectIds = [];
|
|
2064
|
+
for (const project of args.projects) {
|
|
2065
|
+
projectIds.push(await resolveProjectId({ serverUrl: args.serverUrl, apiKey: args.apiKey, project, key: "" }));
|
|
2066
|
+
}
|
|
2067
|
+
const res = await fetchReport(args, reportQuery(args, projectIds, args.format));
|
|
2068
|
+
const bytes = new Uint8Array(await res.arrayBuffer());
|
|
2069
|
+
if (args.output) fs11.writeFileSync(args.output, bytes);
|
|
2070
|
+
else process.stdout.write(bytes);
|
|
2071
|
+
if (!args.failOn) return EXIT_OK5;
|
|
2072
|
+
const bundle = args.format === "json" ? JSON.parse(new TextDecoder().decode(bytes)) : await (await fetchReport(args, reportQuery(args, projectIds, "json"))).json();
|
|
2073
|
+
if (failsOn(bundle.verdict.tone, args.failOn)) {
|
|
2074
|
+
console.error(`piwi report: the verdict is ${bundle.verdict.tone}`);
|
|
2075
|
+
return EXIT_VERDICT;
|
|
2076
|
+
}
|
|
2077
|
+
return EXIT_OK5;
|
|
2078
|
+
} catch (e) {
|
|
2079
|
+
console.error(`piwi report: ${e.message}`);
|
|
2080
|
+
return EXIT_ERROR5;
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
|
|
2084
|
+
// src/cli/index.ts
|
|
2085
|
+
var USAGE8 = `
|
|
1950
2086
|
piwi \u2014 companion commands for the Piwi Dashboard reporter
|
|
1951
2087
|
|
|
1952
2088
|
Usage:
|
|
@@ -1956,6 +2092,7 @@ Commands:
|
|
|
1956
2092
|
init Wire a Playwright project up to a Piwi Dashboard
|
|
1957
2093
|
skills Install the Piwi agent skills into this project
|
|
1958
2094
|
gate Fail a CI job on the dashboard's analysis of a run
|
|
2095
|
+
report Print a quality report (Markdown, PDF, HTML, CSV or JSON)
|
|
1959
2096
|
select Print the Playwright args for a saved test selection
|
|
1960
2097
|
run Run a saved test selection with playwright test
|
|
1961
2098
|
probe Run the dashboard's probe plan and record what the suite noticed
|
|
@@ -1974,6 +2111,8 @@ async function main() {
|
|
|
1974
2111
|
return runSkills(rest, findTemplatesDir(__dirname));
|
|
1975
2112
|
case "gate":
|
|
1976
2113
|
return runGate(rest);
|
|
2114
|
+
case "report":
|
|
2115
|
+
return runQualityReport(rest);
|
|
1977
2116
|
case "select":
|
|
1978
2117
|
return runSelect(rest);
|
|
1979
2118
|
case "run":
|
|
@@ -1985,12 +2124,12 @@ async function main() {
|
|
|
1985
2124
|
case void 0:
|
|
1986
2125
|
case "-h":
|
|
1987
2126
|
case "--help":
|
|
1988
|
-
console.log(
|
|
2127
|
+
console.log(USAGE8);
|
|
1989
2128
|
return 0;
|
|
1990
2129
|
default:
|
|
1991
2130
|
console.error(`piwi: unknown command "${command}"
|
|
1992
2131
|
`);
|
|
1993
|
-
console.error(
|
|
2132
|
+
console.error(USAGE8);
|
|
1994
2133
|
return 2;
|
|
1995
2134
|
}
|
|
1996
2135
|
}
|
package/dist/index.js
CHANGED
|
@@ -1382,7 +1382,7 @@ var MetadataCollector = class {
|
|
|
1382
1382
|
/**
|
|
1383
1383
|
* Walk the test's parent `describe` suites to extract the suite path (titles)
|
|
1384
1384
|
* and per-level config (parallel mode + annotations). Reaches into Playwright
|
|
1385
|
-
* suite internals (`_parallelMode`, `
|
|
1385
|
+
* suite internals (`_parallelMode`, `_staticAnnotations`) — kept here next to
|
|
1386
1386
|
* `getBrowserConfig` so all such access is guarded behind one class.
|
|
1387
1387
|
*/
|
|
1388
1388
|
getSuiteInfo(test) {
|
|
@@ -1399,7 +1399,10 @@ var MetadataCollector = class {
|
|
|
1399
1399
|
suitePath.push(s.title);
|
|
1400
1400
|
const rawMode = s._parallelMode;
|
|
1401
1401
|
const mode = rawMode === "parallel" ? "parallel" : rawMode === "serial" ? "serial" : "default";
|
|
1402
|
-
const
|
|
1402
|
+
const staticAnnotations = s._staticAnnotations ?? [];
|
|
1403
|
+
const annotations = staticAnnotations.map(
|
|
1404
|
+
(a) => a.description === void 0 ? { type: a.type } : { type: a.type, description: a.description }
|
|
1405
|
+
);
|
|
1403
1406
|
suiteConfig.push({ mode, annotations });
|
|
1404
1407
|
}
|
|
1405
1408
|
return { suitePath, suiteConfig };
|
|
@@ -1497,6 +1500,8 @@ var MetadataCollector = class {
|
|
|
1497
1500
|
// The origin of `baseURL` is what the graph treats as the run's own
|
|
1498
1501
|
// surface, so only requests to it become route nodes.
|
|
1499
1502
|
baseURL: p.use?.baseURL,
|
|
1503
|
+
// The attribute `getByTestId` reads, so the locator index can resolve those locators on a live page.
|
|
1504
|
+
testIdAttribute: p.use?.testIdAttribute,
|
|
1500
1505
|
viewport: p.use?.viewport,
|
|
1501
1506
|
deviceScaleFactor: p.use?.deviceScaleFactor
|
|
1502
1507
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@piwitests/reporter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.39.0",
|
|
4
4
|
"description": "Playwright reporter that streams results, traces and HTML reports to a Piwi Dashboard instance",
|
|
5
5
|
"url": "https://github.com/PiwiTests/platform",
|
|
6
6
|
"homepage": "https://piwitests.dev",
|