ai-chatbot/tests/flow-board/matrix-tables.spec.ts

225 lines
8.1 KiB
TypeScript

import { expect, type Page, test } from "@playwright/test";
import JSZip from "jszip";
async function upload(page: Page, rootTopic: object, name: string) {
const zip = new JSZip();
zip.file("content.json", JSON.stringify([{ title: name, rootTopic }]));
await page.locator("input[accept*=xmind]").setInputFiles({
name: `${name}.xmind`,
mimeType: "application/octet-stream",
buffer: await zip.generateAsync({ type: "nodebuffer" }),
});
}
test("side-title tree table renders a spanning left header and preserves its native structure", async ({
page,
}) => {
const errors: string[] = [];
page.on("pageerror", (error) => errors.push(error.message));
await page.goto("/dev/flow-board-preview", { waitUntil: "commit" });
await upload(
page,
{
id: "root",
title: "发布协调",
structureClass: "org.xmind.ui.treetable",
style: { properties: { "svg:fill": "#000000FF" } },
children: {
attached: [
{
id: "plan",
title: "规划",
style: { properties: { "svg:fill": "#F7C85FFF" } },
children: {
attached: [
{ id: "scope", title: "范围确认" },
{ id: "date", title: "发布日期" },
],
},
},
{ id: "ship", title: "交付" },
],
},
},
"侧标题树形表格"
);
const canvas = page.locator(".drawnix");
const table = canvas.locator('[data-special-table="treeTableSide"]');
await expect(table).toBeVisible();
await expect(table.locator("[data-special-table-cell]")).toHaveCount(5);
await expect(
table.locator('[data-special-table-cell][data-depth="0"]')
).toHaveAttribute("fill", "#000000");
await page.getByRole("button", { name: "适应画布", exact: true }).click();
const box = async (title: string) =>
(await canvas.getByText(title, { exact: true }).boundingBox())!;
const root = await box("发布协调");
const plan = await box("规划");
const scope = await box("范围确认");
const date = await box("发布日期");
expect(plan.x).toBeGreaterThan(root.x + root.width);
expect(scope.x).toBeGreaterThan(plan.x + plan.width);
expect(date.y).toBeGreaterThan(scope.y);
await canvas.getByText("交付", { exact: true }).click();
await page.getByRole("button", { name: "子主题", exact: true }).click();
await page.keyboard.insertText("上线复盘");
await page
.locator(".flow-board-canvas-shell")
.click({ position: { x: 50, y: 50 } });
await expect(canvas.getByText("上线复盘", { exact: true })).toBeVisible();
await expect(table.locator("[data-special-table-cell]")).toHaveCount(6);
await page.screenshot({
path: test.info().outputPath("side-tree-table-preview.png"),
animations: "disabled",
});
await canvas.getByText("发布协调", { exact: true }).click();
await expect(
page.getByRole("button", { name: "树形表格(侧标题)", exact: true })
).toHaveAttribute("aria-pressed", "true");
const downloadPromise = page.waitForEvent("download");
await page.getByRole("button", { name: "导出 XMind", exact: true }).click();
const stream = await (await downloadPromise).createReadStream();
const chunks: Buffer[] = [];
for await (const chunk of stream!) chunks.push(chunk);
const exported = await JSZip.loadAsync(Buffer.concat(chunks));
const content = JSON.parse(
await exported.file("content.json")!.async("string")
);
expect(content[0].rootTopic.structureClass).toBe(
"org.xmind.ui.treetable"
);
expect(errors).toEqual([]);
});
test("spreadsheet imports XMind columns, stacks labelled entries and round-trips the column model", async ({
page,
}) => {
const errors: string[] = [];
page.on("pageerror", (error) => errors.push(error.message));
await page.goto("/dev/flow-board-preview", { waitUntil: "commit" });
const columns = ["Monday", "Tuesday", "Friday"];
await upload(
page,
{
id: "root",
title: "Mood Tracker",
structureClass: "org.xmind.ui.spreadsheet",
style: { properties: { "svg:fill": "#1F2766FF" } },
extensions: [
{
provider: "org.xmind.ui.spreadsheet",
content: [
{
name: "columns",
content: columns.map((column) => ({
name: "column",
content: column,
})),
},
],
},
],
children: {
attached: [
{
id: "happy",
title: "Happy",
style: { properties: { "svg:fill": "#FF9D8EFF" } },
children: {
attached: [
{ id: "walk", title: "Morning walk", labels: ["Monday"] },
{ id: "coffee", title: "Coffee", labels: ["Monday"] },
{ id: "music", title: "Music", labels: ["Friday"] },
],
},
},
{
id: "calm",
title: "Calm",
children: {
attached: [
{ id: "read", title: "Read", labels: ["Tuesday"] },
],
},
},
],
},
},
"矩阵"
);
const canvas = page.locator(".drawnix");
const table = canvas.locator('[data-special-table="spreadsheet"]');
await expect(table).toBeVisible();
await expect(table.locator("[data-special-matrix-column]")).toHaveCount(3);
await expect(table.locator("[data-special-matrix-cell]")).toHaveCount(6);
await expect(table.locator("[data-special-matrix-entry]")).toHaveCount(4);
for (const column of columns)
await expect(
table.locator(`[data-special-matrix-column-label="${column}"]`)
).toHaveText(column);
await page.getByRole("button", { name: "适应画布", exact: true }).click();
const box = async (title: string) =>
(await canvas.getByText(title, { exact: true }).boundingBox())!;
const happy = await box("Happy");
const walk = await box("Morning walk");
const coffee = await box("Coffee");
const music = await box("Music");
const read = await box("Read");
expect(Math.abs(walk.x + walk.width / 2 - (coffee.x + coffee.width / 2))).toBeLessThan(1);
expect(coffee.y).toBeGreaterThan(walk.y);
expect(music.x).toBeGreaterThan(walk.x);
expect(read.x).toBeGreaterThan(walk.x);
expect(read.y).toBeGreaterThan(happy.y);
await canvas.getByText("Coffee", { exact: true }).dblclick();
await page.keyboard.press("ControlOrMeta+A");
await page.keyboard.insertText("Team coffee");
await page
.locator(".flow-board-canvas-shell")
.click({ position: { x: 50, y: 50 } });
await expect(canvas.getByText("Team coffee", { exact: true })).toBeVisible();
await canvas.getByText("Mood Tracker", { exact: true }).click();
await expect(
page.getByLabel("矩阵", { exact: true })
).toHaveAttribute("aria-pressed", "true");
const downloadPromise = page.waitForEvent("download");
await page.getByRole("button", { name: "导出 XMind", exact: true }).click();
const stream = await (await downloadPromise).createReadStream();
const chunks: Buffer[] = [];
for await (const chunk of stream!) chunks.push(chunk);
const buffer = Buffer.concat(chunks);
const exported = await JSZip.loadAsync(buffer);
const content = JSON.parse(
await exported.file("content.json")!.async("string")
);
expect(content[0].rootTopic.structureClass).toBe(
"org.xmind.ui.spreadsheet"
);
expect(
content[0].rootTopic.extensions[0].content[0].content.map(
(column: { content: string }) => column.content
)
).toEqual(columns);
expect(
content[0].rootTopic.children.attached[0].children.attached.find(
(topic: { title: string }) => topic.title === "Team coffee"
).labels
).toEqual(["Monday"]);
await page.locator("input[accept*=xmind]").setInputFiles({
name: "矩阵往返.xmind",
mimeType: "application/octet-stream",
buffer,
});
await expect(table).toBeVisible();
await expect(table.locator("[data-special-matrix-column]")).toHaveCount(3);
await page.getByRole("button", { name: "适应画布", exact: true }).click();
await page.screenshot({
path: test.info().outputPath("spreadsheet-preview.png"),
animations: "disabled",
});
expect(errors).toEqual([]);
});