diff --git a/extensions/qa-lab/src/lab-server.ts b/extensions/qa-lab/src/lab-server.ts index 7203d1df873..413c539d67c 100644 --- a/extensions/qa-lab/src/lab-server.ts +++ b/extensions/qa-lab/src/lab-server.ts @@ -651,6 +651,7 @@ export async function startQaLabServer(params?: { state, cfg: gateway?.cfg ?? createQaLabConfig(listenUrl), outputPath: params?.outputPath, + repoRoot, }); latestScenarioRun = withQaLabRunCounts({ kind: "self-check", @@ -698,7 +699,7 @@ export async function startQaLabServer(params?: { const { runQaSuiteFromRuntime } = await import("./suite-launch.runtime.js"); const result = await runQaSuiteFromRuntime({ lab: labHandle ?? undefined, - outputDir: createQaRunOutputDir(), + outputDir: createQaRunOutputDir(repoRoot), providerMode: selection.providerMode, primaryModel: selection.primaryModel, alternateModel: selection.alternateModel, diff --git a/extensions/qa-lab/src/run-config.test.ts b/extensions/qa-lab/src/run-config.test.ts index 65794a26951..e312c8992fe 100644 --- a/extensions/qa-lab/src/run-config.test.ts +++ b/extensions/qa-lab/src/run-config.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"; import { createDefaultQaRunSelection, createIdleQaRunnerSnapshot, + createQaRunOutputDir, normalizeQaRunSelection, } from "./run-config.js"; @@ -67,4 +68,9 @@ describe("qa run config", () => { ).scenarioIds, ).toEqual(["dm-chat-baseline", "thread-lifecycle"]); }); + + it("anchors generated run output dirs under the provided repo root", () => { + const outputDir = createQaRunOutputDir("/tmp/openclaw-repo"); + expect(outputDir.startsWith("/tmp/openclaw-repo/.artifacts/qa-e2e/lab-")).toBe(true); + }); }); diff --git a/extensions/qa-lab/src/self-check.test.ts b/extensions/qa-lab/src/self-check.test.ts new file mode 100644 index 00000000000..25c17bd879d --- /dev/null +++ b/extensions/qa-lab/src/self-check.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, it } from "vitest"; +import { resolveQaSelfCheckOutputPath } from "./self-check.js"; + +describe("resolveQaSelfCheckOutputPath", () => { + it("keeps explicit output paths untouched", () => { + expect( + resolveQaSelfCheckOutputPath({ + repoRoot: "/tmp/openclaw-repo", + outputPath: "/tmp/custom/self-check.md", + }), + ).toBe("/tmp/custom/self-check.md"); + }); + + it("anchors default self-check reports under the provided repo root", () => { + expect(resolveQaSelfCheckOutputPath({ repoRoot: "/tmp/openclaw-repo" })).toBe( + "/tmp/openclaw-repo/.artifacts/qa-e2e/self-check.md", + ); + }); +}); diff --git a/extensions/qa-lab/src/self-check.ts b/extensions/qa-lab/src/self-check.ts index 472cc35123e..09dc58187ef 100644 --- a/extensions/qa-lab/src/self-check.ts +++ b/extensions/qa-lab/src/self-check.ts @@ -14,10 +14,19 @@ export type QaSelfCheckResult = { scenarioResult: QaScenarioResult; }; +export function resolveQaSelfCheckOutputPath(params?: { outputPath?: string; repoRoot?: string }) { + if (params?.outputPath) { + return params.outputPath; + } + const repoRoot = path.resolve(params?.repoRoot ?? process.cwd()); + return path.join(repoRoot, ".artifacts", "qa-e2e", "self-check.md"); +} + export async function runQaSelfCheckAgainstState(params: { state: QaBusState; cfg: OpenClawConfig; outputPath?: string; + repoRoot?: string; notes?: string[]; }): Promise { const startedAt = new Date(); @@ -64,8 +73,10 @@ export async function runQaSelfCheckAgainstState(params: { ], }); - const outputPath = - params.outputPath ?? path.join(process.cwd(), ".artifacts", "qa-e2e", "self-check.md"); + const outputPath = resolveQaSelfCheckOutputPath({ + outputPath: params.outputPath, + repoRoot: params.repoRoot, + }); await fs.mkdir(path.dirname(outputPath), { recursive: true }); await fs.writeFile(outputPath, report, "utf8"); @@ -77,8 +88,9 @@ export async function runQaSelfCheckAgainstState(params: { }; } -export async function runQaLabSelfCheck(params?: { outputPath?: string }) { +export async function runQaLabSelfCheck(params?: { repoRoot?: string; outputPath?: string }) { const server = await startQaLabServer({ + repoRoot: params?.repoRoot, outputPath: params?.outputPath, }); try {