import { expect, test, type Page } from "@playwright/test"; import JSZip from "jszip"; import { clickXmindAction } from "./editor-actions"; async function openMap(page: Page, markers = false) { await page.goto("/dev/flow-board-preview", { waitUntil: "commit" }); const zip = new JSZip(); zip.file("content.json", JSON.stringify([{ title: "标记验证", rootTopic: { id: "root", title: "项目", children: { attached: [ { id: "a", title: "设计", ...(markers ? { markers: [{ markerId: "priority-1" }] } : {}) }, { id: "b", title: "开发", ...(markers ? { markers: [{ markerId: "priority-2" }, { markerId: "unknown-marker" }] } : {}) }, ] }, } }])); await page.locator("input[accept*=xmind]").setInputFiles({ name: "markers.xmind", mimeType: "application/octet-stream", buffer: await zip.generateAsync({ type: "nodebuffer" }) }); await expect(page.locator(".drawnix").getByText("设计", { exact: true })).toBeVisible(); await page.getByRole("button", { name: "标记", exact: true }).click(); } const markerImages = (page: Page, id?: string) => page.locator(`.drawnix .xmind-topic-marker${id ? `[data-marker-id="${id}"]` : ""}`); const markerButton = (page: Page, id: string) => page.getByLabel("标记面板", { exact: true }).locator(`button[data-marker-id="${id}"]`); test("native marker palette fits the panel and adds, replaces, toggles, clears and undoes", async ({ page }) => { const errors: string[] = []; page.on("pageerror", error => errors.push(error.message)); await openMap(page); const panel = page.getByLabel("标记面板", { exact: true }); await expect(page.getByRole("button", { name: "标记", exact: true })).toHaveAttribute("aria-pressed", "true"); await expect(panel.locator("button[data-marker-id]")).toHaveCount(58); await expect(panel).toHaveCSS("width", "240px"); expect(await panel.evaluate(element => element.scrollWidth <= element.clientWidth)).toBe(true); await page.mouse.click(130, 150); await expect(markerButton(page, "priority-1")).toBeDisabled(); await page.locator(".drawnix").getByText("设计", { exact: true }).click(); await markerButton(page, "priority-1").click(); await expect(markerImages(page, "priority-1")).toHaveCount(1); await expect(markerButton(page, "priority-1")).toHaveAttribute("aria-pressed", "true"); await expect.poll(() => markerImages(page, "priority-1").evaluate((image: HTMLImageElement) => image.complete && image.naturalWidth > 0)).toBe(true); await markerButton(page, "priority-2").click(); await expect(markerImages(page, "priority-1")).toHaveCount(0); await expect(markerImages(page, "priority-2")).toHaveCount(1); await markerButton(page, "task-half").click(); await markerButton(page, "flag-green").click(); await expect(markerImages(page)).toHaveCount(3); await markerButton(page, "flag-green").click(); await expect(markerImages(page)).toHaveCount(2); await panel.getByRole("button", { name: "清空所选节点标记", exact: true }).click(); await expect(markerImages(page)).toHaveCount(0); await clickXmindAction(page, "撤销"); await expect(markerImages(page)).toHaveCount(2); await expect(markerButton(page, "priority-2")).toHaveAttribute("aria-pressed", "true"); await expect(markerButton(page, "task-half")).toHaveAttribute("aria-pressed", "true"); await page.setViewportSize({ width: 800, height: 700 }); expect(await panel.evaluate(element => element.scrollWidth <= element.clientWidth)).toBe(true); await page.setViewportSize({ width: 1440, height: 900 }); await expect(markerImages(page, "priority-2")).toBeVisible(); await page.screenshot({ path: "test-results/flow-board-ui/marker-panel.png" }); expect(errors).toEqual([]); }); test("mixed multi-selection applies one value consistently and undo restores the batch", async ({ page }) => { await openMap(page, true); const canvas = page.locator(".drawnix"); await canvas.getByText("设计", { exact: true }).click(); await canvas.getByText("开发", { exact: true }).click({ modifiers: ["Shift"] }); await expect(page.getByLabel("标记面板", { exact: true }).getByRole("status")).toHaveText("已选中 2 个主题"); await expect(markerButton(page, "priority-1")).toHaveAttribute("aria-pressed", "mixed"); await markerButton(page, "priority-1").click(); await expect(markerImages(page, "priority-1")).toHaveCount(2); await expect(markerImages(page, "priority-2")).toHaveCount(0); await clickXmindAction(page, "撤销"); await expect(markerImages(page, "priority-1")).toHaveCount(1); await expect(markerImages(page, "priority-2")).toHaveCount(1); await clickXmindAction(page, "重做"); await expect(markerImages(page, "priority-1")).toHaveCount(2); await expect(markerButton(page, "priority-1")).toHaveAttribute("aria-pressed", "true"); await page.getByRole("button", { name: "清空所选节点标记", exact: true }).click(); await expect(markerImages(page)).toHaveCount(0); const downloadPending = page.waitForEvent("download"); await page.getByRole("button", { name: "导出 XMind", exact: true }).click(); const stream = await (await downloadPending).createReadStream(); const chunks: Buffer[] = []; for await (const chunk of stream!) chunks.push(chunk); const zip = await JSZip.loadAsync(Buffer.concat(chunks)); const sheets = JSON.parse(await zip.file("content.json")!.async("string")); expect(sheets[0].rootTopic.children.attached.every((topic: { markers?: unknown }) => !topic.markers)).toBe(true); await clickXmindAction(page, "撤销"); await expect(markerImages(page, "priority-1")).toHaveCount(2); await expect(markerButton(page, "priority-1")).toHaveAttribute("aria-pressed", "true"); }); test("search, group collapse and additional installed marker resources are usable", async ({ page }) => { await openMap(page); const panel = page.getByLabel("标记面板", { exact: true }); await page.locator(".drawnix").getByText("设计", { exact: true }).click(); await panel.getByRole("button", { name: "标签 7", exact: true }).click(); await expect(markerButton(page, "tag-red")).toHaveCount(0); await panel.getByLabel("搜索标记").fill("priority-9"); await expect(panel.locator("button[data-marker-id]")).toHaveCount(1); await markerButton(page, "priority-9").click(); await expect(markerImages(page, "priority-9")).toHaveCount(1); await panel.getByLabel("搜索标记").fill("没有这个标记"); await expect(panel.getByText("没有匹配的标记", { exact: true })).toBeVisible(); await panel.getByLabel("搜索标记").fill(""); await panel.getByRole("button", { name: "标签 7", exact: true }).click(); await panel.getByRole("button", { name: "更多标记", exact: true }).click(); await expect(panel.locator("button[data-marker-id]")).toHaveCount(219); await page.getByRole("button", { name: "关闭标记面板", exact: true }).click(); await expect(panel).toHaveCount(0); await expect(page.getByLabel("格式面板", { exact: true })).toBeVisible(); });