

















@@ -83,6 +83,7 @@ import {
8383runQaJsonlReplayCommand,
8484runQaManualLaneCommand,
8585runQaParityReportCommand,
86+runQaProfileCommand,
8687runQaSuiteCommand,
8788} from "./cli.runtime.js";
8889import { QaSuiteInfraError } from "./errors.js";
@@ -139,21 +140,22 @@ function flowSuiteRuntimeResult(params: {
139140};
140141}
141142142-function testFileSuiteRuntimeResult(params: {
143+function unifiedSuiteRuntimeResult(params: {
143144evidencePath: string;
144-executionKind?: "vitest" | "playwright";
145145outputDir: string;
146146reportPath: string;
147-results?: unknown[];
147+summaryPath: string;
148+scenarios?: unknown[];
148149}) {
149150return {
150-executionKind: params.executionKind ?? "playwright",
151+executionKind: "suite",
151152result: {
152153outputDir: params.outputDir,
153-executionKind: params.executionKind ?? "playwright",
154154reportPath: params.reportPath,
155155evidencePath: params.evidencePath,
156-results: params.results ?? [{ status: "pass" }],
156+summaryPath: params.summaryPath,
157+report: "# QA Suite Report\n",
158+scenarios: params.scenarios ?? [],
157159},
158160};
159161}
@@ -301,9 +303,10 @@ describe("qa cli runtime", () => {
301303const evidencePath = path.join(suiteArtifactsDir, "qa-evidence.json");
302304await fs.writeFile(evidencePath, JSON.stringify({ entries: [] }), "utf8");
303305runQaSuite.mockResolvedValueOnce(
304-testFileSuiteRuntimeResult({
306+unifiedSuiteRuntimeResult({
305307outputDir: suiteArtifactsDir,
306308reportPath: suiteReportPath,
309+summaryPath: suiteSummaryPath,
307310 evidencePath,
308311}),
309312);
@@ -325,6 +328,7 @@ describe("qa cli runtime", () => {
325328scenarioIds: ["control-ui-chat-flow-playwright"],
326329});
327330expectWriteContains(stdoutWrite, `QA suite evidence: ${evidencePath}`);
331+expectWriteContains(stdoutWrite, `QA suite summary: ${suiteSummaryPath}`);
328332});
329333330334it("rejects host-only resource options for Playwright scenarios", async () => {
@@ -339,6 +343,75 @@ describe("qa cli runtime", () => {
339343expect(runQaSuite).not.toHaveBeenCalled();
340344});
341345346+it("dispatches a taxonomy-backed profile category through the suite runner", async () => {
347+const previousProfile = process.env.OPENCLAW_QA_PROFILE;
348+process.env.OPENCLAW_QA_PROFILE = "release";
349+try {
350+runQaSuite.mockImplementationOnce(async () => {
351+expect(process.env.OPENCLAW_QA_PROFILE).toBe("smoke-ci");
352+return flowSuiteRuntimeResult({
353+reportPath: suiteReportPath,
354+summaryPath: suiteSummaryPath,
355+});
356+});
357+358+await runQaProfileCommand({
359+repoRoot: "/tmp/openclaw-repo",
360+outputDir: ".artifacts/qa-e2e/smoke-ci",
361+profile: "smoke-ci",
362+surface: "agent-runtime-and-provider-execution",
363+category: "agent-runtime-and-provider-execution.agent-turn-execution",
364+transportId: "qa-channel",
365+fastMode: true,
366+concurrency: 2,
367+allowFailures: true,
368+});
369+370+const suiteArgs = mockFirstObjectArg(runQaSuite);
371+expectFields(suiteArgs, {
372+repoRoot: path.resolve("/tmp/openclaw-repo"),
373+outputDir: path.resolve("/tmp/openclaw-repo", ".artifacts/qa-e2e/smoke-ci"),
374+transportId: "qa-channel",
375+providerMode: "mock-openai",
376+fastMode: true,
377+concurrency: 2,
378+});
379+expect(suiteArgs.scenarioIds).toEqual(expect.arrayContaining(["dm-chat-baseline"]));
380+expect(suiteArgs.scenarioIds).not.toContain("thinking-slash-model-remap");
381+expect(process.env.OPENCLAW_QA_PROFILE).toBe("release");
382+expectWriteContains(stdoutWrite, "QA run profile: smoke-ci; categories: 1; scenarios:");
383+} finally {
384+if (previousProfile === undefined) {
385+delete process.env.OPENCLAW_QA_PROFILE;
386+} else {
387+process.env.OPENCLAW_QA_PROFILE = previousProfile;
388+}
389+}
390+});
391+392+it("rejects qa profile runs that do not match taxonomy categories", async () => {
393+await expect(
394+runQaProfileCommand({
395+repoRoot: "/tmp/openclaw-repo",
396+profile: "smoke-ci",
397+surface: "unknown-surface",
398+}),
399+).rejects.toThrow(
400+"qa run did not find taxonomy categories for --qa-profile smoke-ci --surface unknown-surface.",
401+);
402+expect(runQaSuite).not.toHaveBeenCalled();
403+});
404+405+it("rejects qa profile runs whose profile is not declared in taxonomy.yaml", async () => {
406+await expect(
407+runQaProfileCommand({
408+repoRoot: "/tmp/openclaw-repo",
409+profile: "nightly",
410+}),
411+).rejects.toThrow('--qa-profile must be one of smoke-ci, release, got "nightly".');
412+expect(runQaSuite).not.toHaveBeenCalled();
413+});
414+342415it("resolves suite repo-root-relative paths before dispatching", async () => {
343416await runQaSuiteCommand({
344417repoRoot: "/tmp/openclaw-repo",
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。