+ {Object.entries(邻接).map(([对象, 边]) => (
+
+ {对象.slice(0, 10)}:{边.join("、")}
+
+ ))}
+
+ );
+}
diff --git a/web/tests/旅程/世界维护.spec.ts b/web/tests/旅程/世界维护.spec.ts
new file mode 100644
index 0000000..d4ea203
--- /dev/null
+++ b/web/tests/旅程/世界维护.spec.ts
@@ -0,0 +1,55 @@
+/** 真实浏览器与隔离库:世界资料页、人物关系页与时间伏笔页的维护旅程。 */
+import { readFileSync } from "node:fs";
+import { expect, test } from "@playwright/test";
+
+test.use({ trace: "off" });
+
+test("NC-browser-world-workbench:世界维护三页旅程", async ({ page }, info) => {
+ if (!process.env.MUSE_WORKBENCH_URL || !process.env.MUSE_AUTHOR_PASSWORD_FILE)
+ throw new Error("需要明确的隔离后端和作者口令文件。");
+ const workId = process.env.MUSE_WORK_ID!;
+ if (!workId) 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.goto(`/works/${workId}/world`);
+ const 清单 = page.locator('section[aria-label="实体清单"]');
+ await expect(清单.getByText(/2 个对象/)).toBeVisible({ timeout: 15_000 });
+ await 清单.getByRole("button", { name: /char-lin/ }).first().click();
+ await expect(page.locator('section[aria-label="版本轨迹"]').getByText(/r1/)).toBeVisible();
+ await expect(
+ page.locator('section[aria-label="修订影响"]').getByText(/受影响事实:\d+ 个/),
+ ).toBeVisible();
+ await page.screenshot({ path: `${info.outputDir}/世界资料页.png`, fullPage: true });
+
+ // 人物关系页:人物分组可见;弧线按已发生/计划分区。
+ await page.goto(`/works/${workId}/world/relations`);
+ await expect(page.locator('section[aria-label="人物分组"]').getByText(/character/)).toBeVisible();
+ await page
+ .locator('section[aria-label="人物分组"]')
+ .getByRole("button", { name: /char-lin/ })
+ .first()
+ .click();
+ await expect(
+ page.locator('section[aria-label="人物弧线"]').getByText(/已发生(\d+)/),
+ ).toBeVisible();
+ await page.screenshot({ path: `${info.outputDir}/人物关系页.png`, fullPage: true });
+
+ // 时间与伏笔页:时点查询表单与说明可见。
+ await page.goto(`/works/${workId}/world/timeline`);
+ await expect(page.getByRole("button", { name: "查询时点世界" })).toBeVisible();
+ await expect(page.locator('section[aria-label="伏笔与承诺"]')).toBeVisible();
+ await page.screenshot({ path: `${info.outputDir}/时间与伏笔页.png`, fullPage: true });
+
+ expect(
+ consoleErrors.filter((文本) => !文本.includes("favicon")),
+ "页面不得出现控制台错误",
+ ).toEqual([]);
+});