112 lines
3.8 KiB
TypeScript
112 lines
3.8 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import JSZip from "jszip";
|
|
import { exportXmind } from "@/lib/flow-board/xmind-export";
|
|
|
|
const topic = (id: string, title: string, children: any[] = []): any => ({
|
|
id,
|
|
type: "mind_child",
|
|
data: { topic: { children: [{ text: title }] } },
|
|
children,
|
|
});
|
|
const root = (id: string, title: string, children: any[] = []): any => ({
|
|
...topic(id, title, children),
|
|
type: "mindmap",
|
|
layout: "right",
|
|
points: [[0, 0]],
|
|
});
|
|
const read = async (result: Awaited<ReturnType<typeof exportXmind>>) =>
|
|
JSZip.loadAsync(await result.blob.arrayBuffer());
|
|
|
|
test("XMind export uses current active content, fresh IDs and valid cross-topic references", async () => {
|
|
const a = topic("a", "研发");
|
|
const b = topic("b", "上线");
|
|
const r = root("r", "最新中心", [
|
|
a,
|
|
b,
|
|
{ ...topic("sum", "第一阶段"), start: 0, end: 1 },
|
|
]);
|
|
a.xmind = {
|
|
topicLinkId: "b",
|
|
notes: { plain: { content: "计划\n第二行" } },
|
|
labels: ["工作"],
|
|
};
|
|
a.data.topic.children[0] = { text: "研发", bold: true, "font-size": 24 };
|
|
const elements = [
|
|
r,
|
|
{
|
|
id: "boundary",
|
|
type: "geometry",
|
|
mindAnnotation: { kind: "boundary", topicIds: ["a", "b"], padding: 18 },
|
|
},
|
|
{
|
|
id: "rel",
|
|
type: "arrow-line",
|
|
texts: [{ text: { children: [{ text: "前置" }] } }],
|
|
mindAnnotation: { kind: "relationship", sourceId: "a", targetId: "b" },
|
|
},
|
|
];
|
|
const snapshot = {
|
|
activeSheetId: "one",
|
|
children: elements,
|
|
sheets: [
|
|
{ id: "one", title: "计划", children: [root("r", "旧数据")] },
|
|
{ id: "two", title: "归档", children: [root("r", "第二中心")] },
|
|
],
|
|
};
|
|
const before = JSON.stringify(snapshot);
|
|
const result = await exportXmind(snapshot, "计划");
|
|
const zip = await read(result);
|
|
const sheets = JSON.parse(await zip.file("content.json")!.async("string"));
|
|
expect(sheets).toHaveLength(2);
|
|
const out = sheets[0].rootTopic;
|
|
expect(out.title).toBe("最新中心");
|
|
expect(out.children.attached[0].href).toBe(
|
|
`xmind:#${out.children.attached[1].id}`
|
|
);
|
|
expect(out.children.attached[0].notes.plain.content).toBe("计划\n第二行");
|
|
expect(out.children.attached[0].style.properties["fo:font-size"]).toBe("24");
|
|
expect(out.boundaries[0].range).toBe("(0,1)");
|
|
expect(out.summaries[0].topicId).toBe(out.children.summary[0].id);
|
|
expect(sheets[0].relationships[0]).toMatchObject({
|
|
end1Id: out.children.attached[0].id,
|
|
end2Id: out.children.attached[1].id,
|
|
title: "前置",
|
|
});
|
|
expect(sheets[1].rootTopic.id).not.toBe(out.id);
|
|
expect(JSON.stringify(snapshot)).toBe(before);
|
|
expect(result.warnings).toEqual([]);
|
|
});
|
|
|
|
test("XMind attachment bytes and manifest entries are portable", async () => {
|
|
const r = root("root", "附件");
|
|
r.xmind = {
|
|
attachment: {
|
|
name: "需求.txt",
|
|
url: "data:application/octet-stream;base64,5YaF5a65",
|
|
mimeType: "application/octet-stream",
|
|
},
|
|
};
|
|
const zip = await read(await exportXmind({ children: [r] }, "附件"));
|
|
const sheets = JSON.parse(await zip.file("content.json")!.async("string"));
|
|
const path = sheets[0].rootTopic.href.replace(/^xap:/, "");
|
|
expect(await zip.file(path)!.async("string")).toBe("内容");
|
|
expect(
|
|
JSON.parse(await zip.file("manifest.json")!.async("string"))["file-entries"]
|
|
).toHaveProperty([path]);
|
|
expect(
|
|
JSON.parse(await zip.file("metadata.json")!.async("string"))
|
|
.dataStructureVersion
|
|
).toBe("2");
|
|
});
|
|
|
|
test("unsupported board elements warn and empty mind sheets never export misleading workbooks", async () => {
|
|
const result = await exportXmind(
|
|
{ children: [root("r", "中心"), { id: "text", type: "text" }] },
|
|
"测试"
|
|
);
|
|
expect(result.warnings[0]).toContain("自由画布");
|
|
await expect(exportXmind({ children: [] }, "空白")).rejects.toThrow(
|
|
"没有可导出"
|
|
);
|
|
});
|