557 lines
15 KiB
TypeScript
557 lines
15 KiB
TypeScript
import { ExcalidrawElement } from "./element/types";
|
|
import {
|
|
convertToExcalidrawElements,
|
|
Excalidraw,
|
|
} from "./packages/excalidraw/index";
|
|
import { API } from "./tests/helpers/api";
|
|
import { Keyboard, Pointer } from "./tests/helpers/ui";
|
|
import { render } from "./tests/test-utils";
|
|
|
|
const { h } = window;
|
|
const mouse = new Pointer("mouse");
|
|
|
|
describe("adding elements to frames", () => {
|
|
type ElementType = string;
|
|
const assertOrder = (
|
|
els: readonly { type: ElementType }[],
|
|
order: ElementType[],
|
|
) => {
|
|
expect(els.map((el) => el.type)).toEqual(order);
|
|
};
|
|
|
|
const reorderElements = <T extends { type: ElementType }>(
|
|
els: readonly T[],
|
|
order: ElementType[],
|
|
) => {
|
|
return order.reduce((acc: T[], el) => {
|
|
acc.push(els.find((e) => e.type === el)!);
|
|
return acc;
|
|
}, []);
|
|
};
|
|
|
|
function resizeFrameOverElement(
|
|
frame: ExcalidrawElement,
|
|
element: ExcalidrawElement,
|
|
) {
|
|
mouse.clickAt(0, 0);
|
|
mouse.downAt(frame.x + frame.width, frame.y + frame.height);
|
|
mouse.moveTo(
|
|
element.x + element.width + 50,
|
|
element.y + element.height + 50,
|
|
);
|
|
mouse.up();
|
|
}
|
|
|
|
function dragElementIntoFrame(
|
|
frame: ExcalidrawElement,
|
|
element: ExcalidrawElement,
|
|
) {
|
|
mouse.clickAt(element.x, element.y);
|
|
mouse.downAt(element.x + element.width / 2, element.y + element.height / 2);
|
|
mouse.moveTo(frame.x + frame.width / 2, frame.y + frame.height / 2);
|
|
mouse.up();
|
|
}
|
|
|
|
function selectElementAndDuplicate(
|
|
element: ExcalidrawElement,
|
|
moveTo: [number, number] = [element.x + 25, element.y + 25],
|
|
) {
|
|
const [x, y] = [
|
|
element.x + element.width / 2,
|
|
element.y + element.height / 2,
|
|
];
|
|
|
|
Keyboard.withModifierKeys({ alt: true }, () => {
|
|
mouse.downAt(x, y);
|
|
mouse.moveTo(moveTo[0], moveTo[1]);
|
|
mouse.up();
|
|
});
|
|
}
|
|
|
|
function expectEqualIds(expected: ExcalidrawElement[]) {
|
|
expect(h.elements.map((x) => x.id)).toEqual(expected.map((x) => x.id));
|
|
}
|
|
|
|
let frame: ExcalidrawElement;
|
|
let rect1: ExcalidrawElement;
|
|
let rect2: ExcalidrawElement;
|
|
let rect3: ExcalidrawElement;
|
|
let rect4: ExcalidrawElement;
|
|
let text: ExcalidrawElement;
|
|
let arrow: ExcalidrawElement;
|
|
|
|
beforeEach(async () => {
|
|
await render(<Excalidraw />);
|
|
|
|
frame = API.createElement({ id: "id0", type: "frame", x: 0, width: 150 });
|
|
rect1 = API.createElement({
|
|
id: "id1",
|
|
type: "rectangle",
|
|
x: -1000,
|
|
});
|
|
rect2 = API.createElement({
|
|
id: "id2",
|
|
type: "rectangle",
|
|
x: 200,
|
|
width: 50,
|
|
});
|
|
rect3 = API.createElement({
|
|
id: "id3",
|
|
type: "rectangle",
|
|
x: 400,
|
|
width: 50,
|
|
});
|
|
rect4 = API.createElement({
|
|
id: "id4",
|
|
type: "rectangle",
|
|
x: 1000,
|
|
width: 50,
|
|
});
|
|
text = API.createElement({
|
|
id: "id5",
|
|
type: "text",
|
|
x: 100,
|
|
});
|
|
arrow = API.createElement({
|
|
id: "id6",
|
|
type: "arrow",
|
|
x: 100,
|
|
boundElements: [{ id: text.id, type: "text" }],
|
|
});
|
|
});
|
|
|
|
const commonTestCases = async (
|
|
func: typeof resizeFrameOverElement | typeof dragElementIntoFrame,
|
|
) => {
|
|
describe.skip("when frame is in a layer below", async () => {
|
|
it("should add an element", async () => {
|
|
h.elements = [frame, rect2];
|
|
|
|
func(frame, rect2);
|
|
|
|
expect(h.elements[0].frameId).toBe(frame.id);
|
|
expectEqualIds([rect2, frame]);
|
|
});
|
|
|
|
it("should add elements", async () => {
|
|
h.elements = [frame, rect2, rect3];
|
|
|
|
func(frame, rect2);
|
|
func(frame, rect3);
|
|
|
|
expect(rect2.frameId).toBe(frame.id);
|
|
expect(rect3.frameId).toBe(frame.id);
|
|
expectEqualIds([rect2, rect3, frame]);
|
|
});
|
|
|
|
it("should add elements when there are other other elements in between", async () => {
|
|
h.elements = [frame, rect1, rect2, rect4, rect3];
|
|
|
|
func(frame, rect2);
|
|
func(frame, rect3);
|
|
|
|
expect(rect2.frameId).toBe(frame.id);
|
|
expect(rect3.frameId).toBe(frame.id);
|
|
expectEqualIds([rect2, rect3, frame, rect1, rect4]);
|
|
});
|
|
|
|
it("should add elements when there are other elements in between and the order is reversed", async () => {
|
|
h.elements = [frame, rect3, rect4, rect2, rect1];
|
|
|
|
func(frame, rect2);
|
|
func(frame, rect3);
|
|
|
|
expect(rect2.frameId).toBe(frame.id);
|
|
expect(rect3.frameId).toBe(frame.id);
|
|
expectEqualIds([rect2, rect3, frame, rect4, rect1]);
|
|
});
|
|
});
|
|
|
|
describe.skip("when frame is in a layer above", async () => {
|
|
it("should add an element", async () => {
|
|
h.elements = [rect2, frame];
|
|
|
|
func(frame, rect2);
|
|
|
|
expect(h.elements[0].frameId).toBe(frame.id);
|
|
expectEqualIds([rect2, frame]);
|
|
});
|
|
|
|
it("should add elements", async () => {
|
|
h.elements = [rect2, rect3, frame];
|
|
|
|
func(frame, rect2);
|
|
func(frame, rect3);
|
|
|
|
expect(rect2.frameId).toBe(frame.id);
|
|
expect(rect3.frameId).toBe(frame.id);
|
|
expectEqualIds([rect3, rect2, frame]);
|
|
});
|
|
|
|
it("should add elements when there are other other elements in between", async () => {
|
|
h.elements = [rect1, rect2, rect4, rect3, frame];
|
|
|
|
func(frame, rect2);
|
|
func(frame, rect3);
|
|
|
|
expect(rect2.frameId).toBe(frame.id);
|
|
expect(rect3.frameId).toBe(frame.id);
|
|
expectEqualIds([rect1, rect4, rect3, rect2, frame]);
|
|
});
|
|
|
|
it("should add elements when there are other elements in between and the order is reversed", async () => {
|
|
h.elements = [rect3, rect4, rect2, rect1, frame];
|
|
|
|
func(frame, rect2);
|
|
func(frame, rect3);
|
|
|
|
expect(rect2.frameId).toBe(frame.id);
|
|
expect(rect3.frameId).toBe(frame.id);
|
|
expectEqualIds([rect4, rect1, rect3, rect2, frame]);
|
|
});
|
|
});
|
|
|
|
describe("when frame is in an inner layer", async () => {
|
|
it.skip("should add elements", async () => {
|
|
h.elements = [rect2, frame, rect3];
|
|
|
|
func(frame, rect2);
|
|
func(frame, rect3);
|
|
|
|
expect(rect2.frameId).toBe(frame.id);
|
|
expect(rect3.frameId).toBe(frame.id);
|
|
expectEqualIds([rect2, rect3, frame]);
|
|
});
|
|
|
|
it.skip("should add elements when there are other other elements in between", async () => {
|
|
h.elements = [rect2, rect1, frame, rect4, rect3];
|
|
|
|
func(frame, rect2);
|
|
func(frame, rect3);
|
|
|
|
expect(rect2.frameId).toBe(frame.id);
|
|
expect(rect3.frameId).toBe(frame.id);
|
|
expectEqualIds([rect1, rect2, rect3, frame, rect4]);
|
|
});
|
|
|
|
it.skip("should add elements when there are other elements in between and the order is reversed", async () => {
|
|
h.elements = [rect3, rect4, frame, rect2, rect1];
|
|
|
|
func(frame, rect2);
|
|
func(frame, rect3);
|
|
|
|
expect(rect2.frameId).toBe(frame.id);
|
|
expect(rect3.frameId).toBe(frame.id);
|
|
expectEqualIds([rect4, rect3, rect2, frame, rect1]);
|
|
});
|
|
});
|
|
};
|
|
|
|
const resizingTest = async (
|
|
containerType: "arrow" | "rectangle",
|
|
initialOrder: ElementType[],
|
|
expectedOrder: ElementType[],
|
|
) => {
|
|
await render(<Excalidraw />);
|
|
|
|
const frame = API.createElement({ type: "frame", x: 0, y: 0 });
|
|
|
|
h.elements = reorderElements(
|
|
[
|
|
frame,
|
|
...convertToExcalidrawElements([
|
|
{
|
|
type: containerType,
|
|
x: 100,
|
|
y: 100,
|
|
height: 10,
|
|
label: { text: "xx" },
|
|
},
|
|
]),
|
|
],
|
|
initialOrder,
|
|
);
|
|
|
|
assertOrder(h.elements, initialOrder);
|
|
|
|
expect(h.elements[1].frameId).toBe(null);
|
|
expect(h.elements[2].frameId).toBe(null);
|
|
|
|
const container = h.elements[1];
|
|
|
|
resizeFrameOverElement(frame, container);
|
|
assertOrder(h.elements, expectedOrder);
|
|
|
|
expect(h.elements[0].frameId).toBe(frame.id);
|
|
expect(h.elements[1].frameId).toBe(frame.id);
|
|
};
|
|
|
|
describe("resizing frame over elements", async () => {
|
|
await commonTestCases(resizeFrameOverElement);
|
|
|
|
it.skip("resizing over text containers and labelled arrows", async () => {
|
|
await resizingTest(
|
|
"rectangle",
|
|
["frame", "rectangle", "text"],
|
|
["rectangle", "text", "frame"],
|
|
);
|
|
await resizingTest(
|
|
"rectangle",
|
|
["frame", "text", "rectangle"],
|
|
["rectangle", "text", "frame"],
|
|
);
|
|
await resizingTest(
|
|
"rectangle",
|
|
["rectangle", "text", "frame"],
|
|
["rectangle", "text", "frame"],
|
|
);
|
|
await resizingTest(
|
|
"rectangle",
|
|
["text", "rectangle", "frame"],
|
|
["rectangle", "text", "frame"],
|
|
);
|
|
await resizingTest(
|
|
"arrow",
|
|
["frame", "arrow", "text"],
|
|
["arrow", "text", "frame"],
|
|
);
|
|
await resizingTest(
|
|
"arrow",
|
|
["text", "arrow", "frame"],
|
|
["arrow", "text", "frame"],
|
|
);
|
|
await resizingTest(
|
|
"arrow",
|
|
["frame", "arrow", "text"],
|
|
["arrow", "text", "frame"],
|
|
);
|
|
|
|
// FIXME failing in tests (it fails to add elements to frame for some
|
|
// reason) but works in browser. (╯°□°)╯︵ ┻━┻
|
|
//
|
|
// Looks like the `getElementsCompletelyInFrame()` doesn't work
|
|
// in these cases.
|
|
//
|
|
// await testElements(
|
|
// "arrow",
|
|
// ["arrow", "text", "frame"],
|
|
// ["arrow", "text", "frame"],
|
|
// );
|
|
});
|
|
|
|
it.skip("should add arrow bound with text when frame is in a layer below", async () => {
|
|
h.elements = [frame, arrow, text];
|
|
|
|
resizeFrameOverElement(frame, arrow);
|
|
|
|
expect(arrow.frameId).toBe(frame.id);
|
|
expect(text.frameId).toBe(frame.id);
|
|
expectEqualIds([arrow, text, frame]);
|
|
});
|
|
|
|
it("should add arrow bound with text when frame is in a layer above", async () => {
|
|
h.elements = [arrow, text, frame];
|
|
|
|
resizeFrameOverElement(frame, arrow);
|
|
|
|
expect(arrow.frameId).toBe(frame.id);
|
|
expect(text.frameId).toBe(frame.id);
|
|
expectEqualIds([arrow, text, frame]);
|
|
});
|
|
|
|
it.skip("should add arrow bound with text when frame is in an inner layer", async () => {
|
|
h.elements = [arrow, frame, text];
|
|
|
|
resizeFrameOverElement(frame, arrow);
|
|
|
|
expect(arrow.frameId).toBe(frame.id);
|
|
expect(text.frameId).toBe(frame.id);
|
|
expectEqualIds([arrow, text, frame]);
|
|
});
|
|
});
|
|
|
|
describe("resizing frame over elements but downwards", async () => {
|
|
it.skip("should add elements when frame is in a layer below", async () => {
|
|
h.elements = [frame, rect1, rect2, rect3, rect4];
|
|
|
|
resizeFrameOverElement(frame, rect4);
|
|
resizeFrameOverElement(frame, rect3);
|
|
|
|
expect(rect2.frameId).toBe(frame.id);
|
|
expect(rect3.frameId).toBe(frame.id);
|
|
expectEqualIds([rect2, rect3, frame, rect4, rect1]);
|
|
});
|
|
|
|
it.skip("should add elements when frame is in a layer above", async () => {
|
|
h.elements = [rect1, rect2, rect3, rect4, frame];
|
|
|
|
resizeFrameOverElement(frame, rect4);
|
|
resizeFrameOverElement(frame, rect3);
|
|
|
|
expect(rect2.frameId).toBe(frame.id);
|
|
expect(rect3.frameId).toBe(frame.id);
|
|
expectEqualIds([rect1, rect2, rect3, frame, rect4]);
|
|
});
|
|
|
|
it.skip("should add elements when frame is in an inner layer", async () => {
|
|
h.elements = [rect1, rect2, frame, rect3, rect4];
|
|
|
|
resizeFrameOverElement(frame, rect4);
|
|
resizeFrameOverElement(frame, rect3);
|
|
|
|
expect(rect2.frameId).toBe(frame.id);
|
|
expect(rect3.frameId).toBe(frame.id);
|
|
expectEqualIds([rect1, rect2, rect3, frame, rect4]);
|
|
});
|
|
});
|
|
|
|
describe("dragging elements into the frame", async () => {
|
|
await commonTestCases(dragElementIntoFrame);
|
|
|
|
it.skip("should drag element inside, duplicate it and keep it in frame", () => {
|
|
h.elements = [frame, rect2];
|
|
|
|
dragElementIntoFrame(frame, rect2);
|
|
|
|
const rect2_copy = { ...rect2, id: `${rect2.id}_copy` };
|
|
|
|
selectElementAndDuplicate(rect2);
|
|
|
|
expect(rect2_copy.frameId).toBe(frame.id);
|
|
expect(rect2.frameId).toBe(frame.id);
|
|
expectEqualIds([rect2_copy, rect2, frame]);
|
|
});
|
|
|
|
it.skip("should drag element inside, duplicate it and remove it from frame", () => {
|
|
h.elements = [frame, rect2];
|
|
|
|
dragElementIntoFrame(frame, rect2);
|
|
|
|
const rect2_copy = { ...rect2, id: `${rect2.id}_copy` };
|
|
|
|
// move the rect2 outside the frame
|
|
selectElementAndDuplicate(rect2, [-1000, -1000]);
|
|
|
|
expect(rect2_copy.frameId).toBe(frame.id);
|
|
expect(rect2.frameId).toBe(null);
|
|
expectEqualIds([rect2_copy, frame, rect2]);
|
|
});
|
|
|
|
it("random order 01", () => {
|
|
const frame1 = API.createElement({
|
|
type: "frame",
|
|
x: 0,
|
|
y: 0,
|
|
width: 100,
|
|
height: 100,
|
|
});
|
|
const frame2 = API.createElement({
|
|
type: "frame",
|
|
x: 200,
|
|
y: 0,
|
|
width: 100,
|
|
height: 100,
|
|
});
|
|
const frame3 = API.createElement({
|
|
type: "frame",
|
|
x: 300,
|
|
y: 0,
|
|
width: 100,
|
|
height: 100,
|
|
});
|
|
|
|
const rectangle1 = API.createElement({
|
|
type: "rectangle",
|
|
x: 25,
|
|
y: 25,
|
|
width: 50,
|
|
height: 50,
|
|
frameId: frame1.id,
|
|
});
|
|
const rectangle2 = API.createElement({
|
|
type: "rectangle",
|
|
x: 225,
|
|
y: 25,
|
|
width: 50,
|
|
height: 50,
|
|
frameId: frame2.id,
|
|
});
|
|
const rectangle3 = API.createElement({
|
|
type: "rectangle",
|
|
x: 325,
|
|
y: 25,
|
|
width: 50,
|
|
height: 50,
|
|
frameId: frame3.id,
|
|
});
|
|
const rectangle4 = API.createElement({
|
|
type: "rectangle",
|
|
x: 350,
|
|
y: 25,
|
|
width: 50,
|
|
height: 50,
|
|
frameId: frame3.id,
|
|
});
|
|
|
|
h.elements = [
|
|
frame1,
|
|
rectangle4,
|
|
rectangle1,
|
|
rectangle3,
|
|
frame3,
|
|
rectangle2,
|
|
frame2,
|
|
];
|
|
|
|
API.setSelectedElements([rectangle2]);
|
|
|
|
const origSize = h.elements.length;
|
|
|
|
expect(h.elements.length).toBe(origSize);
|
|
dragElementIntoFrame(frame3, rectangle2);
|
|
expect(h.elements.length).toBe(origSize);
|
|
});
|
|
|
|
it("random order 02", () => {
|
|
const frame1 = API.createElement({
|
|
type: "frame",
|
|
x: 0,
|
|
y: 0,
|
|
width: 100,
|
|
height: 100,
|
|
});
|
|
const frame2 = API.createElement({
|
|
type: "frame",
|
|
x: 200,
|
|
y: 0,
|
|
width: 100,
|
|
height: 100,
|
|
});
|
|
const rectangle1 = API.createElement({
|
|
type: "rectangle",
|
|
x: 25,
|
|
y: 25,
|
|
width: 50,
|
|
height: 50,
|
|
frameId: frame1.id,
|
|
});
|
|
const rectangle2 = API.createElement({
|
|
type: "rectangle",
|
|
x: 225,
|
|
y: 25,
|
|
width: 50,
|
|
height: 50,
|
|
frameId: frame2.id,
|
|
});
|
|
|
|
h.elements = [rectangle1, rectangle2, frame1, frame2];
|
|
|
|
API.setSelectedElements([rectangle2]);
|
|
|
|
expect(h.elements.length).toBe(4);
|
|
dragElementIntoFrame(frame2, rectangle1);
|
|
expect(h.elements.length).toBe(4);
|
|
});
|
|
});
|
|
});
|