Sfoglia il codice sorgente

test: add more specs for line editor segment midpoints (#5698)

* tests: add more specs for line editor segment midpoints

* use API to create elements

* Add specs for checking midpoint hidden when points too close
Aakansha Doshi 2 anni fa
parent
commit
5390617c01

+ 0 - 60
src/tests/__snapshots__/linearElementEditor.test.tsx.snap

@@ -1,60 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[` Test Linear Elements Inside editor should allow dragging line from midpoint in 2 pointer lines 1`] = `
-Array [
-  Array [
-    0,
-    0,
-  ],
-  Array [
-    70,
-    50,
-  ],
-  Array [
-    40,
-    0,
-  ],
-]
-`;
-
-exports[` Test Linear Elements Inside editor should allow dragging lines from midpoints in between segments 1`] = `
-Array [
-  Array [
-    0,
-    0,
-  ],
-  Array [
-    85,
-    75,
-  ],
-  Array [
-    70,
-    50,
-  ],
-  Array [
-    105,
-    75,
-  ],
-  Array [
-    40,
-    0,
-  ],
-]
-`;
-
-exports[` Test Linear Elements should allow dragging line from midpoint in 2 pointer lines outside editor 1`] = `
-Array [
-  Array [
-    0,
-    0,
-  ],
-  Array [
-    70,
-    50,
-  ],
-  Array [
-    40,
-    0,
-  ],
-]
-`;

+ 488 - 63
src/tests/linearElementEditor.test.tsx

@@ -4,10 +4,9 @@ import ExcalidrawApp from "../excalidraw-app";
 import { centerPoint } from "../math";
 import { reseed } from "../random";
 import * as Renderer from "../renderer/renderScene";
-import { Keyboard } from "./helpers/ui";
-import { screen } from "./test-utils";
-
-import { render, fireEvent } from "./test-utils";
+import { Keyboard, Pointer } from "./helpers/ui";
+import { screen, render, fireEvent } from "./test-utils";
+import { API } from "../tests/helpers/api";
 import { Point } from "../types";
 import { KEYS } from "../keys";
 import { LinearElementEditor } from "../element/linearElementEditor";
@@ -17,7 +16,6 @@ const renderScene = jest.spyOn(Renderer, "renderScene");
 const { h } = window;
 
 describe(" Test Linear Elements", () => {
-  let getByToolName: (...args: string[]) => HTMLElement;
   let container: HTMLElement;
   let canvas: HTMLCanvasElement;
 
@@ -28,119 +26,546 @@ describe(" Test Linear Elements", () => {
     renderScene.mockClear();
     reseed(7);
     const comp = await render(<ExcalidrawApp />);
-    getByToolName = comp.getByToolName;
     container = comp.container;
     canvas = container.querySelector("canvas")!;
+    canvas.width = 1000;
+    canvas.height = 1000;
   });
 
   const p1: Point = [20, 20];
   const p2: Point = [60, 20];
   const midpoint = centerPoint(p1, p2);
+  const delta = 50;
+  const mouse = new Pointer("mouse");
 
   const createTwoPointerLinearElement = (
     type: ExcalidrawLinearElement["type"],
-    edge: "Sharp" | "Round" = "Sharp",
-    roughness: "Architect" | "Cartoonist" | "Artist" = "Architect",
+    strokeSharpness: ExcalidrawLinearElement["strokeSharpness"] = "sharp",
+    roughness: ExcalidrawLinearElement["roughness"] = 0,
   ) => {
-    const tool = getByToolName(type);
-    fireEvent.click(tool);
-    fireEvent.click(screen.getByTitle(edge));
-    fireEvent.click(screen.getByTitle(roughness));
-    fireEvent.pointerDown(canvas, { clientX: p1[0], clientY: p1[1] });
-    fireEvent.pointerMove(canvas, { clientX: p2[0], clientY: p2[1] });
-    fireEvent.pointerUp(canvas, { clientX: p2[0], clientY: p2[1] });
+    h.elements = [
+      API.createElement({
+        x: p1[0],
+        y: p1[1],
+        width: p2[0] - p1[0],
+        height: 0,
+        type,
+        roughness,
+        points: [
+          [0, 0],
+          [p2[0] - p1[0], p2[1] - p1[1]],
+        ],
+        strokeSharpness,
+      }),
+    ];
+
+    mouse.clickAt(p1[0], p1[1]);
   };
 
   const createThreePointerLinearElement = (
     type: ExcalidrawLinearElement["type"],
-    edge: "Sharp" | "Round" = "Sharp",
+    strokeSharpness: ExcalidrawLinearElement["strokeSharpness"] = "sharp",
+    roughness: ExcalidrawLinearElement["roughness"] = 0,
   ) => {
-    createTwoPointerLinearElement("line");
-    // Extending line via midpoint
+    //dragging line from midpoint
+    const p3 = [midpoint[0] + delta - p1[0], midpoint[1] + delta - p1[1]];
+    h.elements = [
+      API.createElement({
+        x: p1[0],
+        y: p1[1],
+        width: p3[0] - p1[0],
+        height: 0,
+        type,
+        roughness,
+        points: [
+          [0, 0],
+          [p3[0], p3[1]],
+          [p2[0] - p1[0], p2[1] - p1[1]],
+        ],
+        strokeSharpness,
+      }),
+    ];
+    mouse.clickAt(p1[0], p1[1]);
+  };
+
+  const enterLineEditingMode = (line: ExcalidrawLinearElement) => {
+    mouse.clickAt(p1[0], p1[1]);
+    Keyboard.keyPress(KEYS.ENTER);
+    expect(h.state.editingLinearElement?.elementId).toEqual(line.id);
+  };
+
+  const drag = (startPoint: Point, endPoint: Point) => {
     fireEvent.pointerDown(canvas, {
-      clientX: midpoint[0],
-      clientY: midpoint[1],
+      clientX: startPoint[0],
+      clientY: startPoint[1],
     });
     fireEvent.pointerMove(canvas, {
-      clientX: midpoint[0] + 50,
-      clientY: midpoint[1] + 50,
+      clientX: endPoint[0],
+      clientY: endPoint[1],
     });
     fireEvent.pointerUp(canvas, {
-      clientX: midpoint[0] + 50,
-      clientY: midpoint[1] + 50,
+      clientX: endPoint[0],
+      clientY: endPoint[1],
     });
   };
 
-  const dragLinearElementFromPoint = (point: Point) => {
+  const deletePoint = (point: Point) => {
     fireEvent.pointerDown(canvas, {
       clientX: point[0],
       clientY: point[1],
     });
-    fireEvent.pointerMove(canvas, {
-      clientX: point[0] + 50,
-      clientY: point[1] + 50,
-    });
     fireEvent.pointerUp(canvas, {
-      clientX: point[0] + 50,
-      clientY: point[1] + 50,
+      clientX: point[0],
+      clientY: point[1],
     });
+    Keyboard.keyPress(KEYS.DELETE);
   };
 
   it("should allow dragging line from midpoint in 2 pointer lines outside editor", async () => {
     createTwoPointerLinearElement("line");
     const line = h.elements[0] as ExcalidrawLinearElement;
 
-    expect(renderScene).toHaveBeenCalledTimes(10);
+    expect(renderScene).toHaveBeenCalledTimes(6);
     expect((h.elements[0] as ExcalidrawLinearElement).points.length).toEqual(2);
 
     // drag line from midpoint
-    dragLinearElementFromPoint(midpoint);
-    expect(renderScene).toHaveBeenCalledTimes(13);
+    drag(midpoint, [midpoint[0] + delta, midpoint[1] + delta]);
+    expect(renderScene).toHaveBeenCalledTimes(9);
     expect(line.points.length).toEqual(3);
-    expect(line.points).toMatchSnapshot();
+    expect(line.points).toMatchInlineSnapshot(`
+      Array [
+        Array [
+          0,
+          0,
+        ],
+        Array [
+          70,
+          50,
+        ],
+        Array [
+          40,
+          0,
+        ],
+      ]
+    `);
   });
 
   describe("Inside editor", () => {
     it("should allow dragging line from midpoint in 2 pointer lines", async () => {
       createTwoPointerLinearElement("line");
-      const line = h.elements[0] as ExcalidrawLinearElement;
-
-      fireEvent.click(canvas, { clientX: p1[0], clientY: p1[1] });
 
-      Keyboard.keyPress(KEYS.ENTER);
-      expect(h.state.editingLinearElement?.elementId).toEqual(h.elements[0].id);
+      const line = h.elements[0] as ExcalidrawLinearElement;
+      enterLineEditingMode(line);
 
       // drag line from midpoint
-      dragLinearElementFromPoint(midpoint);
+      drag(midpoint, [midpoint[0] + delta, midpoint[1] + delta]);
+      expect(renderScene).toHaveBeenCalledTimes(13);
+
       expect(line.points.length).toEqual(3);
-      expect(line.points).toMatchSnapshot();
+      expect(line.points).toMatchInlineSnapshot(`
+        Array [
+          Array [
+            0,
+            0,
+          ],
+          Array [
+            70,
+            50,
+          ],
+          Array [
+            40,
+            0,
+          ],
+        ]
+      `);
     });
 
-    it("should allow dragging lines from midpoints in between segments", async () => {
+    it("should update the midpoints when element sharpness changed", async () => {
       createThreePointerLinearElement("line");
 
       const line = h.elements[0] as ExcalidrawLinearElement;
       expect(line.points.length).toEqual(3);
-      fireEvent.click(canvas, { clientX: p1[0], clientY: p1[1] });
-
-      Keyboard.keyPress(KEYS.ENTER);
-      expect(h.state.editingLinearElement?.elementId).toEqual(h.elements[0].id);
-
-      let points = LinearElementEditor.getPointsGlobalCoordinates(line);
-      const firstSegmentMidpoint = centerPoint(points[0], points[1]);
-      // drag line via first segment midpoint
-      dragLinearElementFromPoint(firstSegmentMidpoint);
-      expect(line.points.length).toEqual(4);
-
-      // drag line from last segment midpoint
-      points = LinearElementEditor.getPointsGlobalCoordinates(line);
-      const lastSegmentMidpoint = centerPoint(points.at(-2)!, points.at(-1)!);
-      dragLinearElementFromPoint(lastSegmentMidpoint);
-      expect(line.points.length).toEqual(5);
-
-      expect(
-        (h.elements[0] as ExcalidrawLinearElement).points,
-      ).toMatchSnapshot();
+
+      enterLineEditingMode(line);
+
+      const midPointsWithSharpEdge = LinearElementEditor.getEditorMidPoints(
+        line,
+        h.state,
+      );
+
+      // update sharpness
+      fireEvent.click(screen.getByTitle("Round"));
+
+      expect(renderScene).toHaveBeenCalledTimes(11);
+      const midPointsWithRoundEdge = LinearElementEditor.getEditorMidPoints(
+        h.elements[0] as ExcalidrawLinearElement,
+        h.state,
+      );
+      expect(midPointsWithRoundEdge[0]).not.toEqual(midPointsWithSharpEdge[0]);
+      expect(midPointsWithRoundEdge[1]).not.toEqual(midPointsWithSharpEdge[1]);
+
+      expect(midPointsWithRoundEdge).toMatchInlineSnapshot(`
+        Array [
+          Array [
+            55.9697848965255,
+            47.442326230998205,
+          ],
+          Array [
+            76.08587175006699,
+            43.294165939653226,
+          ],
+        ]
+      `);
+    });
+
+    it("should update all the midpoints when element position changed", async () => {
+      createThreePointerLinearElement("line", "round");
+
+      const line = h.elements[0] as ExcalidrawLinearElement;
+      expect(line.points.length).toEqual(3);
+      enterLineEditingMode(line);
+
+      const points = LinearElementEditor.getPointsGlobalCoordinates(line);
+      expect([line.x, line.y]).toEqual(points[0]);
+
+      const midPoints = LinearElementEditor.getEditorMidPoints(line, h.state);
+
+      const startPoint = centerPoint(points[0], midPoints[0] as Point);
+      const deltaX = 50;
+      const deltaY = 20;
+      const endPoint: Point = [startPoint[0] + deltaX, startPoint[1] + deltaY];
+
+      // Move the element
+      drag(startPoint, endPoint);
+
+      expect(renderScene).toHaveBeenCalledTimes(14);
+      expect([line.x, line.y]).toEqual([
+        points[0][0] + deltaX,
+        points[0][1] + deltaY,
+      ]);
+
+      const newMidPoints = LinearElementEditor.getEditorMidPoints(
+        line,
+        h.state,
+      );
+      expect(midPoints[0]).not.toEqual(newMidPoints[0]);
+      expect(midPoints[1]).not.toEqual(newMidPoints[1]);
+      expect(newMidPoints).toMatchInlineSnapshot(`
+        Array [
+          Array [
+            105.96978489652551,
+            67.4423262309982,
+          ],
+          Array [
+            126.08587175006699,
+            63.294165939653226,
+          ],
+        ]
+      `);
+    });
+
+    describe("When edges are sharp", () => {
+      // This is the expected midpoint for line with sharp edge
+      // hence hardcoding it so if later some bug is introduced
+      // this will fail and we can fix it
+      const firstSegmentMidpoint: Point = [55, 45];
+      const lastSegmentMidpoint: Point = [75, 40];
+
+      let line: ExcalidrawLinearElement;
+
+      beforeEach(() => {
+        createThreePointerLinearElement("line");
+        line = h.elements[0] as ExcalidrawLinearElement;
+        expect(line.points.length).toEqual(3);
+
+        enterLineEditingMode(line);
+      });
+
+      it("should allow dragging lines from midpoints in between segments", async () => {
+        // drag line via first segment midpoint
+        drag(firstSegmentMidpoint, [
+          firstSegmentMidpoint[0] + delta,
+          firstSegmentMidpoint[1] + delta,
+        ]);
+        expect(line.points.length).toEqual(4);
+
+        // drag line from last segment midpoint
+        drag(lastSegmentMidpoint, [
+          lastSegmentMidpoint[0] + delta,
+          lastSegmentMidpoint[1] + delta,
+        ]);
+
+        expect(renderScene).toHaveBeenCalledTimes(18);
+        expect(line.points.length).toEqual(5);
+
+        expect((h.elements[0] as ExcalidrawLinearElement).points)
+          .toMatchInlineSnapshot(`
+          Array [
+            Array [
+              0,
+              0,
+            ],
+            Array [
+              85,
+              75,
+            ],
+            Array [
+              70,
+              50,
+            ],
+            Array [
+              105,
+              75,
+            ],
+            Array [
+              40,
+              0,
+            ],
+          ]
+        `);
+      });
+
+      it("should update only the first segment midpoint when its point is dragged", async () => {
+        const points = LinearElementEditor.getPointsGlobalCoordinates(line);
+        const midPoints = LinearElementEditor.getEditorMidPoints(line, h.state);
+
+        const hitCoords: Point = [points[0][0], points[0][1]];
+
+        // Drag from first point
+        drag(hitCoords, [hitCoords[0] - delta, hitCoords[1] - delta]);
+
+        expect(renderScene).toHaveBeenCalledTimes(14);
+
+        const newPoints = LinearElementEditor.getPointsGlobalCoordinates(line);
+        expect([newPoints[0][0], newPoints[0][1]]).toEqual([
+          points[0][0] - delta,
+          points[0][1] - delta,
+        ]);
+
+        const newMidPoints = LinearElementEditor.getEditorMidPoints(
+          line,
+          h.state,
+        );
+
+        expect(midPoints[0]).not.toEqual(newMidPoints[0]);
+        expect(midPoints[1]).toEqual(newMidPoints[1]);
+      });
+
+      it("should hide midpoints in the segment when points moved close", async () => {
+        const points = LinearElementEditor.getPointsGlobalCoordinates(line);
+        const midPoints = LinearElementEditor.getEditorMidPoints(line, h.state);
+
+        const hitCoords: Point = [points[0][0], points[0][1]];
+
+        // Drag from first point
+        drag(hitCoords, [hitCoords[0] + delta, hitCoords[1] + delta]);
+
+        expect(renderScene).toHaveBeenCalledTimes(14);
+
+        const newPoints = LinearElementEditor.getPointsGlobalCoordinates(line);
+        expect([newPoints[0][0], newPoints[0][1]]).toEqual([
+          points[0][0] + delta,
+          points[0][1] + delta,
+        ]);
+
+        const newMidPoints = LinearElementEditor.getEditorMidPoints(
+          line,
+          h.state,
+        );
+        // This midpoint is hidden since the points are too close
+        expect(newMidPoints[0]).toBeNull();
+        expect(midPoints[1]).toEqual(newMidPoints[1]);
+      });
+
+      it("should remove the midpoint when one of the points in the segment is deleted", async () => {
+        const line = h.elements[0] as ExcalidrawLinearElement;
+        enterLineEditingMode(line);
+        const points = LinearElementEditor.getPointsGlobalCoordinates(line);
+
+        // dragging line from last segment midpoint
+        drag(lastSegmentMidpoint, [
+          lastSegmentMidpoint[0] + 50,
+          lastSegmentMidpoint[1] + 50,
+        ]);
+        expect(line.points.length).toEqual(4);
+
+        const midPoints = LinearElementEditor.getEditorMidPoints(line, h.state);
+
+        // delete 3rd point
+        deletePoint(points[2]);
+        expect(line.points.length).toEqual(3);
+        expect(renderScene).toHaveBeenCalledTimes(19);
+
+        const newMidPoints = LinearElementEditor.getEditorMidPoints(
+          line,
+          h.state,
+        );
+        expect(newMidPoints.length).toEqual(2);
+        expect(midPoints[0]).toEqual(newMidPoints[0]);
+        expect(midPoints[1]).toEqual(newMidPoints[1]);
+      });
+    });
+
+    describe("When edges are round", () => {
+      // This is the expected midpoint for line with round edge
+      // hence hardcoding it so if later some bug is introduced
+      // this will fail and we can fix it
+      const firstSegmentMidpoint: Point = [
+        55.9697848965255, 47.442326230998205,
+      ];
+      const lastSegmentMidpoint: Point = [
+        76.08587175006699, 43.294165939653226,
+      ];
+      let line: ExcalidrawLinearElement;
+
+      beforeEach(() => {
+        createThreePointerLinearElement("line", "round");
+        line = h.elements[0] as ExcalidrawLinearElement;
+        expect(line.points.length).toEqual(3);
+
+        enterLineEditingMode(line);
+      });
+
+      it("should allow dragging lines from midpoints in between segments", async () => {
+        // drag line from first segment midpoint
+        drag(firstSegmentMidpoint, [
+          firstSegmentMidpoint[0] + delta,
+          firstSegmentMidpoint[1] + delta,
+        ]);
+        expect(line.points.length).toEqual(4);
+
+        // drag line from last segment midpoint
+        drag(lastSegmentMidpoint, [
+          lastSegmentMidpoint[0] + delta,
+          lastSegmentMidpoint[1] + delta,
+        ]);
+        expect(renderScene).toHaveBeenCalledTimes(18);
+
+        expect(line.points.length).toEqual(5);
+
+        expect((h.elements[0] as ExcalidrawLinearElement).points)
+          .toMatchInlineSnapshot(`
+          Array [
+            Array [
+              0,
+              0,
+            ],
+            Array [
+              85.96978489652551,
+              77.4423262309982,
+            ],
+            Array [
+              70,
+              50,
+            ],
+            Array [
+              104.58050066266131,
+              74.24758482724201,
+            ],
+            Array [
+              40,
+              0,
+            ],
+          ]
+        `);
+      });
+
+      it("should update all the midpoints when its point is dragged", async () => {
+        const points = LinearElementEditor.getPointsGlobalCoordinates(line);
+        const midPoints = LinearElementEditor.getEditorMidPoints(line, h.state);
+
+        const hitCoords: Point = [points[0][0], points[0][1]];
+
+        // Drag from first point
+        drag(hitCoords, [hitCoords[0] - delta, hitCoords[1] - delta]);
+
+        const newPoints = LinearElementEditor.getPointsGlobalCoordinates(line);
+        expect([newPoints[0][0], newPoints[0][1]]).toEqual([
+          points[0][0] - delta,
+          points[0][1] - delta,
+        ]);
+
+        const newMidPoints = LinearElementEditor.getEditorMidPoints(
+          line,
+          h.state,
+        );
+
+        expect(midPoints[0]).not.toEqual(newMidPoints[0]);
+        expect(midPoints[1]).not.toEqual(newMidPoints[1]);
+        expect(newMidPoints).toMatchInlineSnapshot(`
+          Array [
+            Array [
+              31.884084517616053,
+              23.13275505472383,
+            ],
+            Array [
+              77.74792546875662,
+              44.57840982272327,
+            ],
+          ]
+        `);
+      });
+
+      it("should hide midpoints in the segment when points moved close", async () => {
+        const points = LinearElementEditor.getPointsGlobalCoordinates(line);
+        const midPoints = LinearElementEditor.getEditorMidPoints(line, h.state);
+
+        const hitCoords: Point = [points[0][0], points[0][1]];
+
+        // Drag from first point
+        drag(hitCoords, [hitCoords[0] + delta, hitCoords[1] + delta]);
+
+        expect(renderScene).toHaveBeenCalledTimes(14);
+
+        const newPoints = LinearElementEditor.getPointsGlobalCoordinates(line);
+        expect([newPoints[0][0], newPoints[0][1]]).toEqual([
+          points[0][0] + delta,
+          points[0][1] + delta,
+        ]);
+
+        const newMidPoints = LinearElementEditor.getEditorMidPoints(
+          line,
+          h.state,
+        );
+        // This mid point is hidden due to point being too close
+        expect(newMidPoints[0]).toBeNull();
+        expect(newMidPoints[1]).not.toEqual(midPoints[1]);
+      });
+
+      it("should update all the midpoints when a point is deleted", async () => {
+        drag(lastSegmentMidpoint, [
+          lastSegmentMidpoint[0] + delta,
+          lastSegmentMidpoint[1] + delta,
+        ]);
+        expect(line.points.length).toEqual(4);
+
+        const midPoints = LinearElementEditor.getEditorMidPoints(line, h.state);
+        const points = LinearElementEditor.getPointsGlobalCoordinates(line);
+
+        // delete 3rd point
+        deletePoint(points[2]);
+        expect(line.points.length).toEqual(3);
+
+        const newMidPoints = LinearElementEditor.getEditorMidPoints(
+          line,
+          h.state,
+        );
+        expect(newMidPoints.length).toEqual(2);
+        expect(midPoints[0]).not.toEqual(newMidPoints[0]);
+        expect(midPoints[1]).not.toEqual(newMidPoints[1]);
+        expect(newMidPoints).toMatchInlineSnapshot(`
+          Array [
+            Array [
+              55.9697848965255,
+              47.442326230998205,
+            ],
+            Array [
+              76.08587175006699,
+              43.294165939653226,
+            ],
+          ]
+        `);
+      });
     });
   });
 });