110 lines
3.7 KiB
TypeScript
110 lines
3.7 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import type { PlaitElement } from "@plait/core";
|
|
import {
|
|
applyOutlineCommand,
|
|
renameOutlineTopic,
|
|
} from "@/lib/flow-board/mind-outline";
|
|
import { getTopicEntries } from "@/lib/flow-board/topic-details";
|
|
const topic = (id: string, children: PlaitElement[] = []): PlaitElement => ({
|
|
id,
|
|
type: "mind_child",
|
|
data: {
|
|
topic: { type: "paragraph", children: [{ text: id, bold: true }] },
|
|
image: { url: "picture" },
|
|
},
|
|
xmind: { notes: "保留备注" },
|
|
children,
|
|
});
|
|
const root = (...children: PlaitElement[]): PlaitElement => ({
|
|
...topic("root", children),
|
|
type: "mindmap",
|
|
layout: "standard",
|
|
rightNodeCount: children.length,
|
|
points: [[0, 0]],
|
|
});
|
|
const find = (children: PlaitElement[], id: string) =>
|
|
getTopicEntries(children).find((entry) => entry.element.id === id)!;
|
|
|
|
test("outline nesting preserves IDs and metadata and rejects moving into descendants", () => {
|
|
const source = [root(topic("a"), topic("b", [topic("c")]))];
|
|
const before = JSON.stringify(source);
|
|
const nested = applyOutlineCommand(source, "b", "indent");
|
|
expect(find(nested.children, "b").path).toEqual([0, 0, 0]);
|
|
expect(find(nested.children, "c").path).toEqual([0, 0, 0, 0]);
|
|
expect(find(nested.children, "b").element.xmind).toEqual({
|
|
notes: "保留备注",
|
|
});
|
|
expect(JSON.stringify(source)).toBe(before);
|
|
expect(() =>
|
|
applyOutlineCommand(nested.children, "a", {
|
|
targetId: "c",
|
|
placement: "child",
|
|
})
|
|
).toThrow("不能把主题");
|
|
const outdented = applyOutlineCommand(nested.children, "b", "outdent");
|
|
expect(find(outdented.children, "b").path).toEqual([0, 1]);
|
|
});
|
|
|
|
test("outline deletion adjusts summary range without assigning unrelated topics", () => {
|
|
const source = [
|
|
root(topic("a"), topic("b"), topic("c"), {
|
|
...topic("summary"),
|
|
start: 0,
|
|
end: 1,
|
|
}),
|
|
];
|
|
const removed = applyOutlineCommand(source, "a", "delete");
|
|
expect(find(removed.children, "summary").element).toMatchObject({
|
|
start: 0,
|
|
end: 0,
|
|
});
|
|
expect(() => applyOutlineCommand(source, "b", "down")).toThrow(
|
|
"概要范围断开"
|
|
);
|
|
expect(find(source, "summary").element).toMatchObject({ start: 0, end: 1 });
|
|
});
|
|
|
|
test("outline insertion expands folded parents and includes new topics inside a summary", () => {
|
|
const source = [
|
|
root(topic("a"), topic("b"), { ...topic("summary"), start: 0, end: 1 }),
|
|
];
|
|
const inserted = applyOutlineCommand(source, "a", "sibling");
|
|
expect(find(inserted.children, "summary").element).toMatchObject({
|
|
start: 0,
|
|
end: 2,
|
|
});
|
|
expect(find(inserted.children, inserted.selectedId).path).toEqual([0, 1]);
|
|
const folded = [{ ...root(topic("a")), isCollapsed: true }];
|
|
expect(
|
|
applyOutlineCommand(folded, "root", "child").children[0].isCollapsed
|
|
).toBe(false);
|
|
});
|
|
|
|
test("outline drag moves a subtree across branches and retains annotation references", () => {
|
|
const relation = {
|
|
id: "link",
|
|
type: "arrow-line",
|
|
mindAnnotation: { kind: "relationship", sourceId: "b", targetId: "c" },
|
|
};
|
|
const source = [root(topic("a", [topic("b")]), topic("c")), relation];
|
|
const moved = applyOutlineCommand(source, "b", {
|
|
targetId: "c",
|
|
placement: "child",
|
|
});
|
|
expect(find(moved.children, "b").path).toEqual([0, 1, 0]);
|
|
expect(moved.children[1]).toEqual(relation);
|
|
});
|
|
|
|
test("outline creates the first root and renaming preserves image and text marks", () => {
|
|
const created = applyOutlineCommand([], null, "child");
|
|
expect(created.children[0]).toMatchObject({
|
|
type: "mindmap",
|
|
id: created.selectedId,
|
|
});
|
|
const data = renameOutlineTopic(topic("a"), "新名称");
|
|
expect(data).toMatchObject({
|
|
image: { url: "picture" },
|
|
topic: { children: [{ text: "新名称", bold: true }] },
|
|
});
|
|
});
|