ai-chatbot/tests/flow-board/native-editor.spec.ts
134 79253245f3 fix(flow-board): align Xmind editor UI and restore marquee feedback
Add native-style menus, search and presentation controls, typography resources, and Xmind import/export styling support.

Bundle canvas styles for preview and production, show marquee borders and selection fills, and align editor density with the project UI. Include interaction regression coverage and client comparison notes.
2026-10-04 13:32:54 +08:00

87 lines
5.3 KiB
TypeScript

import { expect, test } from "@playwright/test";
import JSZip from "jszip";
import { clickXmindAction } from "./editor-actions";
async function importWorkbook(page: import("@playwright/test").Page) {
await page.goto("/dev/flow-board-preview",{waitUntil:"commit"});
const zip = new JSZip();
zip.file("content.json",JSON.stringify([
{title:"产品计划",rootTopic:{id:"r",title:"产品计划",children:{attached:[
{id:"a",title:"设计评审",branch:"folded",children:{attached:[{id:"b",title:"设计细节"}]}},
{id:"c",title:"开发验收"},
]}}},
{title:"归档",rootTopic:{id:"r2",title:"历史设计记录"}},
]));
await page.locator("input[accept*=xmind]").setInputFiles({name:"计划.xmind",mimeType:"application/octet-stream",buffer:await zip.generateAsync({type:"nodebuffer"})});
await expect(page.locator(".drawnix").getByText("产品计划",{exact:true})).toBeVisible();
}
test("native floating toolbar and menus work with keyboard and narrow viewports",async ({page}) => {
const errors:string[]=[];page.on("pageerror",e => errors.push(e.message));
await importWorkbook(page);
await expect(page.locator(".xmind-toolbar-pill").first()).toHaveCSS("border-radius","8px");
await expect(page.getByLabel("格式面板",{exact:true})).toHaveCSS("width","240px");
await page.getByRole("button",{name:"文件与编辑",exact:true}).focus();
await page.keyboard.press("ArrowDown");
await expect(page.getByRole("menuitem",{name:"保存",exact:false})).toBeFocused();
await page.keyboard.press("ArrowDown");
await expect(page.getByRole("menuitem",{name:"云文件",exact:true})).toBeFocused();
await page.keyboard.press("Escape");
await expect(page.getByRole("menu",{name:"文件与编辑",exact:true})).toHaveCount(0);
await page.setViewportSize({width:800,height:700});
const toolbar=await page.locator(".xmind-topbar").boundingBox();
expect(toolbar!.x+toolbar!.width).toBeLessThanOrEqual(800);
await page.getByRole("button",{name:"格式",exact:true}).click();
await expect(page.getByLabel("格式面板",{exact:true})).toHaveCount(0);
await page.getByRole("button",{name:"格式",exact:true}).click();
await expect(page.getByLabel("格式面板",{exact:true})).toBeVisible();
expect(errors).toEqual([]);
});
test("search finds collapsed and cross-sheet topics, replacement undoes as a batch",async ({page}) => {
await importWorkbook(page);
await page.keyboard.press("ControlOrMeta+f");
await page.getByLabel("查找主题",{exact:true}).fill("设计");
const search = page.getByLabel("查找与替换",{exact:true});
await expect(search.getByRole("status")).toHaveText("2 个匹配主题");
await search.getByRole("button",{name:"定位 设计细节",exact:true}).click();
await expect(page.locator(".drawnix").getByText("设计细节",{exact:true})).toBeVisible();
await page.getByLabel("替换为",{exact:true}).fill("方案");
await search.getByRole("button",{name:"全部替换",exact:true}).click();
await expect(page.locator(".drawnix").getByText("方案评审",{exact:true})).toBeVisible();
await expect(page.locator(".drawnix").getByText("方案细节",{exact:true})).toBeVisible();
await clickXmindAction(page,"撤销");
await expect(page.locator(".drawnix").getByText("设计评审",{exact:true})).toBeVisible();
await clickXmindAction(page,"重做");
await expect(page.locator(".drawnix").getByText("方案评审",{exact:true})).toBeVisible();
await page.getByLabel("查找范围",{exact:true}).selectOption("all");
await expect(search.getByRole("status")).toHaveText("1 个匹配主题");
await search.getByRole("button",{name:"定位 历史设计记录",exact:true}).click();
await expect(page.locator(".drawnix").getByText("历史设计记录",{exact:true})).toBeVisible();
await page.getByRole("button",{name:"关闭查找",exact:true}).click();
await page.keyboard.press("ControlOrMeta+f");
await page.keyboard.press("Escape");
await expect(search).toHaveCount(0);
});
test("ZEN and presentation restore the edited workbook and sheet",async ({page}) => {
await importWorkbook(page);
await page.getByRole("button",{name:"ZEN 模式",exact:true}).click();
await expect(page.locator(".xmind-topbar")).toHaveCount(0);
await expect(page.getByLabel("格式面板",{exact:true})).toHaveCount(0);
await expect(page.locator(".drawnix").getByText("产品计划",{exact:true})).toBeVisible();
await page.keyboard.press("Escape");
await expect(page.locator(".xmind-topbar")).toBeVisible();
await page.getByRole("button",{name:"演示",exact:true}).click();
const presentation = page.getByLabel("演示视图",{exact:true});
await expect(presentation.getByRole("heading",{name:"产品计划",exact:true})).toBeVisible();
await page.keyboard.press("ArrowRight");
await expect(presentation.getByRole("heading",{name:"设计评审",exact:true})).toBeVisible();
await page.keyboard.press("Escape");
await expect(presentation).toHaveCount(0);
await expect(page.locator(".drawnix").getByText("产品计划",{exact:true})).toBeVisible();
await page.getByRole("button",{name:"产品计划",exact:true}).dblclick();
const title=page.getByLabel("重命名画布",{exact:true});await title.fill("错误标题");await title.press("Escape");
await expect(page.getByRole("button",{name:"产品计划",exact:true})).toBeVisible();
});