muse-agent-example/web/tests/旅程/任务材料.spec.ts
zizi ae02fd1732 前端:工作台页面、事件标签与旅程用例
- 上下文清单、任务列表与详情、恢复控制、运行观察、创作对话与消息流、正式变更决策面板、正文写作与候选审阅、资料研究等页面同步本轮后端能力。
- 事件标签表补齐后端事件类型;查询键与生成客户端随 HTTP 契约重新生成。
- 依赖调整后 lockfile 同步;交互用例与浏览器旅程 spec 一并更新。
- 证据:make 前端检查(lint + typecheck + Pi 工具桥 tsc)exit 0;make 前端测试 37 文件 / 120 用例通过。
2026-09-18 01:15:07 +08:00

105 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/** 真实浏览器和隔离库:任务材料清单、来源证据与漂移后的失效说明可见。 */
import { readFileSync } from "node:fs";
import { expect, test } from "@playwright/test";
test.use({ trace: "off" });
// @muse-case {"case_id":"NC-browser-context-workbench","environment":"真实浏览器","given":"真实隔离库上的工作台与一处真实漂移","when":"真实浏览器登录并打开任务详情","then":["材料清单/来源证据/失效说明可见(DOM 断言 24 条,页面与控制台错误 0);截图视觉判定四项清晰可读"],"contract":"docs/系统架构/新版设计/接口契约/任务工具与事件.md"}
test("NC-browser-context-workbench:任务材料清单、来源证据与失效说明", async ({
page,
}, info) => {
if (!process.env.MUSE_WORKBENCH_URL || !process.env.MUSE_AUTHOR_PASSWORD_FILE)
throw new Error("需要明确的隔离后端和作者口令文件。");
const taskId = process.env.MUSE_CONTEXT_TASK_ID!;
const chapterId = process.env.MUSE_CONTEXT_CHAPTER_ID ?? "ch-1";
if (!process.env.MUSE_CONTEXT_TASK_ID)
throw new Error("需要带冻结快照的任务编号(MUSE_CONTEXT_TASK_ID)。");
const consoleErrors: string[] = [];
page.on("pageerror", (error) => consoleErrors.push(String(error)));
await page.goto("/");
await page
.getByLabel("访问口令")
.fill(readFileSync(process.env.MUSE_AUTHOR_PASSWORD_FILE, "utf8").trim());
await page.getByRole("button", { name: "进入工作台" }).click();
await page.getByRole("link", { name: /任务记录/ }).waitFor();
// 连接前探测会话的 401 与 favicon 404 属正常噪声;只记录进入工作台后的真实错误。
page.on("console", (message) => {
if (message.type() === "error" && !/favicon/.test(message.text()))
consoleErrors.push(message.text());
});
async function 打开任务() {
await page.goto("/tasks");
await page.getByLabel("任务编号").fill(taskId);
await page.getByRole("button", { name: "查看任务" }).click();
await expect(
page.locator('section[aria-label="任务材料"]'),
).toBeVisible();
}
await 打开任务();
const 材料 = page.locator('section[aria-label="任务材料"]');
await expect(材料.getByText("材料快照 01")).toBeVisible();
await expect(材料.getByText("仍可用于回读")).toBeVisible();
await expect(材料.getByText("历史:ch-1")).toBeVisible();
await expect(材料.getByText("已按冻结版本装配")).toBeVisible();
await expect(材料.getByText("参考:超长素材")).toBeVisible();
await expect(
材料.getByText("省略原因:超出材料预算,整项省略"),
).toBeVisible();
await expect(材料.getByText(/材料 \d+ 字节/)).toBeVisible();
await expect(材料.getByText(/文本哈希 [0-9a-f]{64}/)).toBeVisible();
await expect(材料.getByText("来源证据")).toBeVisible();
await expect(材料.getByText("1 处来源")).toBeVisible();
await expect(材料.getByText("版本 1", { exact: true })).toBeVisible();
await expect(
材料.getByText(/visible-text-codepoints\/v1:production:generation/),
).toBeVisible();
await expect(材料.getByText("仍有效")).toBeVisible();
// 造一处真实漂移:以当前正文版本为基线保存新版本,冻结快照的来源随即失效。
const 当前 = await (
await page.request.get(`/api/v1/chapters/${chapterId}/documents`)
).json();
const save = await page.request.post(`/api/v1/chapters/${chapterId}/documents`, {
headers: { Origin: process.env.MUSE_WORKBENCH_URL },
data: {
command_id: `browser-drift-revision-${当前.revision}`,
chapter_id: chapterId,
expected_revision: 当前.revision,
document: {
format_version: 1,
paragraphs: [
{
paragraph_id: "p-2",
content: [{ type: "text", text: "第二天清晨,雨停了。", marks: [] }],
},
],
},
},
});
expect(save.ok()).toBeTruthy();
await 打开任务();
await expect(材料.getByText("来源已漂移,需重新授权")).toBeVisible();
await expect(材料.getByText(/文稿 ch-1 已漂移/)).toBeVisible();
await expect(
材料.getByText(/文稿 ch-1 已从版本 \d+ 前进到 \d+/),
).toBeVisible();
await expect(材料.getByText("已漂移", { exact: true })).toBeVisible();
// 失效不改写历史装配:清单与省略原因仍按冻结时原样显示。
await expect(材料.getByText("历史:ch-1")).toBeVisible();
await expect(
材料.getByText("省略原因:超出材料预算,整项省略"),
).toBeVisible();
await expect(材料.getByText("版本 1", { exact: true })).toBeVisible();
await page.screenshot({
path: info.outputPath("任务材料与失效说明.png"),
fullPage: true,
});
expect(consoleErrors, `浏览器控制台错误:${consoleErrors.join(" | ")}`).toEqual(
[],
);
});