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

67 lines
3.7 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-generation-dialog","environment":"真实浏览器隔离库","given":"隔离后端已有跑完的通过与失败生成任务","when":"真实浏览器打开任务","then":["创作对话与正文可见","检查报告可见","失败候选采纳被阻断"],"contract":"docs/系统架构/新版设计/接口契约/任务工具与事件.md"}
test("NC-browser-generation-dialog:创作对话、独立检查与采纳阻断", async ({
page,
}, info) => {
if (!process.env.MUSE_WORKBENCH_URL || !process.env.MUSE_AUTHOR_PASSWORD_FILE)
throw new Error("需要明确的隔离后端和作者口令文件。");
const goodTaskId = process.env.MUSE_GEN_TASK_ID;
const failedTaskId = process.env.MUSE_GEN_FAILED_TASK_ID;
if (!goodTaskId || !failedTaskId)
throw new Error("需要已跑完的通过任务与失败候选任务编号。");
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();
page.on("console", (message) => {
if (message.type() === "error" && !/favicon/.test(message.text()))
consoleErrors.push(message.text());
});
await page.goto("/tasks");
await page.getByLabel("任务编号").fill(goodTaskId);
await page.getByRole("button", { name: "查看任务" }).click();
const 对话 = page.locator('section[aria-label="创作对话"]');
await expect(对话).toBeVisible();
// 流式正文与过程事件可见。
await expect(对话.getByText("雨点敲着铁皮屋顶,林深把信压在掌心。")).toBeVisible();
await expect(对话.getByText("候选已保存")).toBeVisible();
// 独立检查报告:基础项全过、连续性无发现;采纳入口开放。
const 动作 = page.locator('section[aria-label="候选检查与决定"]');
await expect(动作.getByText("通过", { exact: true }).first()).toBeVisible();
await expect(动作.getByText("连续性检查没有发现问题。")).toBeVisible();
await expect(
动作.getByRole("button", { name: "采纳为正文新版本" }),
).toBeEnabled();
await page.screenshot({ path: `${info.outputDir}/生成任务与候选检查.png`, fullPage: true });
// 失败候选:复读细纲措辞 → 冲突呈现且采纳被服务端阻断(错误经作者决定面板呈现)。
await page.goto("/tasks");
await page.getByLabel("任务编号").fill(failedTaskId);
await page.getByRole("button", { name: "查看任务" }).click();
const 失败动作 = page.locator('section[aria-label="候选检查与决定"]');
await expect(失败动作).toBeVisible();
await expect(失败动作.getByText(/复读了细纲措辞/)).toBeVisible();
await expect(失败动作.getByText("未通过").first()).toBeVisible();
await 失败动作.getByRole("button", { name: "采纳为正文新版本" }).click();
await expect(失败动作.locator("p[role=\"alert\"]")).toContainText(/必需检查未通过/);
// 上一次点击的服务端拒绝(CHECK_FAILED)会以 422 响应返回,浏览器控制台把它记为资源错误;
// 拒绝本身正是上一行断言钉住的行为,故按同样的噪声排除纪律只排除这一条。
expect(
consoleErrors.filter((文本) => !文本.includes("status of 422")),
"页面不得出现预期外的控制台错误",
).toEqual([]);
}
);