@piwitests/reporter 0.38.0 → 0.40.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 +3 -3
- package/dist/cli/index.js +142 -3
- package/dist/index.d.ts +3 -2
- package/dist/index.js +476 -83
- package/dist/internal/capture/attachments.d.ts +1 -0
- package/dist/internal/capture/attachments.js +1 -0
- package/dist/internal/capture/capture-fixtures.d.ts +47 -1
- package/dist/internal/capture/capture-fixtures.js +309 -73
- package/dist/internal/capture/locator-healing.js +7 -1
- package/dist/internal/capture/pick-on-failure.js +7 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ From your Playwright project, `npx @piwitests/reporter init` installs the report
|
|
|
18
18
|
npx @piwitests/reporter init --server-url http://localhost:3000 --project my-project
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
Every step is idempotent (safe to re-run); a config shape it will not rewrite is reported as `manual` with the exact change to make, never mangled. Add `--dry-run` to preview or `--json` for a machine-readable plan an agent can act on. It also installs the [Piwi agent skills](https://piwitests.dev/features/
|
|
21
|
+
Every step is idempotent (safe to re-run); a config shape it will not rewrite is reported as `manual` with the exact change to make, never mangled. Add `--dry-run` to preview or `--json` for a machine-readable plan an agent can act on. It also installs the [Piwi agent skills](https://piwitests.dev/features/agent-skills) so your coding agent can investigate failures, heal locators, and stabilize flaky tests. Run `npx @piwitests/reporter init --help` for all options, or wire it up by hand with the steps below.
|
|
22
22
|
|
|
23
23
|
> The package is `@piwitests/reporter`; its command is `piwi`. Invoke it through the package name (`npx @piwitests/reporter <command>`) so npx resolves this package — `npx piwi` would fetch an unrelated `piwi` from npm. Once the reporter is a project dependency, `npx piwi <command>` resolves the local binary and works too.
|
|
24
24
|
|
|
@@ -57,12 +57,12 @@ Import `test` from this file in your specs instead of `@playwright/test`. A spec
|
|
|
57
57
|
|
|
58
58
|
- **Run history, statuses, errors, traces, reports and live streaming** — with no test-code changes.
|
|
59
59
|
- **Capture fixtures** add slow-endpoint analysis, Web Vitals, console capture, failure-time ARIA snapshots and locator healing.
|
|
60
|
-
- **AI steps** (`page.piwiLocator(...)` / `page.piwiRun(...)`) drive flows in plain English, compiled once and replayed deterministically with zero LLM calls in CI — see [AI steps](https://piwitests.dev/
|
|
60
|
+
- **AI steps** (`page.piwiLocator(...)` / `page.piwiRun(...)`) drive flows in plain English, compiled once and replayed deterministically with zero LLM calls in CI — see [AI steps](https://piwitests.dev/features/ai-steps).
|
|
61
61
|
- **CI-aware** — auto-detects the run label, branch and commit, publishes the run URL back to the pipeline, and shards into a single run. See [CI & sharding](https://piwitests.dev/guide/ci).
|
|
62
62
|
|
|
63
63
|
## Configuration
|
|
64
64
|
|
|
65
|
-
Every option can also be set via a `PIWI_*` environment variable (config wins over env). The full option and env-var reference lives in
|
|
65
|
+
Every option can also be set via a `PIWI_*` environment variable (config wins over env). The full option and env-var reference lives in [Reporter options](https://piwitests.dev/reference/reporter-options); authentication for CI is covered under [API keys](https://piwitests.dev/operate/api-keys).
|
|
66
66
|
|
|
67
67
|
## Requirements
|
|
68
68
|
|
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.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { PlaywrightTestConfig, Locator, TestType, Page, Fixtures, PlaywrightTest
|
|
|
3
3
|
export { PlaywrightTestConfig } from '@playwright/test';
|
|
4
4
|
export { PiwiFixtures, extendPiwiFixtures, piwiFixtures } from './internal/capture/capture-fixtures.js';
|
|
5
5
|
import '@piwitests/picker-dom';
|
|
6
|
+
import '@piwitests/core/wire';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Options for configuring the Piwi Dashboard reporter.
|
|
@@ -175,8 +176,8 @@ interface PiwiDashboardOptions {
|
|
|
175
176
|
label?: string;
|
|
176
177
|
/**
|
|
177
178
|
* 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.
|
|
179
|
-
* with `PIWI_KEEP=true`.
|
|
179
|
+
* for the runs worth keeping, such as release or tag builds. Defaults to
|
|
180
|
+
* `false`. Can also be set with `PIWI_KEEP=true`.
|
|
180
181
|
*/
|
|
181
182
|
keep?: boolean;
|
|
182
183
|
/** Related issue reference, e.g. `"JIRA-123"` */
|