muse-agent-example/web/tests/旅程/候选审阅.spec.ts
zizi 98530437f2 W11 编辑器、动态表单与人审工作台:编辑器、动态表单与人审工作台(确认前正文不落正式库)。
按 R2 串行阶段整理提交;包内文件为该阶段交付(含后续小增量),状态以工作包清单为准。
2026-09-10 19:25:41 +08:00

126 lines
5.9 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

/** 浏览器、安装后的真实后端与专用PG;候选由作者操作产生,不声称模型生成。 */
import { readFileSync } from "node:fs";
import { expect, test } from "@playwright/test";
test.use({ trace: "off" });
test("NC-browser-candidate-review:改稿恢复部分选择旧审阅冲突与真实采纳", async ({
page,
context,
}, info) => {
if (!process.env.MUSE_WORKBENCH_URL || !process.env.MUSE_AUTHOR_PASSWORD_FILE)
throw new 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("button", { name: "新建作品" }).click();
await page.getByLabel("名称", { exact: true }).fill("候选改稿(合成验收)");
await page.getByRole("button", { name: "保存档案" }).click();
await page.getByLabel("新章节标题").fill("街角的信");
await page.getByRole("button", { name: "添加章节" }).click();
const editor = page.getByRole("textbox", { name: "章节正文" });
await editor.click();
await page.keyboard.insertText(" “原句。”e\u0301🧑‍🏽");
await page.keyboard.press("Enter");
await page.keyboard.press("Enter");
await page.keyboard.insertText("末段仍留在原处。");
await expect(page.getByText("服务器已保存", { exact: true })).toBeVisible();
const chapter = decodeURIComponent(
new URL(page.url()).pathname.split("/").at(-1)!,
);
const docURL = `/api/v1/chapters/${encodeURIComponent(chapter)}/documents`;
const listURL = `/api/v1/chapters/${encodeURIComponent(chapter)}/writing-candidates`;
const original = await (await page.request.get(docURL)).json();
await page.getByRole("link", { name: "比较正文候选" }).click();
await page.getByRole("button", { name: "另拟改稿" }).click();
let draft = page.getByRole("textbox", { name: "候选改稿" });
await draft.locator("p").first().click();
await page.keyboard.press("Meta+ArrowLeft");
await page.keyboard.insertText("改稿:");
await draft.locator("p").last().click();
await page.keyboard.press("Meta+ArrowRight");
await page.keyboard.insertText("新增末句。");
// 刷新只恢复私人候选缓冲,不产生正文或候选写入。
await page.reload();
await page.getByRole("button", { name: "另拟改稿" }).click();
draft = page.getByRole("textbox", { name: "候选改稿" });
await expect(draft).toContainText("改稿:");
await expect(draft).toContainText("新增末句。");
expect((await (await page.request.get(docURL)).json()).revision).toBe(
original.revision,
);
expect(await (await page.request.get(listURL)).json()).toEqual([]);
await page.getByRole("button", { name: "保存候选版本", exact: true }).click();
await page.getByRole("button", { name: /^候选 1 · 版本 1/ }).click();
await expect(
page.getByRole("button", { name: "采纳为正文新版本" }),
).toBeEnabled();
const before = (await (await page.request.get(listURL)).json())[0];
expect(before.changes).toHaveLength(2);
const other = await context.newPage();
await other.goto(page.url());
await other.getByRole("button", { name: /^候选 1 · 版本 1/ }).click();
await expect(
other.getByRole("button", { name: "采纳为正文新版本" }),
).toBeEnabled();
await page.getByRole("button", { name: "编辑候选或部分选择" }).click();
await page.getByRole("button", { name: "按段选择", exact: true }).click();
await page.getByLabel("选择第 1 处改动").check();
await page
.getByRole("button", { name: "将所选改动保存为候选新版本" })
.click();
await page.getByRole("button", { name: /^候选 1 · 版本 2/ }).click();
const partial = (await (await page.request.get(listURL)).json())[0];
expect(partial.revision).toBe(2);
expect(partial.candidate_hash).not.toBe(before.candidate_hash);
expect(partial.visible_text).toContain("改稿:");
expect(partial.visible_text).not.toContain("新增末句。");
await page.screenshot({
path: info.outputPath("候选部分选择后审阅.png"),
fullPage: true,
timeout: 10000,
});
await page
.getByRole("button", { name: "采纳为正文新版本" })
.scrollIntoViewIfNeeded();
await page.screenshot({
path: info.outputPath("正文差异与决定面板.png"),
fullPage: true,
timeout: 10000,
});
await other.getByRole("button", { name: "采纳为正文新版本" }).click();
await expect(
other.getByRole("button", { name: "重新打开审阅" }),
).toBeVisible();
expect((await (await page.request.get(docURL)).json()).revision).toBe(
original.revision,
);
await other.close();
await page.getByRole("button", { name: "采纳为正文新版本" }).click();
await expect(page.getByText("已采纳", { exact: true })).toBeVisible();
const final = await (await page.request.get(docURL)).json();
expect(final.revision).toBe(original.revision + 1);
expect(final.visible_text).toBe(partial.visible_text);
expect(final.document.paragraphs[1]).toEqual(original.document.paragraphs[1]);
expect(final.document.paragraphs[2]).toEqual(original.document.paragraphs[2]);
await page.getByRole("link", { name: "返回正文" }).click();
await expect(page.getByRole("textbox", { name: "章节正文" })).toContainText(
"改稿:",
);
await page.getByRole("button", { name: "查看创作进度" }).click();
await expect(page.getByText("共 1 章,1 章已保存正文。")).toBeVisible();
const progress = await (
await page.request.get(
`/api/v1/works/${encodeURIComponent(original.work_id)}/progress`,
)
).json();
expect(progress.undecided_candidates).toBe(0);
expect(progress.chapters[0].document_revision).toBe(final.revision);
await info.attach("真实候选与正文版本", {
body: JSON.stringify({ original, candidate: partial, final }, null, 2),
contentType: "application/json",
});
});