From 18febfeaf2e49691cc1668805bc1a1c59396ac51 Mon Sep 17 00:00:00 2001 From: Ryan Di Date: Tue, 28 Apr 2026 16:02:13 +1000 Subject: [PATCH] test(linear): cover line snapping interactions --- .../tests/linearElementEditor.test.tsx | 92 ++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/packages/element/tests/linearElementEditor.test.tsx b/packages/element/tests/linearElementEditor.test.tsx index 45be3b76e6..95feeb933c 100644 --- a/packages/element/tests/linearElementEditor.test.tsx +++ b/packages/element/tests/linearElementEditor.test.tsx @@ -155,6 +155,24 @@ describe("Test Linear Elements", () => { }); }; + const dragMove = (startPoint: GlobalPoint, endPoint: GlobalPoint) => { + fireEvent.pointerDown(interactiveCanvas, { + clientX: startPoint[0], + clientY: startPoint[1], + }); + fireEvent.pointerMove(interactiveCanvas, { + clientX: endPoint[0], + clientY: endPoint[1], + }); + }; + + const dragEnd = (endPoint: GlobalPoint) => { + fireEvent.pointerUp(interactiveCanvas, { + clientX: endPoint[0], + clientY: endPoint[1], + }); + }; + const deletePoint = (point: GlobalPoint) => { fireEvent.pointerDown(interactiveCanvas, { clientX: point[0], @@ -258,6 +276,43 @@ describe("Test Linear Elements", () => { expect(h.state.selectedLinearElement?.elementId).toEqual(h.elements[0].id); }); + it("shows snap lines and snaps the endpoint when creating a line", () => { + const rect = API.createElement({ + type: "rectangle", + x: 100, + y: 100, + width: 40, + height: 40, + }); + API.setElements([rect]); + API.setAppState({ objectsSnapModeEnabled: true }); + + UI.clickTool("line"); + + const startPoint = pointFrom(20, 20); + const pointerNearCorner = pointFrom(95, 95); + + dragMove(startPoint, pointerNearCorner); + + expect(h.state.snapLines.length).toBeGreaterThan(0); + + dragEnd(pointerNearCorner); + + const line = h.elements.find( + (element): element is ExcalidrawLinearElement => element.type === "line", + ); + + expect(line).toBeDefined(); + + const endpoint = LinearElementEditor.getPointGlobalCoordinates( + line!, + line!.points[line!.points.length - 1], + h.app.scene.getNonDeletedElementsMap(), + ); + + expect(endpoint).toEqual(pointFrom(100, 100)); + }); + it("should enter line editor via enter (line)", () => { createTwoPointerLinearElement("line"); expect(h.state.selectedLinearElement?.isEditing).toBe(false); @@ -401,6 +456,39 @@ describe("Test Linear Elements", () => { `); }); + it("shows snap lines when dragging a point to another line point axis", () => { + const line = API.createElement({ + type: "line", + x: 20, + y: 20, + width: 100, + height: 50, + roughness: 0, + points: [ + pointFrom(0, 0), + pointFrom(50, 50), + pointFrom(100, 0), + ], + }); + + API.setElements([line]); + API.setAppState({ objectsSnapModeEnabled: true }); + enterLineEditingMode(line); + + const middlePoint = pointFrom(70, 70); + const pointerNearEndPointX = pointFrom(117, 65); + + dragMove(middlePoint, pointerNearEndPointX); + + expect(h.state.snapLines.length).toBeGreaterThan(0); + + dragEnd(pointerNearEndPointX); + + expect(API.getElement(line).points[1]).toEqual( + pointFrom(100, 45), + ); + }); + it("should update the midpoints when element roundness changed", async () => { createThreePointerLinearElement("line"); @@ -548,7 +636,7 @@ describe("Test Linear Elements", () => { ); expect(renderInteractiveScene.mock.calls.length).toMatchInlineSnapshot( - `16`, + `15`, ); expect(renderStaticScene.mock.calls.length).toMatchInlineSnapshot(`9`); @@ -746,7 +834,7 @@ describe("Test Linear Elements", () => { ), ); expect(renderInteractiveScene.mock.calls.length).toMatchInlineSnapshot( - `16`, + `15`, ); expect(renderStaticScene.mock.calls.length).toMatchInlineSnapshot(`9`); expect(line.points.length).toEqual(5);