resize.test.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import ReactDOM from "react-dom";
  2. import { render } from "./test-utils";
  3. import App from "../components/App";
  4. import * as Renderer from "../renderer/renderScene";
  5. import { reseed } from "../random";
  6. import { UI, Pointer, Keyboard, KeyboardModifiers } from "./helpers/ui";
  7. import {
  8. getTransformHandles,
  9. TransformHandleDirection,
  10. } from "../element/transformHandles";
  11. import { ExcalidrawElement } from "../element/types";
  12. const mouse = new Pointer("mouse");
  13. // Unmount ReactDOM from root
  14. ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
  15. const renderScene = jest.spyOn(Renderer, "renderScene");
  16. beforeEach(() => {
  17. localStorage.clear();
  18. renderScene.mockClear();
  19. reseed(7);
  20. });
  21. const { h } = window;
  22. describe("resize rectangle ellipses and diamond elements", () => {
  23. const elemData = {
  24. x: 0,
  25. y: 0,
  26. width: 100,
  27. height: 100,
  28. };
  29. // Value for irrelevant cursor movements
  30. const _ = 234;
  31. it.each`
  32. handle | move | dimensions | topLeft
  33. ${"n"} | ${[_, -100]} | ${[100, 200]} | ${[elemData.x, -100]}
  34. ${"s"} | ${[_, 39]} | ${[100, 139]} | ${[elemData.x, elemData.x]}
  35. ${"e"} | ${[-20, _]} | ${[80, 100]} | ${[elemData.x, elemData.y]}
  36. ${"w"} | ${[-20, _]} | ${[120, 100]} | ${[-20, elemData.y]}
  37. ${"ne"} | ${[5, 55]} | ${[105, 45]} | ${[elemData.x, 55]}
  38. ${"se"} | ${[-30, -10]} | ${[70, 90]} | ${[elemData.x, elemData.y]}
  39. ${"nw"} | ${[-300, -200]} | ${[400, 300]} | ${[-300, -200]}
  40. ${"sw"} | ${[40, -20]} | ${[60, 80]} | ${[40, 0]}
  41. `(
  42. "resizes with handle $handle",
  43. async ({ handle, move, dimensions, topLeft }) => {
  44. await render(<App />);
  45. const rectangle = UI.createElement("rectangle", elemData);
  46. resize(rectangle, handle, move);
  47. const element = h.elements[0];
  48. expect([element.width, element.height]).toEqual(dimensions);
  49. expect([element.x, element.y]).toEqual(topLeft);
  50. },
  51. );
  52. it.each`
  53. handle | move | dimensions | topLeft
  54. ${"n"} | ${[_, -100]} | ${[200, 200]} | ${[-50, -100]}
  55. ${"nw"} | ${[-300, -200]} | ${[400, 400]} | ${[-300, -300]}
  56. ${"sw"} | ${[40, -20]} | ${[80, 80]} | ${[20, 0]}
  57. `(
  58. "resizes with fixed side ratios on handle $handle",
  59. async ({ handle, move, dimensions, topLeft }) => {
  60. await render(<App />);
  61. const rectangle = UI.createElement("rectangle", elemData);
  62. resize(rectangle, handle, move, { shift: true });
  63. const element = h.elements[0];
  64. expect([element.width, element.height]).toEqual(dimensions);
  65. expect([element.x, element.y]).toEqual(topLeft);
  66. },
  67. );
  68. it.each`
  69. handle | move | dimensions | topLeft
  70. ${"nw"} | ${[0, 120]} | ${[100, 100]} | ${[0, 100]}
  71. ${"ne"} | ${[-120, 0]} | ${[100, 100]} | ${[-100, 0]}
  72. ${"sw"} | ${[200, -200]} | ${[100, 100]} | ${[100, -100]}
  73. ${"n"} | ${[_, 150]} | ${[50, 50]} | ${[25, 100]}
  74. `(
  75. "Flips while resizing and keeping side ratios on handle $handle",
  76. async ({ handle, move, dimensions, topLeft }) => {
  77. await render(<App />);
  78. const rectangle = UI.createElement("rectangle", elemData);
  79. resize(rectangle, handle, move, { shift: true });
  80. const element = h.elements[0];
  81. expect([element.width, element.height]).toEqual(dimensions);
  82. expect([element.x, element.y]).toEqual(topLeft);
  83. },
  84. );
  85. it.each`
  86. handle | move | dimensions | topLeft
  87. ${"ne"} | ${[50, -100]} | ${[200, 300]} | ${[-50, -100]}
  88. ${"s"} | ${[_, -20]} | ${[100, 60]} | ${[0, 20]}
  89. `(
  90. "Resizes from center on handle $handle",
  91. async ({ handle, move, dimensions, topLeft }) => {
  92. await render(<App />);
  93. const rectangle = UI.createElement("rectangle", elemData);
  94. resize(rectangle, handle, move, { alt: true });
  95. const element = h.elements[0];
  96. expect([element.width, element.height]).toEqual(dimensions);
  97. expect([element.x, element.y]).toEqual(topLeft);
  98. },
  99. );
  100. it.each`
  101. handle | move | dimensions | topLeft
  102. ${"nw"} | ${[100, 120]} | ${[140, 140]} | ${[-20, -20]}
  103. ${"e"} | ${[-130, _]} | ${[160, 160]} | ${[-30, -30]}
  104. `(
  105. "Resizes from center, flips and keeps side rations on handle $handle",
  106. async ({ handle, move, dimensions, topLeft }) => {
  107. await render(<App />);
  108. const rectangle = UI.createElement("rectangle", elemData);
  109. resize(rectangle, handle, move, { alt: true, shift: true });
  110. const element = h.elements[0];
  111. expect([element.width, element.height]).toEqual(dimensions);
  112. expect([element.x, element.y]).toEqual(topLeft);
  113. },
  114. );
  115. });
  116. const resize = (
  117. element: ExcalidrawElement,
  118. handleDir: TransformHandleDirection,
  119. mouseMove: [number, number],
  120. keyboardModifiers: KeyboardModifiers = {},
  121. ) => {
  122. mouse.select(element);
  123. const handle = getTransformHandles(element, h.state.zoom, "mouse")[
  124. handleDir
  125. ]!;
  126. const clientX = handle[0] + handle[2] / 2;
  127. const clientY = handle[1] + handle[3] / 2;
  128. Keyboard.withModifierKeys(keyboardModifiers, () => {
  129. mouse.reset();
  130. mouse.down(clientX, clientY);
  131. mouse.move(mouseMove[0], mouseMove[1]);
  132. mouse.up();
  133. });
  134. };