mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 10:16:44 +00:00
refactor(qa): keep synthetic UX evidence out of scenario catalog (#109315)
* refactor(qa): keep UX evidence producer out of catalog * test(qa): preserve UX producer runner integration
This commit is contained in:
@@ -314,17 +314,17 @@ describe("evidence gallery", () => {
|
||||
suiteDir,
|
||||
"script",
|
||||
...producerRootLeakSegments(repoRoot),
|
||||
"ux-matrix-evidence-dashboard",
|
||||
"ux-matrix-producer",
|
||||
"run-1",
|
||||
);
|
||||
const expectedWebScreenshotNeedle =
|
||||
process.platform === "win32"
|
||||
? ".artifacts/qa-e2e/suite/script/nested"
|
||||
: ".artifacts/qa-e2e/suite/script/nested<repo-root>/ux-matrix-evidence-dashboard/run-1/surfaces/web-ui/stages/first-run/screenshot.png";
|
||||
: ".artifacts/qa-e2e/suite/script/nested<repo-root>/ux-matrix-producer/run-1/surfaces/web-ui/stages/first-run/screenshot.png";
|
||||
const expectedCliLogNeedle =
|
||||
process.platform === "win32"
|
||||
? ".artifacts/qa-e2e/suite/script/nested"
|
||||
: ".artifacts/qa-e2e/suite/script/nested<repo-root>/ux-matrix-evidence-dashboard/run-1/surfaces/cli/stages/error-state/logs.txt";
|
||||
: ".artifacts/qa-e2e/suite/script/nested<repo-root>/ux-matrix-producer/run-1/surfaces/cli/stages/error-state/logs.txt";
|
||||
await fs.mkdir(path.join(runDir, "surfaces", "web-ui", "stages", "first-run"), {
|
||||
recursive: true,
|
||||
});
|
||||
@@ -365,10 +365,10 @@ describe("evidence gallery", () => {
|
||||
cells: [
|
||||
null,
|
||||
{
|
||||
coverageIds: [`${repoRoot}/ui.control`],
|
||||
coverageIds: [],
|
||||
runner: {
|
||||
availability: "local",
|
||||
command: `${repoRoot}/openclaw.mjs qa suite --scenario ux-matrix-evidence-dashboard`,
|
||||
command: `node --import tsx ${repoRoot}/scripts/qa/ux-matrix-evidence-producer.ts --artifact-base ${runDir}`,
|
||||
lane: "web-ui-playwright",
|
||||
workflow: `${repoRoot}/.github/workflows/ux-matrix-qa.yml#ux-matrix-local`,
|
||||
},
|
||||
@@ -377,10 +377,11 @@ describe("evidence gallery", () => {
|
||||
surface: "web-ui",
|
||||
},
|
||||
{
|
||||
coverageIds: ["cli.entrypoint"],
|
||||
coverageIds: [],
|
||||
runner: {
|
||||
availability: "local",
|
||||
command: "pnpm openclaw qa suite --scenario ux-matrix-evidence-dashboard",
|
||||
command:
|
||||
"node --import tsx scripts/qa/ux-matrix-evidence-producer.ts --artifact-base .artifacts/qa-e2e/ux-matrix",
|
||||
lane: "cli-status",
|
||||
workflow: ".github/workflows/ux-matrix-qa.yml#ux-matrix-local",
|
||||
},
|
||||
@@ -399,7 +400,11 @@ describe("evidence gallery", () => {
|
||||
},
|
||||
});
|
||||
await fs.writeFile(path.join(runDir, "scorecard.md"), "# UX Matrix\n\n- pass: 1\n", "utf8");
|
||||
await fs.writeFile(path.join(runDir, "commands.txt"), "node ux matrix\n", "utf8");
|
||||
await fs.writeFile(
|
||||
path.join(runDir, "commands.txt"),
|
||||
"node --import tsx scripts/qa/ux-matrix-evidence-producer.ts --artifact-base .artifacts/qa-e2e/ux-matrix\n",
|
||||
"utf8",
|
||||
);
|
||||
await fs.mkdir(path.join(runDir, "preflight"), { recursive: true });
|
||||
await fs.writeFile(path.join(runDir, "preflight", "memory.txt"), "memory ok\n", "utf8");
|
||||
await fs.writeFile(
|
||||
@@ -419,9 +424,9 @@ describe("evidence gallery", () => {
|
||||
kind: "ux-matrix-cell",
|
||||
id: "ux-matrix.web-ui.first-run",
|
||||
title: `UX Matrix: web-ui / first-run at ${repoRoot}`,
|
||||
source: { path: "scripts/ux-matrix/dashboard.ts" },
|
||||
source: { path: "scripts/qa/ux-matrix-evidence-producer.ts" },
|
||||
},
|
||||
coverage: [{ id: "ui.control", role: "primary" }],
|
||||
coverage: [],
|
||||
execution: {
|
||||
runner: "ux-matrix-dashboard",
|
||||
environment: {
|
||||
@@ -458,9 +463,9 @@ describe("evidence gallery", () => {
|
||||
kind: "ux-matrix-cell",
|
||||
id: "qa-lab.wrapper-cli-error",
|
||||
title: "UX Matrix: cli / error-state",
|
||||
source: { path: "scripts/ux-matrix/dashboard.ts" },
|
||||
source: { path: "scripts/qa/ux-matrix-evidence-producer.ts" },
|
||||
},
|
||||
coverage: [{ id: "cli.status-snapshots", role: "primary" }],
|
||||
coverage: [],
|
||||
execution: {
|
||||
runner: "ux-matrix-dashboard",
|
||||
environment: {
|
||||
@@ -530,10 +535,11 @@ describe("evidence gallery", () => {
|
||||
{
|
||||
artifactKinds: ["screenshot"],
|
||||
artifactPaths: [expect.stringContaining(expectedWebScreenshotNeedle)],
|
||||
coverageIds: ["<repo-root>/ui.control"],
|
||||
coverageIds: [],
|
||||
runner: {
|
||||
availability: "local",
|
||||
command: "<repo-root>/openclaw.mjs qa suite --scenario ux-matrix-evidence-dashboard",
|
||||
command:
|
||||
"node --import tsx <repo-root>/scripts/qa/ux-matrix-evidence-producer.ts --artifact-base <repo-root>/.artifacts/qa-e2e/suite/script/nested<repo-root>/ux-matrix-producer/run-1",
|
||||
lane: "web-ui-playwright",
|
||||
workflow: "<repo-root>/.github/workflows/ux-matrix-qa.yml#ux-matrix-local",
|
||||
},
|
||||
@@ -546,10 +552,11 @@ describe("evidence gallery", () => {
|
||||
{
|
||||
artifactKinds: [],
|
||||
artifactPaths: [],
|
||||
coverageIds: ["cli.entrypoint"],
|
||||
coverageIds: [],
|
||||
runner: {
|
||||
availability: "local",
|
||||
command: "pnpm openclaw qa suite --scenario ux-matrix-evidence-dashboard",
|
||||
command:
|
||||
"node --import tsx scripts/qa/ux-matrix-evidence-producer.ts --artifact-base .artifacts/qa-e2e/ux-matrix",
|
||||
lane: "cli-status",
|
||||
workflow: ".github/workflows/ux-matrix-qa.yml#ux-matrix-local",
|
||||
},
|
||||
@@ -576,7 +583,9 @@ describe("evidence gallery", () => {
|
||||
expect(decodeURIComponent(model.producerContext?.scorecard?.href ?? "")).not.toContain(
|
||||
repoRoot,
|
||||
);
|
||||
expect(model.producerContext?.commands?.preview).toBe("node ux matrix\n");
|
||||
expect(model.producerContext?.commands?.preview).toBe(
|
||||
"node --import tsx scripts/qa/ux-matrix-evidence-producer.ts --artifact-base .artifacts/qa-e2e/ux-matrix\n",
|
||||
);
|
||||
expect(model.producerContext?.commands?.path).toContain("commands.txt");
|
||||
expect(decodeURIComponent(model.producerContext?.commands?.href ?? "")).not.toContain(repoRoot);
|
||||
expect(model.producerContext?.manifest?.preview).toContain('"runId": "run-1"');
|
||||
|
||||
@@ -231,7 +231,6 @@ describe("qa scenario catalog", () => {
|
||||
|
||||
it("loads native test execution scenarios from YAML", () => {
|
||||
const scenario = readQaScenarioById("control-ui-chat-flow-playwright");
|
||||
const uxMatrix = readQaScenarioById("ux-matrix-evidence-dashboard");
|
||||
const otelSmoke = readQaScenarioById("qa-otel-smoke");
|
||||
|
||||
expect(scenario.execution.kind).toBe("playwright");
|
||||
@@ -244,14 +243,6 @@ describe("qa scenario catalog", () => {
|
||||
);
|
||||
expect(scenario.execution.flow).toBeUndefined();
|
||||
expect(scenario.coverage?.primary).toContain("ui.control");
|
||||
expect(uxMatrix.execution.kind).toBe("script");
|
||||
if (uxMatrix.execution.kind !== "script") {
|
||||
throw new Error(`expected script scenario, got ${uxMatrix.execution.kind}`);
|
||||
}
|
||||
expect(uxMatrix.execution.path).toBe("scripts/qa/ux-matrix-evidence-producer.ts");
|
||||
expect(uxMatrix.execution.args).toStrictEqual(["--artifact-base", "${outputDir}"]);
|
||||
expect(uxMatrix.execution.config).toBeUndefined();
|
||||
expect(uxMatrix.coverage?.primary).toContain("qa.artifact-safety");
|
||||
expect(otelSmoke.execution.kind).toBe("script");
|
||||
if (otelSmoke.execution.kind !== "script") {
|
||||
throw new Error(`expected script scenario, got ${otelSmoke.execution.kind}`);
|
||||
|
||||
@@ -4,7 +4,7 @@ import path from "node:path";
|
||||
import { setTimeout as sleep } from "node:timers/promises";
|
||||
import { afterAll, afterEach, beforeAll, describe, expect, it, vi } from "vitest";
|
||||
import { validateQaEvidenceSummaryJson } from "./evidence-summary.js";
|
||||
import { readQaScenarioById, type QaSeedScenarioWithSource } from "./scenario-catalog.js";
|
||||
import type { QaSeedScenarioWithSource } from "./scenario-catalog.js";
|
||||
import { createTempDirHarness } from "./temp-dir.test-helper.js";
|
||||
import {
|
||||
qaTestFileScenarioRunnerTesting,
|
||||
@@ -1081,72 +1081,58 @@ describe("qa test file scenario runner", () => {
|
||||
expect(artifactPath?.includes("..")).toBe(false);
|
||||
});
|
||||
|
||||
describe("UX Matrix scenario composition", () => {
|
||||
let outputDir: string;
|
||||
let result: Awaited<ReturnType<typeof runQaTestFileScenarios>>;
|
||||
let evidence: ReturnType<typeof validateQaEvidenceSummaryJson>;
|
||||
it("imports the standalone UX Matrix producer as coverage-free infrastructure", async () => {
|
||||
const outputDir = await fs.mkdtemp(path.join(os.tmpdir(), "qa-ux-matrix-producer-"));
|
||||
tempRoots.push(outputDir);
|
||||
// The runner accepts scenario-shaped execution input, but this test fixture is not cataloged
|
||||
// and deliberately carries no product taxonomy coverage.
|
||||
const infrastructureFixture: QaSeedScenarioWithSource = {
|
||||
id: "scenario-script",
|
||||
title: "UX Matrix producer infrastructure fixture",
|
||||
surface: "qa-lab",
|
||||
objective: "Exercise the standalone UX Matrix evidence producer through the script runner.",
|
||||
successCriteria: ["The runner imports the producer's structured evidence bundle."],
|
||||
codeRefs: ["scripts/qa/ux-matrix-evidence-producer.ts"],
|
||||
sourcePath: "test/scripts/qa-ux-matrix-evidence-producer.test.ts",
|
||||
execution: {
|
||||
kind: "script",
|
||||
path: "scripts/qa/ux-matrix-evidence-producer.ts",
|
||||
allowBlockedEvidence: true,
|
||||
args: ["--artifact-base", "${outputDir}", "--skip-visual-proof"],
|
||||
},
|
||||
};
|
||||
|
||||
beforeAll(async () => {
|
||||
outputDir = await fs.mkdtemp(path.join(os.tmpdir(), "qa-ux-matrix-script-"));
|
||||
tempRoots.push(outputDir);
|
||||
const scenario = readQaScenarioById("ux-matrix-evidence-dashboard");
|
||||
|
||||
expect(scenario.execution.kind).toBe("script");
|
||||
result = await runQaTestFileScenarios({
|
||||
repoRoot: process.cwd(),
|
||||
outputDir,
|
||||
providerMode: "mock-openai",
|
||||
primaryModel: "mock-openai/gpt-5.6-luna",
|
||||
scenarios: [scenario],
|
||||
env: {
|
||||
OPENCLAW_QA_REF: "scenario-ref",
|
||||
} as NodeJS.ProcessEnv,
|
||||
});
|
||||
evidence = validateQaEvidenceSummaryJson(
|
||||
JSON.parse(await fs.readFile(result.evidencePath, "utf8")),
|
||||
);
|
||||
const result = await runQaTestFileScenarios({
|
||||
repoRoot: process.cwd(),
|
||||
outputDir,
|
||||
providerMode: "mock-openai",
|
||||
primaryModel: "mock-openai/gpt-5.6-luna",
|
||||
scenarios: [infrastructureFixture],
|
||||
env: { OPENCLAW_QA_REF: "infrastructure-fixture" } as NodeJS.ProcessEnv,
|
||||
});
|
||||
const evidence = validateQaEvidenceSummaryJson(
|
||||
JSON.parse(await fs.readFile(result.evidencePath, "utf8")),
|
||||
);
|
||||
|
||||
it("runs the checked-in producer and imports its evidence bundle", () => {
|
||||
expect(result.executionKind).toBe("script");
|
||||
expect(result.results[0]?.producerEvidence?.entries).toHaveLength(3);
|
||||
expect(evidence.entries.map((entry) => entry.test.id)).toEqual([
|
||||
"ux-matrix.qa-lab.producer-artifact-fixture",
|
||||
"ux-matrix.control-ui.screenshot-artifact",
|
||||
"ux-matrix.cli.entrypoint-help",
|
||||
]);
|
||||
expect(
|
||||
evidence.entries.flatMap((entry) => entry.coverage.map((coverage) => coverage.id)),
|
||||
).toEqual(
|
||||
expect.arrayContaining([
|
||||
"qa.artifact-safety",
|
||||
"tools.evidence",
|
||||
"workspace.artifacts",
|
||||
"ui.control",
|
||||
"gateway.control-ui-hosting",
|
||||
"cli.entrypoint",
|
||||
"cli.status-snapshots",
|
||||
]),
|
||||
);
|
||||
const artifactKinds = evidence.entries.flatMap(
|
||||
expect(result.executionKind).toBe("script");
|
||||
expect(result.results[0]).toMatchObject({ status: "pass" });
|
||||
expect(result.results[0]?.producerEvidence?.entries).toHaveLength(3);
|
||||
expect(evidence.entries.map((entry) => entry.test.id)).toEqual([
|
||||
"ux-matrix.qa-lab.producer-artifact-fixture",
|
||||
"ux-matrix.control-ui.screenshot-artifact",
|
||||
"ux-matrix.cli.entrypoint-help",
|
||||
]);
|
||||
expect(evidence.entries.every((entry) => entry.coverage.length === 0)).toBe(true);
|
||||
expect(
|
||||
evidence.entries.flatMap(
|
||||
(entry) => entry.execution?.artifacts.map((artifact) => artifact.kind) ?? [],
|
||||
);
|
||||
expect(artifactKinds).toEqual(expect.arrayContaining(["html", "log"]));
|
||||
const fixtureEntry = evidence.entries.find(
|
||||
(entry) => entry.test.id === "ux-matrix.qa-lab.producer-artifact-fixture",
|
||||
);
|
||||
expect(fixtureEntry?.execution?.artifacts.map((artifact) => artifact.path)).toContain(
|
||||
path.join(
|
||||
outputDir,
|
||||
"ux-matrix-evidence-dashboard",
|
||||
"surfaces",
|
||||
"qa-lab",
|
||||
"stages",
|
||||
"producer-artifact-fixture",
|
||||
"producer-artifact-fixture.html",
|
||||
),
|
||||
);
|
||||
});
|
||||
),
|
||||
).toEqual(expect.arrayContaining(["html", "log"]));
|
||||
expect(
|
||||
evidence.entries
|
||||
.flatMap((entry) => entry.execution?.artifacts.map((artifact) => artifact.path) ?? [])
|
||||
.some((artifactPath) => artifactPath.includes(path.join(outputDir, "scenario-script"))),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
/* oxlint-disable max-lines -- TODO: split this grandfathered oversized file. */
|
||||
|
||||
@@ -252,7 +252,7 @@ describe("QA Lab UI evidence render", () => {
|
||||
source: "ux-matrix:web-ui:first-run",
|
||||
},
|
||||
],
|
||||
coverage: [{ id: "ui.control", role: "primary" }],
|
||||
coverage: [],
|
||||
failureReason: null,
|
||||
id: "ux-matrix.web-ui.first-run",
|
||||
kind: "ux-matrix-cell",
|
||||
@@ -279,10 +279,11 @@ describe("QA Lab UI evidence render", () => {
|
||||
{
|
||||
artifactKinds: ["screenshot"],
|
||||
artifactPaths: ["screenshot.png"],
|
||||
coverageIds: ["ui.control"],
|
||||
coverageIds: [],
|
||||
runner: {
|
||||
availability: "local",
|
||||
command: "pnpm openclaw qa suite --scenario ux-matrix-evidence-dashboard",
|
||||
command:
|
||||
"node --import tsx scripts/qa/ux-matrix-evidence-producer.ts --artifact-base .artifacts/qa-e2e/ux-matrix",
|
||||
lane: "web-ui-playwright",
|
||||
workflow: ".github/workflows/ux-matrix-qa.yml#ux-matrix-local",
|
||||
},
|
||||
@@ -295,10 +296,11 @@ describe("QA Lab UI evidence render", () => {
|
||||
{
|
||||
artifactKinds: [],
|
||||
artifactPaths: [],
|
||||
coverageIds: ["cli.entrypoint"],
|
||||
coverageIds: [],
|
||||
runner: {
|
||||
availability: "local",
|
||||
command: "pnpm openclaw qa suite --scenario ux-matrix-evidence-dashboard",
|
||||
command:
|
||||
"node --import tsx scripts/qa/ux-matrix-evidence-producer.ts --artifact-base .artifacts/qa-e2e/ux-matrix",
|
||||
lane: "cli-status",
|
||||
workflow: ".github/workflows/ux-matrix-qa.yml#ux-matrix-local",
|
||||
},
|
||||
@@ -316,7 +318,7 @@ describe("QA Lab UI evidence render", () => {
|
||||
},
|
||||
preflight: { adbDevices: null, memory: null },
|
||||
releaseLedger: null,
|
||||
rootPath: ".artifacts/qa-e2e/suite/script/ux-matrix-evidence-dashboard/run-1",
|
||||
rootPath: ".artifacts/qa-e2e/suite/script/ux-matrix-producer/run-1",
|
||||
scorecard: null,
|
||||
},
|
||||
profile: null,
|
||||
@@ -329,7 +331,7 @@ describe("QA Lab UI evidence render", () => {
|
||||
expect(html).toContain('data-evidence-entry-id="ux-matrix.web-ui.first-run"');
|
||||
expect(html).toContain("evidence-matrix-cell-proof-gap");
|
||||
expect(html).toContain("not executed in this run");
|
||||
expect(html).toContain("Coverage: cli.entrypoint");
|
||||
expect(html).not.toContain("Coverage:");
|
||||
expect(html).toContain("Runner: cli-status");
|
||||
expect(html).toContain("Open media artifact");
|
||||
expect(html).toContain("Open video artifact");
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
title: UX Matrix evidence dashboard
|
||||
|
||||
scenario:
|
||||
id: ux-matrix-evidence-dashboard
|
||||
surface: control-ui
|
||||
category: browser-control-ui-and-webchat.browser-ui
|
||||
coverage:
|
||||
primary:
|
||||
- qa.artifact-safety
|
||||
secondary:
|
||||
- ui.control
|
||||
- gateway.control-ui-hosting
|
||||
- cli.entrypoint
|
||||
- cli.status-snapshots
|
||||
- tools.evidence
|
||||
- workspace.artifacts
|
||||
objective: Produce UX Matrix evidence artifacts through the QA Lab script producer contract.
|
||||
successCriteria:
|
||||
- The script producer writes manifest.json, matrix.json, release-ledger.json, scorecard.md, latest-run.json, and qa-evidence.json.
|
||||
- Evidence entries declare coverage IDs and artifact paths for generated logs, screenshot proof, and video-style proof.
|
||||
- Missing local browser or CLI prerequisites are represented as blocked evidence with logs instead of fake artifacts.
|
||||
docsRefs:
|
||||
- docs/concepts/qa-e2e-automation.md
|
||||
- docs/web/control-ui.md
|
||||
codeRefs:
|
||||
- scripts/qa/ux-matrix-evidence-producer.ts
|
||||
- extensions/qa-lab/src/test-file-scenario-runner.ts
|
||||
execution:
|
||||
kind: script
|
||||
path: scripts/qa/ux-matrix-evidence-producer.ts
|
||||
summary: Script-backed UX Matrix producer that emits QA Lab evidence artifacts.
|
||||
allowBlockedEvidence: true
|
||||
args:
|
||||
- --artifact-base
|
||||
- ${outputDir}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Produces a QA Lab UX Matrix evidence bundle through the script scenario contract.
|
||||
// Produces standalone QA Lab UX Matrix fixture artifacts for gallery and evidence tests.
|
||||
import { execFile } from "node:child_process";
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
@@ -16,16 +16,13 @@ import {
|
||||
type QaEvidenceSummaryEntry,
|
||||
type QaEvidenceSummaryJson,
|
||||
} from "../../extensions/qa-lab/src/evidence-summary.js";
|
||||
import { readQaScenarioById } from "../../extensions/qa-lab/src/scenario-catalog.js";
|
||||
import {
|
||||
ensurePlaywrightChromium,
|
||||
resolveSystemChromiumExecutablePath,
|
||||
} from "../ensure-playwright-chromium.mjs";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
const SCENARIO_ID = "ux-matrix-evidence-dashboard";
|
||||
const SOURCE_PATH = "scripts/qa/ux-matrix-evidence-producer.ts";
|
||||
const SUITE_COMMAND = `pnpm openclaw qa suite --scenario ${SCENARIO_ID}`;
|
||||
|
||||
type MatrixCell = {
|
||||
artifacts: Array<{ kind: string; path: string }>;
|
||||
@@ -227,21 +224,7 @@ function buildExecution(params: {
|
||||
};
|
||||
}
|
||||
|
||||
function readCanonicalScenarioCoverage(): QaEvidenceSummaryEntry["coverage"] {
|
||||
// Matrix cells are artifacts of one catalog scenario, not independent scenarios.
|
||||
// Evidence therefore uses that scenario's mapping instead of cell-local coverage IDs.
|
||||
const coverage = readQaScenarioById(SCENARIO_ID).coverage;
|
||||
return [
|
||||
...(coverage?.primary ?? []).map((id) => ({ id, role: "primary" as const })),
|
||||
...(coverage?.secondary ?? []).map((id) => ({ id, role: "secondary" as const })),
|
||||
];
|
||||
}
|
||||
|
||||
function buildEvidenceEntry(
|
||||
cell: MatrixCell,
|
||||
repoRoot: string,
|
||||
coverage: QaEvidenceSummaryEntry["coverage"],
|
||||
): QaEvidenceSummaryEntry {
|
||||
function buildEvidenceEntry(cell: MatrixCell, repoRoot: string): QaEvidenceSummaryEntry {
|
||||
const source = `ux-matrix:${cell.surface}:${cell.stage}`;
|
||||
return {
|
||||
test: {
|
||||
@@ -250,7 +233,8 @@ function buildEvidenceEntry(
|
||||
title: cell.title,
|
||||
source: { path: SOURCE_PATH },
|
||||
},
|
||||
coverage,
|
||||
// These entries prove the evidence/gallery infrastructure, not product taxonomy behavior.
|
||||
coverage: [],
|
||||
refs: [
|
||||
{ kind: "code", path: SOURCE_PATH },
|
||||
{ kind: "docs", path: "docs/concepts/qa-e2e-automation.md" },
|
||||
@@ -282,13 +266,12 @@ function buildEvidenceSummary(params: {
|
||||
generatedAt: string;
|
||||
repoRoot: string;
|
||||
}): QaEvidenceSummaryJson {
|
||||
const coverage = readCanonicalScenarioCoverage();
|
||||
return validateQaEvidenceSummaryJson({
|
||||
kind: QA_EVIDENCE_SUMMARY_KIND,
|
||||
schemaVersion: QA_EVIDENCE_SUMMARY_SCHEMA_VERSION,
|
||||
generatedAt: params.generatedAt,
|
||||
evidenceMode: "full",
|
||||
entries: params.cells.map((cell) => buildEvidenceEntry(cell, params.repoRoot, coverage)),
|
||||
entries: params.cells.map((cell) => buildEvidenceEntry(cell, params.repoRoot)),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -500,9 +483,9 @@ async function writeProducerArtifactFixtureHtml(params: {
|
||||
</style>
|
||||
<main>
|
||||
<h1>UX Matrix Producer Artifact Fixture</h1>
|
||||
<p class="meta">Script-produced ${escapeHtml(QA_EVIDENCE_FILENAME)} for ${escapeHtml(
|
||||
SCENARIO_ID,
|
||||
)}; this fixture is not the QA Lab Evidence Archive UI.</p>
|
||||
<p class="meta">Standalone script-produced ${escapeHtml(
|
||||
QA_EVIDENCE_FILENAME,
|
||||
)}; this fixture is not the QA Lab Evidence Archive UI or product-behavior proof.</p>
|
||||
<section class="panel">
|
||||
<h2>UX Matrix entries</h2>
|
||||
<ul>${entryRows}</ul>
|
||||
@@ -612,7 +595,6 @@ async function writeProducerMetadata(params: {
|
||||
cells: readonly MatrixCell[];
|
||||
repoRoot: string;
|
||||
}) {
|
||||
const coverageIds = readCanonicalScenarioCoverage().map((coverage) => coverage.id);
|
||||
const counts = params.cells.reduce<Record<string, number>>((acc, cell) => {
|
||||
acc[cell.status] = (acc[cell.status] ?? 0) + 1;
|
||||
return acc;
|
||||
@@ -620,13 +602,12 @@ async function writeProducerMetadata(params: {
|
||||
await writeJson(path.join(params.artifactBase, "manifest.json"), {
|
||||
kind: "openclaw.qa.ux-matrix",
|
||||
run: {
|
||||
scenarioId: SCENARIO_ID,
|
||||
status: counts.fail ? "fail" : counts.blocked ? "blocked" : "pass",
|
||||
},
|
||||
});
|
||||
await writeJson(path.join(params.artifactBase, "matrix.json"), {
|
||||
cells: params.cells.map((cell) => ({
|
||||
coverageIds,
|
||||
coverageIds: [],
|
||||
stage: cell.stage,
|
||||
status: cell.status,
|
||||
surface: cell.surface,
|
||||
@@ -635,7 +616,7 @@ async function writeProducerMetadata(params: {
|
||||
});
|
||||
await writeJson(path.join(params.artifactBase, "release-ledger.json"), {
|
||||
entries: params.cells.map((cell) => ({
|
||||
coverageIds,
|
||||
coverageIds: [],
|
||||
stage: cell.stage,
|
||||
status: cell.status,
|
||||
surface: cell.surface,
|
||||
@@ -644,13 +625,10 @@ async function writeProducerMetadata(params: {
|
||||
});
|
||||
await writeText(
|
||||
path.join(params.artifactBase, "commands.txt"),
|
||||
[
|
||||
`${SUITE_COMMAND} --output-dir ${toRepoRelativePath(params.repoRoot, params.artifactBase)}`,
|
||||
`node --import tsx ${SOURCE_PATH} --artifact-base ${toRepoRelativePath(
|
||||
params.repoRoot,
|
||||
params.artifactBase,
|
||||
)}`,
|
||||
].join("\n") + "\n",
|
||||
`node --import tsx ${SOURCE_PATH} --artifact-base ${toRepoRelativePath(
|
||||
params.repoRoot,
|
||||
params.artifactBase,
|
||||
)}\n`,
|
||||
);
|
||||
await writeText(
|
||||
path.join(params.artifactBase, "scorecard.md"),
|
||||
|
||||
@@ -139,6 +139,43 @@ describe("QA UX Matrix evidence producer CLI", () => {
|
||||
expect(cliLog).not.toContain(fakeRepoRoot);
|
||||
expect(`${evidence}\n${cliLog}`).toContain("<repo-root>");
|
||||
expect(visualLog).toBe("blocked: --skip-visual-proof was set\n");
|
||||
|
||||
const evidenceJson = JSON.parse(evidence) as {
|
||||
entries: Array<{
|
||||
coverage: unknown[];
|
||||
execution?: { artifacts: Array<{ kind: string }> };
|
||||
test: { id: string };
|
||||
}>;
|
||||
};
|
||||
const matrix = JSON.parse(
|
||||
fs.readFileSync(path.join(artifactBase, "matrix.json"), "utf8"),
|
||||
) as { cells: Array<{ coverageIds: unknown[] }> };
|
||||
const releaseLedger = JSON.parse(
|
||||
fs.readFileSync(path.join(artifactBase, "release-ledger.json"), "utf8"),
|
||||
) as { entries: Array<{ coverageIds: unknown[] }> };
|
||||
const manifest = JSON.parse(
|
||||
fs.readFileSync(path.join(artifactBase, "manifest.json"), "utf8"),
|
||||
) as { run: Record<string, unknown> };
|
||||
const commands = fs.readFileSync(path.join(artifactBase, "commands.txt"), "utf8");
|
||||
|
||||
expect(evidenceJson.entries.map((entry) => entry.test.id)).toEqual([
|
||||
"ux-matrix.qa-lab.producer-artifact-fixture",
|
||||
"ux-matrix.control-ui.screenshot-artifact",
|
||||
"ux-matrix.cli.entrypoint-help",
|
||||
]);
|
||||
expect(evidenceJson.entries.every((entry) => entry.coverage.length === 0)).toBe(true);
|
||||
expect(matrix.cells.every((cell) => cell.coverageIds.length === 0)).toBe(true);
|
||||
expect(releaseLedger.entries.every((entry) => entry.coverageIds.length === 0)).toBe(true);
|
||||
expect(manifest.run).not.toHaveProperty("scenarioId");
|
||||
expect(commands).toContain(
|
||||
"node --import tsx scripts/qa/ux-matrix-evidence-producer.ts --artifact-base",
|
||||
);
|
||||
expect(commands).not.toContain("qa suite --scenario");
|
||||
expect(
|
||||
evidenceJson.entries.flatMap(
|
||||
(entry) => entry.execution?.artifacts.map((artifact) => artifact.kind) ?? [],
|
||||
),
|
||||
).toEqual(expect.arrayContaining(["html", "log"]));
|
||||
} finally {
|
||||
fs.rmSync(artifactBase, { recursive: true, force: true });
|
||||
fs.rmSync(fakeRepoRoot, { recursive: true, force: true });
|
||||
|
||||
Reference in New Issue
Block a user