move.test.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. import React from "react";
  2. import ReactDOM from "react-dom";
  3. import { render, fireEvent } from "./test-utils";
  4. import ExcalidrawApp from "../excalidraw-app";
  5. import * as Renderer from "../renderer/renderScene";
  6. import { reseed } from "../random";
  7. import { bindOrUnbindLinearElement } from "../element/binding";
  8. import {
  9. ExcalidrawLinearElement,
  10. NonDeleted,
  11. ExcalidrawRectangleElement,
  12. } from "../element/types";
  13. import { UI, Pointer, Keyboard } from "./helpers/ui";
  14. import { KEYS } from "../keys";
  15. // Unmount ReactDOM from root
  16. ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
  17. const renderScene = jest.spyOn(Renderer, "renderScene");
  18. beforeEach(() => {
  19. localStorage.clear();
  20. renderScene.mockClear();
  21. reseed(7);
  22. });
  23. const { h } = window;
  24. describe("move element", () => {
  25. it("rectangle", async () => {
  26. const { getByToolName, container } = await render(<ExcalidrawApp />);
  27. const canvas = container.querySelector("canvas")!;
  28. {
  29. // create element
  30. const tool = getByToolName("rectangle");
  31. fireEvent.click(tool);
  32. fireEvent.pointerDown(canvas, { clientX: 30, clientY: 20 });
  33. fireEvent.pointerMove(canvas, { clientX: 60, clientY: 70 });
  34. fireEvent.pointerUp(canvas);
  35. expect(renderScene).toHaveBeenCalledTimes(8);
  36. expect(h.state.selectionElement).toBeNull();
  37. expect(h.elements.length).toEqual(1);
  38. expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy();
  39. expect([h.elements[0].x, h.elements[0].y]).toEqual([30, 20]);
  40. renderScene.mockClear();
  41. }
  42. fireEvent.pointerDown(canvas, { clientX: 50, clientY: 20 });
  43. fireEvent.pointerMove(canvas, { clientX: 20, clientY: 40 });
  44. fireEvent.pointerUp(canvas);
  45. expect(renderScene).toHaveBeenCalledTimes(3);
  46. expect(h.state.selectionElement).toBeNull();
  47. expect(h.elements.length).toEqual(1);
  48. expect([h.elements[0].x, h.elements[0].y]).toEqual([0, 40]);
  49. h.elements.forEach((element) => expect(element).toMatchSnapshot());
  50. });
  51. it("rectangles with binding arrow", async () => {
  52. await render(<ExcalidrawApp />);
  53. // create elements
  54. const rectA = UI.createElement("rectangle", { size: 100 });
  55. const rectB = UI.createElement("rectangle", { x: 200, y: 0, size: 300 });
  56. const line = UI.createElement("line", { x: 110, y: 50, size: 80 });
  57. // bind line to two rectangles
  58. bindOrUnbindLinearElement(
  59. line.get() as NonDeleted<ExcalidrawLinearElement>,
  60. rectA.get() as ExcalidrawRectangleElement,
  61. rectB.get() as ExcalidrawRectangleElement,
  62. );
  63. // select the second rectangles
  64. new Pointer("mouse").clickOn(rectB);
  65. expect(renderScene).toHaveBeenCalledTimes(21);
  66. expect(h.state.selectionElement).toBeNull();
  67. expect(h.elements.length).toEqual(3);
  68. expect(h.state.selectedElementIds[rectB.id]).toBeTruthy();
  69. expect([rectA.x, rectA.y]).toEqual([0, 0]);
  70. expect([rectB.x, rectB.y]).toEqual([200, 0]);
  71. expect([line.x, line.y]).toEqual([110, 50]);
  72. expect([line.width, line.height]).toEqual([80, 80]);
  73. renderScene.mockClear();
  74. // Move selected rectangle
  75. Keyboard.keyDown(KEYS.ARROW_RIGHT);
  76. Keyboard.keyDown(KEYS.ARROW_DOWN);
  77. Keyboard.keyDown(KEYS.ARROW_DOWN);
  78. // Check that the arrow size has been changed according to moving the rectangle
  79. expect(renderScene).toHaveBeenCalledTimes(3);
  80. expect(h.state.selectionElement).toBeNull();
  81. expect(h.elements.length).toEqual(3);
  82. expect(h.state.selectedElementIds[rectB.id]).toBeTruthy();
  83. expect([rectA.x, rectA.y]).toEqual([0, 0]);
  84. expect([rectB.x, rectB.y]).toEqual([201, 2]);
  85. expect([Math.round(line.x), Math.round(line.y)]).toEqual([110, 50]);
  86. expect([Math.round(line.width), Math.round(line.height)]).toEqual([81, 81]);
  87. h.elements.forEach((element) => expect(element).toMatchSnapshot());
  88. });
  89. });
  90. describe("duplicate element on move when ALT is clicked", () => {
  91. it("rectangle", async () => {
  92. const { getByToolName, container } = await render(<ExcalidrawApp />);
  93. const canvas = container.querySelector("canvas")!;
  94. {
  95. // create element
  96. const tool = getByToolName("rectangle");
  97. fireEvent.click(tool);
  98. fireEvent.pointerDown(canvas, { clientX: 30, clientY: 20 });
  99. fireEvent.pointerMove(canvas, { clientX: 60, clientY: 70 });
  100. fireEvent.pointerUp(canvas);
  101. expect(renderScene).toHaveBeenCalledTimes(8);
  102. expect(h.state.selectionElement).toBeNull();
  103. expect(h.elements.length).toEqual(1);
  104. expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy();
  105. expect([h.elements[0].x, h.elements[0].y]).toEqual([30, 20]);
  106. renderScene.mockClear();
  107. }
  108. fireEvent.pointerDown(canvas, { clientX: 50, clientY: 20 });
  109. fireEvent.pointerMove(canvas, { clientX: 20, clientY: 40, altKey: true });
  110. // firing another pointerMove event with alt key pressed should NOT trigger
  111. // another duplication
  112. fireEvent.pointerMove(canvas, { clientX: 20, clientY: 40, altKey: true });
  113. fireEvent.pointerMove(canvas, { clientX: 10, clientY: 60 });
  114. fireEvent.pointerUp(canvas);
  115. // TODO: This used to be 4, but binding made it go up to 5. Do we need
  116. // that additional render?
  117. expect(renderScene).toHaveBeenCalledTimes(5);
  118. expect(h.state.selectionElement).toBeNull();
  119. expect(h.elements.length).toEqual(2);
  120. // previous element should stay intact
  121. expect([h.elements[0].x, h.elements[0].y]).toEqual([30, 20]);
  122. expect([h.elements[1].x, h.elements[1].y]).toEqual([-10, 60]);
  123. h.elements.forEach((element) => expect(element).toMatchSnapshot());
  124. });
  125. });