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.
160 lines
6.1 KiB
TypeScript
160 lines
6.1 KiB
TypeScript
import { clickXmindAction, clickXmindSheet } from "./editor-actions";
|
|
import { expect, test } from "@playwright/test";
|
|
import JSZip from "jszip";
|
|
|
|
test("outline edits, creates, nests and deletes topics with shared canvas history", async ({
|
|
page,
|
|
}) => {
|
|
const errors: string[] = [];
|
|
page.on("pageerror", (error) => errors.push(error.message));
|
|
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: "市场调研",
|
|
notes: { plain: { content: "调研备注" } },
|
|
},
|
|
{
|
|
id: "b",
|
|
title: "产品设计",
|
|
children: { attached: [{ id: "c", title: "设计评审" }] },
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
{ title: "归档", rootTopic: { id: "other", title: "历史记录" } },
|
|
])
|
|
);
|
|
await page.locator("input[accept*=xmind]").setInputFiles({
|
|
name: "大纲测试.xmind",
|
|
mimeType: "application/octet-stream",
|
|
buffer: await zip.generateAsync({ type: "nodebuffer" }),
|
|
});
|
|
await page.getByRole("button", { name: "切换到大纲", exact: true }).click();
|
|
const outline = page.getByRole("region", { name: "大纲视图", exact: true });
|
|
await expect(
|
|
outline.getByLabel("大纲主题 项目计划", { exact: true })
|
|
).toBeVisible();
|
|
const research = outline.getByLabel("大纲主题 市场调研", { exact: true });
|
|
const initialHeight = (await research.boundingBox())!.height;
|
|
await clickXmindAction(page, "放大");
|
|
await expect
|
|
.poll(async () => (await research.boundingBox())!.height)
|
|
.toBeGreaterThan(initialHeight);
|
|
await clickXmindAction(page, "适应画布");
|
|
await research.fill("用户研究");
|
|
await research.press("Enter");
|
|
await expect(
|
|
outline.getByLabel("大纲主题 新主题", { exact: true })
|
|
).toBeFocused();
|
|
const newTopic = outline.getByLabel("大纲主题 新主题", { exact: true });
|
|
await newTopic.fill("竞争分析");
|
|
await newTopic.press("Tab");
|
|
const competitor = outline.getByLabel("大纲主题 竞争分析", { exact: true });
|
|
await expect(competitor.locator("..")).toHaveAttribute("data-depth", "2");
|
|
await competitor.press("Shift+Tab");
|
|
await expect(competitor.locator("..")).toHaveAttribute("data-depth", "1");
|
|
await clickXmindAction(page, "撤销");
|
|
await expect(competitor.locator("..")).toHaveAttribute("data-depth", "2");
|
|
await clickXmindAction(page, "重做");
|
|
await expect(competitor.locator("..")).toHaveAttribute("data-depth", "1");
|
|
await outline
|
|
.getByRole("button", { name: "折叠 产品设计", exact: true })
|
|
.click();
|
|
await expect(
|
|
outline.getByLabel("大纲主题 设计评审", { exact: true })
|
|
).toHaveCount(0);
|
|
await outline
|
|
.getByRole("button", { name: "展开 产品设计", exact: true })
|
|
.click();
|
|
await expect(
|
|
outline.getByLabel("大纲主题 设计评审", { exact: true })
|
|
).toBeVisible();
|
|
await page
|
|
.getByRole("button", { name: "切换到思维导图", exact: true })
|
|
.click();
|
|
const canvas = page.locator(".drawnix");
|
|
await expect(canvas.getByText("用户研究", { exact: true })).toBeVisible();
|
|
await expect(canvas.getByText("竞争分析", { exact: true })).toBeVisible();
|
|
await canvas.getByText("产品设计", { exact: true }).dblclick();
|
|
await page.keyboard.press("ControlOrMeta+A");
|
|
await page.keyboard.insertText("方案设计");
|
|
await page
|
|
.locator(".flow-board-canvas-shell")
|
|
.click({ position: { x: 100, y: 100 } });
|
|
await page.getByRole("button", { name: "切换到大纲", exact: true }).click();
|
|
await expect(
|
|
outline.getByLabel("大纲主题 方案设计", { exact: true })
|
|
).toBeVisible();
|
|
await outline.getByLabel("大纲主题 竞争分析", { exact: true }).click();
|
|
await outline
|
|
.getByRole("button", { name: "大纲删除主题", exact: true })
|
|
.click();
|
|
await expect(
|
|
outline.getByLabel("大纲主题 竞争分析", { exact: true })
|
|
).toHaveCount(0);
|
|
await clickXmindAction(page, "撤销");
|
|
await expect(
|
|
outline.getByLabel("大纲主题 竞争分析", { exact: true })
|
|
).toBeVisible();
|
|
await page.getByRole("button", { name: "归档", exact: true }).click();
|
|
await expect(
|
|
outline.getByLabel("大纲主题 历史记录", { exact: true })
|
|
).toBeVisible();
|
|
await clickXmindSheet(page, "项目");
|
|
await expect(
|
|
outline.getByLabel("大纲主题 用户研究", { exact: true })
|
|
).toBeVisible();
|
|
const renamedResearch = outline.getByLabel("大纲主题 用户研究", {
|
|
exact: true,
|
|
});
|
|
await renamedResearch.fill("取消这次改名");
|
|
await renamedResearch.press("Escape");
|
|
await expect(renamedResearch).toHaveValue("用户研究");
|
|
await outline.getByLabel("大纲主题 用户研究", { exact: true }).click();
|
|
await clickXmindAction(page, "备注");
|
|
await expect(page.getByLabel("主题备注", { exact: true })).toHaveValue(
|
|
"调研备注"
|
|
);
|
|
await outline
|
|
.getByLabel("拖动 设计评审", { exact: true })
|
|
.dragTo(
|
|
outline.getByLabel("大纲主题 用户研究", { exact: true }).locator("..")
|
|
);
|
|
await expect
|
|
.poll(() =>
|
|
outline
|
|
.locator("[data-outline-input]")
|
|
.evaluateAll((nodes) =>
|
|
nodes.map((node) => (node as HTMLInputElement).value)
|
|
)
|
|
)
|
|
.toEqual(["项目计划", "用户研究", "设计评审", "竞争分析", "方案设计"]);
|
|
await page.screenshot({
|
|
path: test.info().outputPath("outline-preview.png"),
|
|
});
|
|
await page.getByRole("button", { name: "新增画布", exact: true }).click();
|
|
await outline
|
|
.getByRole("button", { name: "添加中心主题", exact: true })
|
|
.click();
|
|
await expect(
|
|
outline.getByLabel("大纲主题 中心主题", { exact: true })
|
|
).toBeVisible();
|
|
await outline.getByLabel("大纲主题 中心主题", { exact: true }).press("Enter");
|
|
await expect(
|
|
outline.getByLabel("大纲主题 新主题", { exact: true })
|
|
).toBeFocused();
|
|
expect(errors).toEqual([]);
|
|
});
|