@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 CHANGED
@@ -1945,8 +1945,144 @@ async function runProbe(argv) {
1945
1945
  return EXIT_OK4;
1946
1946
  }
1947
1947
 
1948
- // src/cli/index.ts
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(USAGE7);
2127
+ console.log(USAGE8);
1989
2128
  return 0;
1990
2129
  default:
1991
2130
  console.error(`piwi: unknown command "${command}"
1992
2131
  `);
1993
- console.error(USAGE7);
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`, `_annotations`) — kept here next to
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 annotations = s._annotations ?? [];
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.38.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",