


























@@ -0,0 +1,98 @@
1+import fs from "node:fs/promises";
2+import os from "node:os";
3+import path from "node:path";
4+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5+import { runMantisBeforeAfter } from "./run.runtime.js";
6+7+describe("mantis before/after runtime", () => {
8+let repoRoot: string;
9+10+beforeEach(async () => {
11+repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), "mantis-before-after-"));
12+});
13+14+afterEach(async () => {
15+await fs.rm(repoRoot, { force: true, recursive: true });
16+});
17+18+it("runs baseline and candidate worktrees and writes stable comparison artifacts", async () => {
19+const commands: { args: readonly string[]; command: string; cwd?: string }[] = [];
20+const runner = vi.fn(async (command: string, args: readonly string[]) => {
21+commands.push({ command, args });
22+if (command !== "pnpm" || !args.includes("openclaw")) {
23+return;
24+}
25+const repoRootArg = args[args.indexOf("--repo-root") + 1];
26+const outputDirArg = args[args.indexOf("--output-dir") + 1];
27+const lane = outputDirArg.endsWith("baseline") ? "baseline" : "candidate";
28+const outputDir = path.join(repoRootArg, outputDirArg);
29+await fs.mkdir(outputDir, { recursive: true });
30+const screenshotPath = path.join(outputDir, `${lane}-timeline.png`);
31+await fs.writeFile(screenshotPath, `${lane} screenshot`);
32+await fs.writeFile(
33+path.join(outputDir, "discord-qa-summary.json"),
34+`${JSON.stringify(
35+ {
36+ scenarios: [
37+ {
38+ artifactPaths: { screenshot: screenshotPath },
39+ details:
40+ lane === "baseline"
41+ ? "reaction timeline missing thinking/done"
42+ : "reaction timeline matched queued -> thinking -> done",
43+ id: "discord-status-reactions-tool-only",
44+ status: lane === "baseline" ? "fail" : "pass",
45+ },
46+ ],
47+ },
48+ null,
49+ 2,
50+ )}\n`,
51+);
52+});
53+54+const result = await runMantisBeforeAfter({
55+baseline: "bug-sha",
56+candidate: "fix-sha",
57+commandRunner: runner,
58+now: () => new Date("2026-05-03T12:00:00.000Z"),
59+outputDir: ".artifacts/qa-e2e/mantis/test-run",
60+ repoRoot,
61+skipBuild: true,
62+skipInstall: true,
63+});
64+65+expect(result.status).toBe("pass");
66+expect(
67+commands.map((entry) => [
68+entry.command,
69+entry.args[0],
70+entry.args[1],
71+entry.args[2],
72+entry.args[3],
73+]),
74+).toEqual([
75+["git", "worktree", "add", "--detach", expect.stringContaining("baseline")],
76+["pnpm", "--dir", expect.stringContaining("baseline"), "openclaw", "qa"],
77+["git", "worktree", "add", "--detach", expect.stringContaining("candidate")],
78+["pnpm", "--dir", expect.stringContaining("candidate"), "openclaw", "qa"],
79+]);
80+81+const comparison = JSON.parse(await fs.readFile(result.comparisonPath, "utf8")) as {
82+baseline: { reproduced: boolean; status: string };
83+candidate: { fixed: boolean; status: string };
84+pass: boolean;
85+};
86+expect(comparison).toMatchObject({
87+baseline: { reproduced: true, status: "fail" },
88+candidate: { fixed: true, status: "pass" },
89+pass: true,
90+});
91+await expect(
92+fs.readFile(path.join(result.outputDir, "baseline", "baseline.png"), "utf8"),
93+).resolves.toBe("baseline screenshot");
94+await expect(
95+fs.readFile(path.join(result.outputDir, "candidate", "candidate.png"), "utf8"),
96+).resolves.toBe("candidate screenshot");
97+});
98+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。