225 lines
8.1 KiB
TypeScript
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([]);
|
|
});
|