feat(cli): show artifacts and top findings
Some checks are pending
Check / Node 22 (push) Waiting to run
Some checks are pending
Check / Node 22 (push) Waiting to run
This commit is contained in:
parent
c89fb0b1c8
commit
ed80dc2d15
@ -83,7 +83,7 @@ async function runCheck(commandArgs) {
|
||||
if (json) {
|
||||
console.log(JSON.stringify(report, null, 2));
|
||||
} else {
|
||||
console.log(renderTextSummary(report));
|
||||
console.log(renderTextSummary(report, { artifacts: paths }));
|
||||
}
|
||||
|
||||
if (report.status !== "pass") {
|
||||
@ -128,7 +128,7 @@ async function runReport(command, commandArgs) {
|
||||
if (json) {
|
||||
console.log(JSON.stringify(report, null, 2));
|
||||
} else {
|
||||
console.log(renderTextSummary(report));
|
||||
console.log(renderTextSummary(report, { artifacts: paths }));
|
||||
}
|
||||
|
||||
if (check && report.status !== "pass") {
|
||||
|
||||
@ -260,13 +260,48 @@ export async function writeCompatibilityReport(report, options = {}) {
|
||||
);
|
||||
}
|
||||
|
||||
export function renderTextSummary(report) {
|
||||
return [
|
||||
export function renderTextSummary(report, options = {}) {
|
||||
const lines = [
|
||||
`Status: ${report.status.toUpperCase()}`,
|
||||
`Fixtures: ${report.summary.fixtureCount}`,
|
||||
`Breakages: ${report.summary.breakageCount}`,
|
||||
...(typeof report.summary.issueCount === "number" ? [`Issues: ${report.summary.issueCount}`] : []),
|
||||
`Logs: ${report.summary.logCount}`,
|
||||
].join("\n");
|
||||
];
|
||||
const artifacts = Object.entries(options.artifacts ?? {}).filter(([, filePath]) => Boolean(filePath));
|
||||
if (artifacts.length > 0) {
|
||||
lines.push("", "Reports:", ...artifacts.map(([name, filePath]) => `- ${artifactLabel(name)}: ${filePath}`));
|
||||
}
|
||||
const findings = topTextFindings(report, options.topFindings ?? 3);
|
||||
if (findings.length > 0) {
|
||||
lines.push("", "Top findings:", ...findings.map((finding) => `- ${finding}`));
|
||||
}
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function topTextFindings(report, limit) {
|
||||
if (report.status === "pass" || limit <= 0) {
|
||||
return [];
|
||||
}
|
||||
return [
|
||||
...(report.breakages ?? []).map((finding) => formatTextFinding(finding, "breakage")),
|
||||
...(report.issues ?? [])
|
||||
.filter((issue) => issue.status === "blocking" || issue.severity === "P0" || issue.severity === "P1")
|
||||
.map((issue) => formatTextFinding(issue, issue.severity ?? "issue")),
|
||||
...(report.warnings ?? []).map((finding) => formatTextFinding(finding, "warning")),
|
||||
].slice(0, limit);
|
||||
}
|
||||
|
||||
function formatTextFinding(finding, fallbackLevel) {
|
||||
const level = finding.level ?? finding.severity ?? fallbackLevel;
|
||||
const code = finding.code ? ` ${finding.code}` : "";
|
||||
const message = finding.message ?? finding.title ?? "see report";
|
||||
const evidence = Array.isArray(finding.evidence) && finding.evidence.length > 0 ? ` (${finding.evidence[0]})` : "";
|
||||
return `${String(level).toUpperCase()} ${finding.fixture ?? "unknown"}${code}: ${message}${evidence}`;
|
||||
}
|
||||
|
||||
function artifactLabel(name) {
|
||||
return String(name).replace(/Path$/u, "");
|
||||
}
|
||||
|
||||
export function renderMarkdownReport(report) {
|
||||
|
||||
@ -18,6 +18,7 @@ import {
|
||||
renderJunitXml,
|
||||
renderMarkdownReport,
|
||||
renderMarkdownTable,
|
||||
renderTextSummary,
|
||||
writeArtifacts,
|
||||
writeCiOutputArtifacts,
|
||||
writeReport,
|
||||
@ -32,6 +33,53 @@ test("markdown report includes summary and inventory", async () => {
|
||||
assert.match(markdown, /\| sample-plugin \| high \| native-tool \| before_tool_call \| definePluginEntry, registerTool \| tools \|/);
|
||||
});
|
||||
|
||||
test("text summary includes artifact paths and top blocking findings", () => {
|
||||
const summary = renderTextSummary(
|
||||
{
|
||||
status: "fail",
|
||||
summary: {
|
||||
fixtureCount: 1,
|
||||
breakageCount: 1,
|
||||
issueCount: 1,
|
||||
logCount: 0,
|
||||
},
|
||||
breakages: [
|
||||
{
|
||||
fixture: "weather",
|
||||
code: "missing-expected-seam",
|
||||
level: "breakage",
|
||||
message: "weather: missing expected registration registerTool",
|
||||
evidence: ["src/index.js:12"],
|
||||
},
|
||||
],
|
||||
warnings: [],
|
||||
issues: [
|
||||
{
|
||||
fixture: "weather",
|
||||
code: "sdk-export-missing",
|
||||
severity: "P0",
|
||||
status: "blocking",
|
||||
title: "SDK export is missing",
|
||||
evidence: ["src/index.js:1"],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
artifacts: {
|
||||
jsonPath: "reports/plugin-inspector-report.json",
|
||||
markdownPath: "reports/plugin-inspector-report.md",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
assert.match(summary, /Status: FAIL/);
|
||||
assert.match(summary, /Issues: 1/);
|
||||
assert.match(summary, /Reports:/);
|
||||
assert.match(summary, /json: reports\/plugin-inspector-report\.json/);
|
||||
assert.match(summary, /Top findings:/);
|
||||
assert.match(summary, /BREAKAGE weather missing-expected-seam/);
|
||||
});
|
||||
|
||||
test("compatibility report renderer supports issue metadata and evidence links", () => {
|
||||
const report = {
|
||||
generatedAt: "test",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user