mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 19:35:28 -06:00
refactor(qa): remove duplicate report renderer (#113258)
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { renderQaMarkdownReport } from "./report.js";
|
||||
|
||||
describe("renderQaMarkdownReport", () => {
|
||||
it("renders checks, scenarios, timeline, and multiline details", () => {
|
||||
const report = renderQaMarkdownReport({
|
||||
title: "QA Report",
|
||||
startedAt: new Date("2026-01-01T00:00:00.000Z"),
|
||||
finishedAt: new Date("2026-01-01T00:00:02.000Z"),
|
||||
checks: [{ name: "preflight", status: "pass" }],
|
||||
scenarios: [
|
||||
{
|
||||
name: "transport reply",
|
||||
status: "fail",
|
||||
details: "line one\nline two",
|
||||
steps: [{ name: "send", status: "pass", details: "ok" }],
|
||||
},
|
||||
],
|
||||
timeline: ["sent request"],
|
||||
notes: ["kept artifacts"],
|
||||
});
|
||||
|
||||
expect(report).toContain("# QA Report");
|
||||
expect(report).toContain("- Duration ms: 2000");
|
||||
expect(report).toContain("- Passed: 1");
|
||||
expect(report).toContain("- Failed: 1");
|
||||
expect(report).toContain("```text\nline one\nline two\n```");
|
||||
expect(report).toContain("- [x] send");
|
||||
expect(report).toContain("## Timeline");
|
||||
});
|
||||
});
|
||||
@@ -118,35 +118,6 @@ describe("plugin-sdk qa-runtime", () => {
|
||||
expect(module.isQaRuntimeAvailable()).toBe(false);
|
||||
});
|
||||
|
||||
it("renders shared QA markdown reports with multiline details", async () => {
|
||||
const module = await import("./qa-runtime.js");
|
||||
|
||||
const report = module.renderQaMarkdownReport({
|
||||
title: "QA Report",
|
||||
startedAt: new Date("2026-01-01T00:00:00.000Z"),
|
||||
finishedAt: new Date("2026-01-01T00:00:02.000Z"),
|
||||
checks: [{ name: "preflight", status: "pass" }],
|
||||
scenarios: [
|
||||
{
|
||||
name: "transport reply",
|
||||
status: "fail",
|
||||
details: "line one\nline two",
|
||||
steps: [{ name: "send", status: "pass", details: "ok" }],
|
||||
},
|
||||
],
|
||||
timeline: ["sent request"],
|
||||
notes: ["kept artifacts"],
|
||||
});
|
||||
|
||||
expect(report).toContain("# QA Report");
|
||||
expect(report).toContain("- Duration ms: 2000");
|
||||
expect(report).toContain("- Passed: 1");
|
||||
expect(report).toContain("- Failed: 1");
|
||||
expect(report).toContain("```text\nline one\nline two\n```");
|
||||
expect(report).toContain("- [x] send");
|
||||
expect(report).toContain("## Timeline");
|
||||
});
|
||||
|
||||
it("registers shared live transport QA CLI options", async () => {
|
||||
const module = await import("./qa-runtime.js");
|
||||
const run = vi.fn(async () => {});
|
||||
|
||||
@@ -219,21 +219,6 @@ export function createLiveTransportQaCliRegistration(
|
||||
};
|
||||
}
|
||||
|
||||
/** One top-level check row in a rendered QA markdown report. */
|
||||
export type QaReportCheck = {
|
||||
name: string;
|
||||
status: "pass" | "fail" | "skip";
|
||||
details?: string;
|
||||
};
|
||||
|
||||
/** One scenario section in a rendered QA markdown report. */
|
||||
export type QaReportScenario = {
|
||||
name: string;
|
||||
status: "pass" | "fail" | "skip";
|
||||
details?: string;
|
||||
steps?: QaReportCheck[];
|
||||
};
|
||||
|
||||
/** Docker command runner abstraction used by QA Docker helpers and tests. */
|
||||
export type QaDockerRunCommand = (
|
||||
command: string,
|
||||
@@ -254,95 +239,6 @@ export type QaDockerFetchLike = (
|
||||
const DEFAULT_QA_DOCKER_COMMAND_TIMEOUT_MS = 120_000;
|
||||
const DEFAULT_QA_DOCKER_HEALTH_REQUEST_TIMEOUT_MS = 2_000;
|
||||
|
||||
function pushQaReportDetailsBlock(lines: string[], label: string, details: string, indent = "") {
|
||||
if (!details.includes("\n")) {
|
||||
lines.push(`${indent}- ${label}: ${details}`);
|
||||
return;
|
||||
}
|
||||
lines.push(`${indent}- ${label}:`);
|
||||
lines.push("", "```text", details, "```");
|
||||
}
|
||||
|
||||
/** Render checks, scenarios, timeline, and notes into the standard QA markdown report format. */
|
||||
export function renderQaMarkdownReport(params: {
|
||||
title: string;
|
||||
startedAt: Date;
|
||||
finishedAt: Date;
|
||||
checks?: QaReportCheck[];
|
||||
scenarios?: QaReportScenario[];
|
||||
timeline?: string[];
|
||||
notes?: string[];
|
||||
}) {
|
||||
const checks = params.checks ?? [];
|
||||
const scenarios = params.scenarios ?? [];
|
||||
const passCount =
|
||||
checks.filter((check) => check.status === "pass").length +
|
||||
scenarios.filter((scenario) => scenario.status === "pass").length;
|
||||
const failCount =
|
||||
checks.filter((check) => check.status === "fail").length +
|
||||
scenarios.filter((scenario) => scenario.status === "fail").length;
|
||||
|
||||
const lines = [
|
||||
`# ${params.title}`,
|
||||
"",
|
||||
`- Started: ${params.startedAt.toISOString()}`,
|
||||
`- Finished: ${params.finishedAt.toISOString()}`,
|
||||
`- Duration ms: ${params.finishedAt.getTime() - params.startedAt.getTime()}`,
|
||||
`- Passed: ${passCount}`,
|
||||
`- Failed: ${failCount}`,
|
||||
"",
|
||||
];
|
||||
|
||||
if (checks.length > 0) {
|
||||
lines.push("## Checks", "");
|
||||
for (const check of checks) {
|
||||
lines.push(`- [${check.status === "pass" ? "x" : " "}] ${check.name}`);
|
||||
if (check.details) {
|
||||
pushQaReportDetailsBlock(lines, "Details", check.details, " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scenarios.length > 0) {
|
||||
lines.push("", "## Scenarios", "");
|
||||
for (const scenario of scenarios) {
|
||||
lines.push(`### ${scenario.name}`);
|
||||
lines.push("");
|
||||
lines.push(`- Status: ${scenario.status}`);
|
||||
if (scenario.details) {
|
||||
pushQaReportDetailsBlock(lines, "Details", scenario.details);
|
||||
}
|
||||
if (scenario.steps?.length) {
|
||||
lines.push("- Steps:");
|
||||
for (const step of scenario.steps) {
|
||||
lines.push(` - [${step.status === "pass" ? "x" : " "}] ${step.name}`);
|
||||
if (step.details) {
|
||||
pushQaReportDetailsBlock(lines, "Details", step.details, " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
lines.push("");
|
||||
}
|
||||
}
|
||||
|
||||
if (params.timeline && params.timeline.length > 0) {
|
||||
lines.push("## Timeline", "");
|
||||
for (const item of params.timeline) {
|
||||
lines.push(`- ${item}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (params.notes && params.notes.length > 0) {
|
||||
lines.push("", "## Notes", "");
|
||||
for (const note of params.notes) {
|
||||
lines.push(`- ${note}`);
|
||||
}
|
||||
}
|
||||
|
||||
lines.push("");
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
/** Append a formatted live-lane issue while preserving the caller-owned issue list. */
|
||||
export function appendQaLiveLaneIssue(issues: string[], label: string, error: unknown) {
|
||||
issues.push(`${label}: ${formatErrorMessage(error)}`);
|
||||
|
||||
Reference in New Issue
Block a user