| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- import { expect, test } from "@playwright/test";
- import { makeView } from "../fixtures";
- const view = makeView(2, 3, 2, "success");
- const artifactDetail = {
- businessSections: [{ id: "summary", title: "主脚本规模", content: "1 个段落、1 个元素、1 条关联" }],
- technical: { source: "read-only-test" },
- changes: { summary: "当前主脚本内容", sections: [], artifactRef: "artifact:base:current", exactness: "current-only" },
- scriptTable: {
- snapshotRef: "artifact:base:current",
- exactness: "current-only",
- sourceOrigin: "database",
- counts: { paragraphs: 1, elements: 1, links: 1 },
- maxDepth: 0,
- paragraphs: [{
- id: 1, rowId: 1, index: 1, level: 1, depth: 0, name: "机制拆解", contentRange: ["正文: 第二段"],
- sections: {
- theme: { label: "主题", description: "解释份额换订单", descriptionParts: [{ type: "text", text: "解释份额换订单" }], dimensions: [{ type: "主维度", name: "核心竞争力", value: "份额换订单" }] },
- form: { label: "形式", description: "案例拆解", descriptionParts: [{ type: "text", text: "案例拆解" }], dimensions: [] },
- function: { label: "作用", description: "承接上文", descriptionParts: [{ type: "text", text: "承接上文" }], dimensions: [] },
- feeling: { label: "感受", description: "建立可信度", descriptionParts: [{ type: "text", text: "建立可信度" }], dimensions: [] },
- },
- fullDescription: "通过份额换订单解释机制。",
- fullDescriptionParts: [{ type: "text", text: "通过" }, { type: "element", id: 10, name: "份额换订单", resolved: true, text: "[元素:10]" }, { type: "text", text: "解释机制。" }],
- }],
- elements: [{ id: 10, rowId: 10, name: "份额换订单", primaryDimension: "核心竞争力", secondaryDimension: "商业模式", paragraphs: [{ id: 1, index: 1, name: "机制拆解" }] }],
- },
- };
- test.beforeEach(async ({ page }) => {
- await page.route("**/api/**", async (route) => {
- const path = new URL(route.request().url()).pathname;
- if (path === "/api/script-builds") {
- await route.fulfill({ json: [{ id: view.header.id, status: "success", currentRound: 2, createdAt: view.header.createdAt }] });
- return;
- }
- if (path.endsWith("/execution-view")) {
- await route.fulfill({ json: view });
- return;
- }
- if (path.includes("/artifacts/")) {
- await route.fulfill({ json: artifactDetail });
- return;
- }
- if (path.includes("/decision-prompts/")) {
- await route.fulfill({ json: {
- promptRef: "prompt:main",
- actor: { role: "main", label: "主 Agent" },
- systemPrompt: {
- content: "当前主 Agent 规则正文。",
- source: "current-db",
- version: 8,
- accuracy: "current-not-run-snapshot",
- truncated: false,
- },
- notices: [{ code: "PROMPT_NOT_SNAPSHOTTED", message: "系统没有保存这次运行所用提示词的历史快照;下方展示当前提示词。" }],
- } });
- return;
- }
- if (path.includes("/inspector-view/")) {
- await route.fulfill({ json: {
- schemaVersion: "inspector-source-v2",
- detailRef: "event:test",
- cardKind: "test",
- completeness: "complete",
- businessProjection: {
- businessSections: [{ id: "summary", title: "业务记录", content: "这是按需读取的完整业务记录。" }],
- },
- modules: [{
- id: "summary",
- title: "业务记录",
- businessPath: "/businessSections/0",
- presentation: "text",
- rows: [{
- id: "summary:content",
- businessSelector: "/businessSections/0/content",
- bindingIds: ["summary:source:1"],
- }],
- bindings: [{
- id: "summary:source:1",
- sourceId: "event:test",
- role: "output",
- selector: { kind: "json-pointer", path: "/output/content" },
- transform: { kind: "direct" },
- evidence: { availability: "produced-by-run", adoption: "not-applicable", confidence: "exact" },
- resolution: "resolved",
- }],
- runtimeRefs: ["event:test"],
- defaultOpen: true,
- }],
- sources: {
- "event:test": {
- id: "event:test",
- kind: "runtime-event",
- label: "测试 Event",
- locator: { eventId: "test" },
- completeness: "complete",
- resultState: "present",
- truncated: false,
- selectedValues: [{ bindingId: "summary:source:1", value: "这是按需读取的完整业务记录。" }],
- rawRecord: { output: { content: "这是按需读取的完整业务记录。" } },
- },
- },
- notices: [],
- } });
- return;
- }
- await route.fulfill({
- json: {
- businessSections: [{ id: "summary", title: "业务记录", content: "这是按需读取的完整业务记录。" }],
- technical: { source: "read-only-test" },
- },
- });
- });
- await page.goto("/");
- });
- test("V8 在桌面与平板呈现发散、取数、取舍再收敛", async ({ page }, testInfo) => {
- test.skip(testInfo.project.name.includes("mobile"), "移动端使用轮次列表");
- await expect(page.getByRole("heading", { name: "脚本构建" })).toBeVisible();
- await expect(page.locator(".react-flow__minimap")).toHaveCount(0);
- await page.getByRole("button", { name: /展开本轮/ }).first().click();
- await expect(page.getByText("主 Agent · 规划", { exact: true })).toBeVisible();
- await expect(page.getByText("实现 Agent · 多路方案", { exact: true })).toBeVisible();
- await expect(page.getByText("多方案评审 Agent", { exact: true }).first()).toBeAttached();
- await expect(page.getByText("主 Agent · 收敛", { exact: true })).toBeAttached();
- await expect(page.getByText("整体评审 Agent", { exact: true }).first()).toBeAttached();
- await expect(page.getByTestId("rf__node-multipath-review-1-1").getByText("多方案评审", { exact: true })).toBeAttached();
- await expect(page.getByTestId("rf__node-multipath-1-1").getByText("第 1 批决策", { exact: true })).toBeAttached();
- await expect(page.getByTestId("rf__node-multipath-1-2").getByText("第 2 批决策", { exact: true })).toBeAttached();
- await page.getByRole("button", { name: /展开方案过程/ }).first().click();
- const canvas = page.locator(".flowShell");
- await expect(canvas.getByText("取数阶段", { exact: true }).first()).toBeAttached();
- await expect(canvas.getByText("工具取数", { exact: true }).first()).toBeAttached();
- await expect(canvas.getByText("Agent 取数", { exact: true }).first()).toBeAttached();
- await expect(canvas.getByText("数据取舍", { exact: true }).first()).toBeAttached();
- const details = page.getByRole("button", { name: /查看详情/ }).first();
- await expect(details).toBeVisible();
- await details.click();
- await expect(page.getByRole("dialog", { name: /三列来源详情/ })).toBeVisible();
- await expect(page.getByRole("columnheader", { name: "业务详情" })).toBeVisible();
- await expect(page.getByRole("columnheader", { name: "数据依据" })).toBeVisible();
- await expect(page.getByRole("columnheader", { name: "原始记录与运行" })).toBeVisible();
- });
- test("收敛卡在全量、紧凑和概览缩放下都不越出代理区域", async ({ page }, testInfo) => {
- test.skip(testInfo.project.name.includes("mobile"), "移动端使用轮次列表");
- await page.getByRole("button", { name: /展开本轮/ }).first().click();
- const assertContained = async (cardSelector: string, regionId: string) => {
- const region = page.locator(`[data-id="${regionId}"]`);
- const cards = page.locator(cardSelector);
- await expect(region).toBeAttached();
- await expect(cards.first()).toBeAttached();
- const regionBox = await region.boundingBox();
- expect(regionBox).not.toBeNull();
- for (let index = 0; index < await cards.count(); index += 1) {
- const box = await cards.nth(index).boundingBox();
- expect(box).not.toBeNull();
- expect(box!.y).toBeGreaterThanOrEqual(regionBox!.y - 1);
- expect(box!.y + box!.height).toBeLessThanOrEqual(regionBox!.y + regionBox!.height + 1);
- }
- };
- const assertConvergenceLayout = async () => {
- await assertContained('[data-id^="multipath-review-"]', "region:round:1:multipath-review");
- await assertContained('[data-id^="multipath-1-"]', "region:round:1:convergence");
- };
- await assertConvergenceLayout();
- await page.locator(".react-flow__controls-zoomout").click();
- await page.locator(".react-flow__controls-zoomout").click();
- await expect(page.locator(".flowShell")).toHaveAttribute("data-detail-level", "compact");
- await assertConvergenceLayout();
- await page.locator(".react-flow__controls-zoomout").click();
- await page.locator(".react-flow__controls-zoomout").click();
- await page.locator(".react-flow__controls-zoomout").click();
- await expect(page.locator(".flowShell")).toHaveAttribute("data-detail-level", "overview");
- await assertConvergenceLayout();
- await expect(page.locator(".react-flow__node-agentRegion")).toHaveCount(6);
- });
- test("Agent 规则抽屉明确区分当前规则与历史运行快照", async ({ page }, testInfo) => {
- test.skip(testInfo.project.name.includes("mobile"), "移动端由组件测试覆盖规则抽屉");
- const objective = page.getByTestId("rf__node-objective");
- await objective.getByRole("button", { name: "查看详情" }).click();
- const inspector = page.getByRole("dialog", { name: /三列来源详情$/ });
- await expect(inspector).toBeVisible();
- const trigger = inspector.getByRole("button", { name: "查看主 Agent 提示词" });
- await expect(trigger).toBeVisible();
- await trigger.click();
- const dialog = page.getByRole("dialog", { name: "Agent 任务与提示词" });
- await expect(dialog).toBeVisible();
- await expect(dialog.getByRole("heading", { name: "当前提示词" })).toBeVisible();
- await expect(dialog.getByText(/没有保存这次运行所用提示词的历史快照/)).toBeVisible();
- await expect(dialog.getByText("当前数据库版本 · v8")).toBeVisible();
- await page.keyboard.press("Escape");
- await expect(dialog).toHaveCount(0);
- await expect(inspector).toBeVisible();
- await expect(trigger).toBeFocused();
- });
- test("V8 移动端按轮次阅读并能展开取数树", async ({ page }, testInfo) => {
- test.skip(!testInfo.project.name.includes("mobile"), "仅验证移动端信息架构");
- await expect(page.getByRole("heading", { name: "这次构建经历了什么" })).toBeVisible();
- await page.getByRole("button", { name: "展开第 1 轮" }).click();
- await expect(page.getByText("候选方案 1", { exact: true })).toBeVisible();
- await page.getByRole("button", { name: "展开方案过程" }).first().click();
- await expect(page.getByText("取数阶段", { exact: true }).first()).toBeVisible();
- await expect(page.getByText("Agent 取数", { exact: true }).first()).toBeVisible();
- await expect(page.getByText("数据取舍", { exact: true }).first()).toBeVisible();
- const mobile = page.locator(".mobileTimeline");
- await expect(mobile.getByText("多方案评审 Agent · 评审判断", { exact: true }).first()).toBeVisible();
- await expect(mobile.getByText("评审建议", { exact: true }).first()).toBeVisible();
- await expect(mobile.getByText("主 Agent · 取舍决定", { exact: true }).first()).toBeVisible();
- await expect(mobile.getByText("最终决定", { exact: true }).first()).toBeVisible();
- await expect(mobile.getByText("整体评审 Agent · 评审判断", { exact: true }).first()).toBeVisible();
- });
- test("完整主脚本在桌面、平板和手机都使用独立可滚动查看器", async ({ page }) => {
- const trigger = page.getByRole("button", { name: "查看完整主脚本" });
- await expect(trigger).toHaveCount(1);
- await trigger.focus();
- await trigger.evaluate((element: HTMLElement) => element.click());
- const dialog = page.getByRole("dialog");
- await expect(dialog).toBeVisible();
- await expect(dialog.getByText("1 个段落 · 1 个元素 · 1 条关联")).toBeVisible();
- await expect(dialog.getByRole("columnheader", { name: "维度类型" }).first()).toBeVisible();
- await expect(dialog.getByText("正文: 第二段")).toBeVisible();
- await expect(dialog.getByText("主", { exact: true }).first()).toBeVisible();
- await dialog.getByRole("tab", { name: "脚本元素 1" }).click();
- await expect(dialog.getByRole("row", { name: /份额换订单/ })).toContainText("商业模式");
- const dimensions = await dialog.evaluate((element) => ({ width: element.getBoundingClientRect().width, viewport: window.innerWidth }));
- expect(dimensions.width).toBeLessThanOrEqual(dimensions.viewport);
- await dialog.getByRole("button", { name: "关闭完整主脚本" }).click();
- await expect(dialog).toHaveCount(0);
- await expect(trigger).toBeFocused();
- });
|