ai-chatbot/tests/flow-board/vertical-timeline.spec.ts

109 lines
4.4 KiB
TypeScript

import { expect, test } from "@playwright/test";
import JSZip from "jszip";
test("vertical timeline mirrors branches, edits children and survives sheet switching and file round-trip", 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: "root",
title: "年度计划",
structureClass: "org.xmind.ui.timeline.vertical",
children: {
attached: [
{ id: "a", title: "第一阶段" },
{
id: "b",
title: "第二阶段",
children: { attached: [{ id: "b1", title: "已有左侧任务" }] },
},
{ id: "c", title: "第三阶段" },
{ id: "d", 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" }),
});
const canvas = page.locator(".drawnix");
const axis = canvas.locator('[data-special-axis="verticalTimeline"]');
await expect(axis).toHaveCount(1);
await page.getByRole("button", { name: "适应画布", exact: true }).click();
const box = async (title: string) =>
(await canvas.getByText(title, { exact: true }).boundingBox())!;
const root = await box("年度计划"),
first = await box("第一阶段"),
second = await box("第二阶段"),
third = await box("第三阶段");
expect(first.x).toBeGreaterThan(root.x + root.width / 2);
expect(second.x + second.width).toBeLessThan(root.x + root.width / 2);
expect(second.y).toBeGreaterThan(first.y + first.height);
expect(third.y).toBeGreaterThan(second.y + second.height);
expect((await box("已有左侧任务")).x).toBeLessThan(second.x);
await canvas.getByText("第二阶段", { exact: true }).click();
await page.getByRole("button", { name: "子主题", exact: true }).click();
await expect(canvas.locator('[contenteditable="true"]')).toBeFocused();
await page.keyboard.insertText("新左侧任务");
await page
.locator(".flow-board-canvas-shell")
.click({ position: { x: 40, y: 40 } });
await expect(canvas.getByText("新左侧任务", { exact: true })).toBeVisible();
expect((await box("新左侧任务")).x).toBeLessThan((await box("第二阶段")).x);
await canvas.getByText("年度计划", { exact: true }).click();
await page.getByRole("button", { name: "向右逻辑", exact: true }).click();
await expect(axis).toHaveCount(0);
await page.getByRole("button", { name: "撤销", exact: true }).click();
await expect(axis).toHaveCount(1);
await page.getByRole("button", { name: "另一个画布", exact: true }).click();
await expect(axis).toHaveCount(0);
await page.getByRole("button", { name: "纵向计划", exact: true }).click();
await expect(axis).toHaveCount(1);
const downloading = page.waitForEvent("download");
await page.getByRole("button", { name: "导出 XMind", exact: true }).click();
const stream = await (await downloading).createReadStream();
const chunks = [];
for await (const chunk of stream!) chunks.push(chunk);
const buffer = Buffer.concat(chunks);
const exported = await JSZip.loadAsync(buffer);
const sheets = JSON.parse(
await exported.file("content.json")!.async("string")
);
expect(sheets[0].rootTopic.structureClass).toBe(
"org.xmind.ui.timeline.vertical"
);
expect(
sheets[0].rootTopic.children.attached[1].children.attached.map(
(t: { title: string }) => t.title
)
).toContain("新左侧任务");
await page
.locator("input[accept*=xmind]")
.setInputFiles({
name: "往返.xmind",
mimeType: "application/octet-stream",
buffer,
});
await expect(axis).toHaveCount(1);
await expect(canvas.getByText("新左侧任务", { exact: true })).toBeVisible();
await page.getByRole("button", { name: "适应画布", exact: true }).click();
await page.screenshot({
path: test.info().outputPath("vertical-timeline-preview.png"),
animations: "disabled",
});
expect(errors).toEqual([]);
});