aboutsummaryrefslogtreecommitdiffstats
path: root/packages/excalidraw/tests/elementLocking.test.tsx
diff options
context:
space:
mode:
authorkj_sh6042026-03-15 16:19:35 -0400
committerkj_sh6042026-03-15 16:19:35 -0400
commit6ec259a0e71174651bae95d4628138bf6fd68742 (patch)
tree5e33c6a5ec091ecabfcb257fdc7b6a88ed8754ac /packages/excalidraw/tests/elementLocking.test.tsx
parent16c8578b15c727f22921f8a80a56ee4d4e7f2272 (diff)
refactor: packages/
Diffstat (limited to 'packages/excalidraw/tests/elementLocking.test.tsx')
-rw-r--r--packages/excalidraw/tests/elementLocking.test.tsx388
1 files changed, 388 insertions, 0 deletions
diff --git a/packages/excalidraw/tests/elementLocking.test.tsx b/packages/excalidraw/tests/elementLocking.test.tsx
new file mode 100644
index 0000000..281c268
--- /dev/null
+++ b/packages/excalidraw/tests/elementLocking.test.tsx
@@ -0,0 +1,388 @@
+import React from "react";
+import { Excalidraw } from "../index";
+import { render, unmountComponent } from "../tests/test-utils";
+import { Keyboard, Pointer, UI } from "../tests/helpers/ui";
+import { KEYS } from "../keys";
+import { API } from "../tests/helpers/api";
+import { actionSelectAll } from "../actions";
+import { t } from "../i18n";
+import { mutateElement } from "../element/mutateElement";
+
+unmountComponent();
+
+const mouse = new Pointer("mouse");
+const h = window.h;
+
+describe("element locking", () => {
+ beforeEach(async () => {
+ await render(<Excalidraw handleKeyboardGlobally={true} />);
+ API.setElements([]);
+ });
+
+ it("click-selecting a locked element is disabled", () => {
+ const lockedRectangle = API.createElement({
+ type: "rectangle",
+ width: 100,
+ backgroundColor: "red",
+ fillStyle: "solid",
+ locked: true,
+ });
+
+ API.setElements([lockedRectangle]);
+
+ mouse.clickAt(50, 50);
+ expect(API.getSelectedElements().length).toBe(0);
+ });
+
+ it("box-selecting a locked element is disabled", () => {
+ const lockedRectangle = API.createElement({
+ type: "rectangle",
+ width: 100,
+ backgroundColor: "red",
+ fillStyle: "solid",
+ locked: true,
+ x: 100,
+ y: 100,
+ });
+
+ API.setElements([lockedRectangle]);
+
+ mouse.downAt(50, 50);
+ mouse.moveTo(250, 250);
+ mouse.upAt(250, 250);
+ expect(API.getSelectedElements().length).toBe(0);
+ });
+
+ it("dragging a locked element is disabled", () => {
+ const lockedRectangle = API.createElement({
+ type: "rectangle",
+ width: 100,
+ backgroundColor: "red",
+ fillStyle: "solid",
+ locked: true,
+ });
+
+ API.setElements([lockedRectangle]);
+
+ mouse.downAt(50, 50);
+ mouse.moveTo(100, 100);
+ mouse.upAt(100, 100);
+ expect(lockedRectangle).toEqual(expect.objectContaining({ x: 0, y: 0 }));
+ });
+
+ it("you can drag element that's below a locked element", () => {
+ const rectangle = API.createElement({
+ type: "rectangle",
+ width: 100,
+ backgroundColor: "red",
+ fillStyle: "solid",
+ });
+ const lockedRectangle = API.createElement({
+ type: "rectangle",
+ width: 100,
+ backgroundColor: "red",
+ fillStyle: "solid",
+ locked: true,
+ });
+
+ API.setElements([rectangle, lockedRectangle]);
+
+ mouse.downAt(50, 50);
+ mouse.moveTo(100, 100);
+ mouse.upAt(100, 100);
+ expect(lockedRectangle).toEqual(expect.objectContaining({ x: 0, y: 0 }));
+ expect(rectangle).toEqual(expect.objectContaining({ x: 50, y: 50 }));
+ expect(API.getSelectedElements().length).toBe(1);
+ expect(API.getSelectedElement().id).toBe(rectangle.id);
+ });
+
+ it("selectAll shouldn't select locked elements", () => {
+ API.setElements([
+ API.createElement({ type: "rectangle" }),
+ API.createElement({ type: "rectangle", locked: true }),
+ ]);
+ API.executeAction(actionSelectAll);
+ expect(API.getSelectedElements().length).toBe(1);
+ });
+
+ it("clicking on a locked element should select the unlocked element beneath it", () => {
+ const rectangle = API.createElement({
+ type: "rectangle",
+ width: 100,
+ backgroundColor: "red",
+ fillStyle: "solid",
+ });
+ const lockedRectangle = API.createElement({
+ type: "rectangle",
+ width: 100,
+ backgroundColor: "red",
+ fillStyle: "solid",
+ locked: true,
+ });
+
+ API.setElements([rectangle, lockedRectangle]);
+ expect(API.getSelectedElements().length).toBe(0);
+ mouse.clickAt(50, 50);
+ expect(API.getSelectedElements().length).toBe(1);
+ expect(API.getSelectedElement().id).toBe(rectangle.id);
+ });
+
+ it("right-clicking on a locked element should select it & open its contextMenu", () => {
+ const rectangle = API.createElement({
+ type: "rectangle",
+ width: 100,
+ backgroundColor: "red",
+ fillStyle: "solid",
+ });
+ const lockedRectangle = API.createElement({
+ type: "rectangle",
+ width: 100,
+ backgroundColor: "red",
+ fillStyle: "solid",
+ locked: true,
+ });
+
+ API.setElements([rectangle, lockedRectangle]);
+ expect(API.getSelectedElements().length).toBe(0);
+ mouse.rightClickAt(50, 50);
+ expect(API.getSelectedElements().length).toBe(1);
+ expect(API.getSelectedElement().id).toBe(lockedRectangle.id);
+
+ const contextMenu = UI.queryContextMenu();
+ expect(contextMenu).not.toBeNull();
+ expect(
+ contextMenu?.querySelector(
+ `li[data-testid="toggleElementLock"] .context-menu-item__label`,
+ ),
+ ).toHaveTextContent(t("labels.elementLock.unlock"));
+ });
+
+ it("right-clicking on element covered by locked element should ignore the locked element", () => {
+ const rectangle = API.createElement({
+ type: "rectangle",
+ width: 100,
+ backgroundColor: "red",
+ fillStyle: "solid",
+ });
+ const lockedRectangle = API.createElement({
+ type: "rectangle",
+ width: 100,
+ backgroundColor: "red",
+ fillStyle: "solid",
+ locked: true,
+ });
+
+ API.setElements([rectangle, lockedRectangle]);
+ API.setSelectedElements([rectangle]);
+ expect(API.getSelectedElements().length).toBe(1);
+ expect(API.getSelectedElement().id).toBe(rectangle.id);
+ mouse.rightClickAt(50, 50);
+ expect(API.getSelectedElements().length).toBe(1);
+ expect(API.getSelectedElement().id).toBe(rectangle.id);
+
+ const contextMenu = UI.queryContextMenu();
+ expect(contextMenu).not.toBeNull();
+ });
+
+ it("selecting a group selects all elements including locked ones", () => {
+ const rectangle = API.createElement({
+ type: "rectangle",
+ width: 100,
+ backgroundColor: "red",
+ fillStyle: "solid",
+ groupIds: ["g1"],
+ });
+ const lockedRectangle = API.createElement({
+ type: "rectangle",
+ width: 100,
+ backgroundColor: "red",
+ fillStyle: "solid",
+ locked: true,
+ groupIds: ["g1"],
+ x: 200,
+ y: 200,
+ });
+
+ API.setElements([rectangle, lockedRectangle]);
+
+ mouse.clickAt(250, 250);
+ expect(API.getSelectedElements().length).toBe(0);
+
+ mouse.clickAt(50, 50);
+ expect(API.getSelectedElements().length).toBe(2);
+ });
+
+ it("should ignore locked text element in center of container on ENTER", () => {
+ const container = API.createElement({
+ type: "rectangle",
+ width: 100,
+ });
+ const textSize = 20;
+ const text = API.createElement({
+ type: "text",
+ text: "ola",
+ x: container.width / 2 - textSize / 2,
+ y: container.height / 2 - textSize / 2,
+ width: textSize,
+ height: textSize,
+ containerId: container.id,
+ locked: true,
+ });
+ API.setElements([container, text]);
+ API.setSelectedElements([container]);
+ Keyboard.keyPress(KEYS.ENTER);
+ expect(h.state.editingTextElement?.id).not.toBe(text.id);
+ expect(h.state.editingTextElement?.id).toBe(h.elements[1].id);
+ });
+
+ it("should ignore locked text under cursor when clicked with text tool", () => {
+ const text = API.createElement({
+ type: "text",
+ text: "ola",
+ x: 60,
+ y: 0,
+ width: 100,
+ height: 100,
+ locked: true,
+ });
+ API.setElements([text]);
+ UI.clickTool("text");
+ mouse.clickAt(text.x + 50, text.y + 50);
+ const editor = document.querySelector(
+ ".excalidraw-textEditorContainer > textarea",
+ ) as HTMLTextAreaElement;
+ expect(editor).not.toBe(null);
+ expect(h.state.editingTextElement?.id).not.toBe(text.id);
+ expect(h.elements.length).toBe(2);
+ expect(h.state.editingTextElement?.id).toBe(h.elements[1].id);
+ });
+
+ it("should ignore text under cursor when double-clicked with selection tool", () => {
+ const text = API.createElement({
+ type: "text",
+ text: "ola",
+ x: 60,
+ y: 0,
+ width: 100,
+ height: 100,
+ locked: true,
+ });
+ API.setElements([text]);
+ UI.clickTool("selection");
+ mouse.doubleClickAt(text.x + 50, text.y + 50);
+ const editor = document.querySelector(
+ ".excalidraw-textEditorContainer > textarea",
+ ) as HTMLTextAreaElement;
+ expect(editor).not.toBe(null);
+ expect(h.state.editingTextElement?.id).not.toBe(text.id);
+ expect(h.elements.length).toBe(2);
+ expect(h.state.editingTextElement?.id).toBe(h.elements[1].id);
+ });
+
+ it("locking should include bound text", () => {
+ const container = API.createElement({
+ type: "rectangle",
+ width: 100,
+ });
+ const textSize = 20;
+ const text = API.createElement({
+ type: "text",
+ text: "ola",
+ x: container.width / 2 - textSize / 2,
+ y: container.height / 2 - textSize / 2,
+ width: textSize,
+ height: textSize,
+ containerId: container.id,
+ });
+ mutateElement(container, {
+ boundElements: [{ id: text.id, type: "text" }],
+ });
+
+ API.setElements([container, text]);
+
+ UI.clickTool("selection");
+ mouse.clickAt(container.x + 10, container.y + 10);
+ Keyboard.withModifierKeys({ ctrl: true, shift: true }, () => {
+ Keyboard.keyPress(KEYS.L);
+ });
+
+ expect(h.elements).toEqual([
+ expect.objectContaining({
+ id: container.id,
+ locked: true,
+ }),
+ expect.objectContaining({
+ id: text.id,
+ locked: true,
+ }),
+ ]);
+ });
+
+ it("bound text shouldn't be editable via double-click", () => {
+ const container = API.createElement({
+ type: "rectangle",
+ width: 100,
+ locked: true,
+ });
+ const textSize = 20;
+ const text = API.createElement({
+ type: "text",
+ text: "ola",
+ x: container.width / 2 - textSize / 2,
+ y: container.height / 2 - textSize / 2,
+ width: textSize,
+ height: textSize,
+ containerId: container.id,
+ locked: true,
+ });
+ mutateElement(container, {
+ boundElements: [{ id: text.id, type: "text" }],
+ });
+ API.setElements([container, text]);
+
+ UI.clickTool("selection");
+ mouse.doubleClickAt(container.width / 2, container.height / 2);
+
+ const editor = document.querySelector(
+ ".excalidraw-textEditorContainer > textarea",
+ ) as HTMLTextAreaElement;
+ expect(editor).not.toBe(null);
+ expect(h.state.editingTextElement?.id).not.toBe(text.id);
+ expect(h.elements.length).toBe(3);
+ expect(h.state.editingTextElement?.id).toBe(h.elements[2].id);
+ });
+
+ it("bound text shouldn't be editable via text tool", () => {
+ const container = API.createElement({
+ type: "rectangle",
+ width: 100,
+ locked: true,
+ });
+ const textSize = 20;
+ const text = API.createElement({
+ type: "text",
+ text: "ola",
+ x: container.width / 2 - textSize / 2,
+ y: container.height / 2 - textSize / 2,
+ width: textSize,
+ height: textSize,
+ containerId: container.id,
+ locked: true,
+ });
+ mutateElement(container, {
+ boundElements: [{ id: text.id, type: "text" }],
+ });
+ API.setElements([container, text]);
+
+ UI.clickTool("text");
+ mouse.clickAt(container.width / 2, container.height / 2);
+
+ const editor = document.querySelector(
+ ".excalidraw-textEditorContainer > textarea",
+ ) as HTMLTextAreaElement;
+ expect(editor).not.toBe(null);
+ expect(h.state.editingTextElement?.id).not.toBe(text.id);
+ expect(h.elements.length).toBe(3);
+ expect(h.state.editingTextElement?.id).toBe(h.elements[2].id);
+ });
+});