import { chooseXmindLayout, clickXmindAction, openXmindStructures } from "./editor-actions"; import { expect, test } from "@playwright/test"; import JSZip from "jszip"; test("tree table aligns hierarchy cells, edits topics and preserves its XMind structure", 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.treetable.toptitle", children: { attached: [ { id: "a", title: "产品", children: { attached: [ { id: "a1", title: "需求分析" }, { id: "a2", title: "原型设计" }, ], }, }, { id: "b", title: "研发" }, { id: "c", 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 table = canvas.locator('[data-special-table="treeTable"]'); await expect(table).toBeVisible(); await expect(table.locator("[data-special-table-cell]")).toHaveCount(6); await clickXmindAction(page, "适应画布"); const box = async (title: string) => (await canvas.getByText(title, { exact: true }).boundingBox())!; const root = await box("项目清单"); const product = await box("产品"); const requirement = await box("需求分析"); const prototype = await box("原型设计"); const development = await box("研发"); expect(root.y + root.height).toBeLessThan(product.y); expect(requirement.x).toBeGreaterThan(product.x + product.width); expect(prototype.x).toBe(requirement.x); expect(development.y).toBeGreaterThan(prototype.y); 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: 100, y: 100 } }); await expect(canvas.getByText("接口联调", { exact: true })).toBeVisible(); await expect(table.locator("[data-special-table-cell]")).toHaveCount(7); await canvas.getByText("项目清单", { exact: true }).click(); await openXmindStructures(page); await expect( page.getByRole("complementary", { name: "格式面板" }).getByLabel("树形表格", { exact: true }) ).toHaveAttribute("aria-pressed", "true"); await chooseXmindLayout(page, "向右逻辑"); await expect(table).toHaveCount(0); await clickXmindAction(page, "撤销"); await expect(table).toBeVisible(); 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 content = JSON.parse( await exported.file("content.json")!.async("string") ); expect(content[0].rootTopic.structureClass).toBe( "org.xmind.ui.treetable.toptitle" ); await page.locator("input[accept*=xmind]").setInputFiles({ name: "树形表格往返.xmind", mimeType: "application/octet-stream", buffer, }); await expect(table).toBeVisible(); await expect(canvas.getByText("接口联调", { exact: true })).toBeVisible(); await clickXmindAction(page, "适应画布"); await page.screenshot({ path: test.info().outputPath("tree-table-preview.png"), animations: "disabled", }); expect(errors).toEqual([]); });