403 lines
9.6 KiB
TypeScript
403 lines
9.6 KiB
TypeScript
|
import { API } from "../tests/helpers/api";
|
||
|
import { mutateElement } from "./mutateElement";
|
||
|
import { normalizeElementOrder } from "./sortElements";
|
||
|
import { ExcalidrawElement } from "./types";
|
||
|
|
||
|
const assertOrder = (
|
||
|
elements: readonly ExcalidrawElement[],
|
||
|
expectedOrder: string[],
|
||
|
) => {
|
||
|
const actualOrder = elements.map((element) => element.id);
|
||
|
expect(actualOrder).toEqual(expectedOrder);
|
||
|
};
|
||
|
|
||
|
describe("normalizeElementsOrder", () => {
|
||
|
it("sort bound-text elements", () => {
|
||
|
const container = API.createElement({
|
||
|
id: "container",
|
||
|
type: "rectangle",
|
||
|
});
|
||
|
const boundText = API.createElement({
|
||
|
id: "boundText",
|
||
|
type: "text",
|
||
|
containerId: container.id,
|
||
|
});
|
||
|
const otherElement = API.createElement({
|
||
|
id: "otherElement",
|
||
|
type: "rectangle",
|
||
|
boundElements: [],
|
||
|
});
|
||
|
const otherElement2 = API.createElement({
|
||
|
id: "otherElement2",
|
||
|
type: "rectangle",
|
||
|
boundElements: [],
|
||
|
});
|
||
|
|
||
|
mutateElement(container, {
|
||
|
boundElements: [{ type: "text", id: boundText.id }],
|
||
|
});
|
||
|
|
||
|
assertOrder(normalizeElementOrder([container, boundText]), [
|
||
|
"container",
|
||
|
"boundText",
|
||
|
]);
|
||
|
assertOrder(normalizeElementOrder([boundText, container]), [
|
||
|
"container",
|
||
|
"boundText",
|
||
|
]);
|
||
|
assertOrder(
|
||
|
normalizeElementOrder([
|
||
|
boundText,
|
||
|
container,
|
||
|
otherElement,
|
||
|
otherElement2,
|
||
|
]),
|
||
|
["container", "boundText", "otherElement", "otherElement2"],
|
||
|
);
|
||
|
assertOrder(normalizeElementOrder([container, otherElement, boundText]), [
|
||
|
"container",
|
||
|
"boundText",
|
||
|
"otherElement",
|
||
|
]);
|
||
|
assertOrder(
|
||
|
normalizeElementOrder([
|
||
|
container,
|
||
|
otherElement,
|
||
|
otherElement2,
|
||
|
boundText,
|
||
|
]),
|
||
|
["container", "boundText", "otherElement", "otherElement2"],
|
||
|
);
|
||
|
|
||
|
assertOrder(
|
||
|
normalizeElementOrder([
|
||
|
boundText,
|
||
|
otherElement,
|
||
|
container,
|
||
|
otherElement2,
|
||
|
]),
|
||
|
["otherElement", "container", "boundText", "otherElement2"],
|
||
|
);
|
||
|
|
||
|
// noop
|
||
|
assertOrder(
|
||
|
normalizeElementOrder([
|
||
|
otherElement,
|
||
|
container,
|
||
|
boundText,
|
||
|
otherElement2,
|
||
|
]),
|
||
|
["otherElement", "container", "boundText", "otherElement2"],
|
||
|
);
|
||
|
|
||
|
// text has existing containerId, but container doesn't list is
|
||
|
// as a boundElement
|
||
|
assertOrder(
|
||
|
normalizeElementOrder([
|
||
|
API.createElement({
|
||
|
id: "boundText",
|
||
|
type: "text",
|
||
|
containerId: "container",
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "container",
|
||
|
type: "rectangle",
|
||
|
}),
|
||
|
]),
|
||
|
["boundText", "container"],
|
||
|
);
|
||
|
assertOrder(
|
||
|
normalizeElementOrder([
|
||
|
API.createElement({
|
||
|
id: "boundText",
|
||
|
type: "text",
|
||
|
containerId: "container",
|
||
|
}),
|
||
|
]),
|
||
|
["boundText"],
|
||
|
);
|
||
|
assertOrder(
|
||
|
normalizeElementOrder([
|
||
|
API.createElement({
|
||
|
id: "container",
|
||
|
type: "rectangle",
|
||
|
boundElements: [],
|
||
|
}),
|
||
|
]),
|
||
|
["container"],
|
||
|
);
|
||
|
assertOrder(
|
||
|
normalizeElementOrder([
|
||
|
API.createElement({
|
||
|
id: "container",
|
||
|
type: "rectangle",
|
||
|
boundElements: [{ id: "x", type: "text" }],
|
||
|
}),
|
||
|
]),
|
||
|
["container"],
|
||
|
);
|
||
|
assertOrder(
|
||
|
normalizeElementOrder([
|
||
|
API.createElement({
|
||
|
id: "arrow",
|
||
|
type: "arrow",
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "container",
|
||
|
type: "rectangle",
|
||
|
boundElements: [{ id: "arrow", type: "arrow" }],
|
||
|
}),
|
||
|
]),
|
||
|
["arrow", "container"],
|
||
|
);
|
||
|
});
|
||
|
|
||
|
it("normalize group order", () => {
|
||
|
assertOrder(
|
||
|
normalizeElementOrder([
|
||
|
API.createElement({
|
||
|
id: "A_rect1",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["A"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "rect2",
|
||
|
type: "rectangle",
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "rect3",
|
||
|
type: "rectangle",
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "A_rect4",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["A"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "A_rect5",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["A"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "rect6",
|
||
|
type: "rectangle",
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "A_rect7",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["A"],
|
||
|
}),
|
||
|
]),
|
||
|
["A_rect1", "A_rect4", "A_rect5", "A_rect7", "rect2", "rect3", "rect6"],
|
||
|
);
|
||
|
assertOrder(
|
||
|
normalizeElementOrder([
|
||
|
API.createElement({
|
||
|
id: "A_rect1",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["A"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "rect2",
|
||
|
type: "rectangle",
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "B_rect3",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["B"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "A_rect4",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["A"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "B_rect5",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["B"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "rect6",
|
||
|
type: "rectangle",
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "A_rect7",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["A"],
|
||
|
}),
|
||
|
]),
|
||
|
["A_rect1", "A_rect4", "A_rect7", "rect2", "B_rect3", "B_rect5", "rect6"],
|
||
|
);
|
||
|
// nested groups
|
||
|
assertOrder(
|
||
|
normalizeElementOrder([
|
||
|
API.createElement({
|
||
|
id: "A_rect1",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["A"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "BA_rect2",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["B", "A"],
|
||
|
}),
|
||
|
]),
|
||
|
["A_rect1", "BA_rect2"],
|
||
|
);
|
||
|
assertOrder(
|
||
|
normalizeElementOrder([
|
||
|
API.createElement({
|
||
|
id: "BA_rect1",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["B", "A"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "A_rect2",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["A"],
|
||
|
}),
|
||
|
]),
|
||
|
["BA_rect1", "A_rect2"],
|
||
|
);
|
||
|
assertOrder(
|
||
|
normalizeElementOrder([
|
||
|
API.createElement({
|
||
|
id: "BA_rect1",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["B", "A"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "A_rect2",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["A"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "CBA_rect3",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["C", "B", "A"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "rect4",
|
||
|
type: "rectangle",
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "A_rect5",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["A"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "BA_rect5",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["B", "A"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "BA_rect6",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["B", "A"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "CBA_rect7",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["C", "B", "A"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "X_rect8",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["X"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "rect9",
|
||
|
type: "rectangle",
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "YX_rect10",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["Y", "X"],
|
||
|
}),
|
||
|
API.createElement({
|
||
|
id: "X_rect11",
|
||
|
type: "rectangle",
|
||
|
groupIds: ["X"],
|
||
|
}),
|
||
|
]),
|
||
|
[
|
||
|
"BA_rect1",
|
||
|
"BA_rect5",
|
||
|
"BA_rect6",
|
||
|
"A_rect2",
|
||
|
"A_rect5",
|
||
|
"CBA_rect3",
|
||
|
"CBA_rect7",
|
||
|
"rect4",
|
||
|
"X_rect8",
|
||
|
"X_rect11",
|
||
|
"YX_rect10",
|
||
|
"rect9",
|
||
|
],
|
||
|
);
|
||
|
});
|
||
|
|
||
|
// TODO
|
||
|
it.skip("normalize boundElements array", () => {
|
||
|
const container = API.createElement({
|
||
|
id: "container",
|
||
|
type: "rectangle",
|
||
|
boundElements: [],
|
||
|
});
|
||
|
const boundText = API.createElement({
|
||
|
id: "boundText",
|
||
|
type: "text",
|
||
|
containerId: container.id,
|
||
|
});
|
||
|
|
||
|
mutateElement(container, {
|
||
|
boundElements: [
|
||
|
{ type: "text", id: boundText.id },
|
||
|
{ type: "text", id: "xxx" },
|
||
|
],
|
||
|
});
|
||
|
|
||
|
expect(normalizeElementOrder([container, boundText])).toEqual([
|
||
|
expect.objectContaining({
|
||
|
id: container.id,
|
||
|
}),
|
||
|
expect.objectContaining({ id: boundText.id }),
|
||
|
]);
|
||
|
});
|
||
|
|
||
|
// should take around <100ms for 10K iterations (@dwelle's PC 22-05-25)
|
||
|
it.skip("normalizeElementsOrder() perf", () => {
|
||
|
const makeElements = (iterations: number) => {
|
||
|
const elements: ExcalidrawElement[] = [];
|
||
|
while (iterations--) {
|
||
|
const container = API.createElement({
|
||
|
type: "rectangle",
|
||
|
boundElements: [],
|
||
|
groupIds: ["B", "A"],
|
||
|
});
|
||
|
const boundText = API.createElement({
|
||
|
type: "text",
|
||
|
containerId: container.id,
|
||
|
groupIds: ["A"],
|
||
|
});
|
||
|
const otherElement = API.createElement({
|
||
|
type: "rectangle",
|
||
|
boundElements: [],
|
||
|
groupIds: ["C", "A"],
|
||
|
});
|
||
|
mutateElement(container, {
|
||
|
boundElements: [{ type: "text", id: boundText.id }],
|
||
|
});
|
||
|
|
||
|
elements.push(boundText, otherElement, container);
|
||
|
}
|
||
|
return elements;
|
||
|
};
|
||
|
|
||
|
const elements = makeElements(10000);
|
||
|
const t0 = Date.now();
|
||
|
normalizeElementOrder(elements);
|
||
|
console.info(`${Date.now() - t0}ms`);
|
||
|
});
|
||
|
});
|