import { describe, expect, test } from "bun:test"; import { MoveElementParameters, movePlugin } from "../plugins/movePlugin"; import { filterElements } from "../utils/elementUtils"; import { createPointerEvent, createTestCanvas } from "./canvasTestUtils"; describe("move plugin", () => { test("enters moving mode and moves elements", () => { const element = { id: "a", rect: { x: 0, y: 0, width: 10, height: 10 } }; const moved: MoveElementParameters<{ snapshot: string }>[] = []; const harness = createTestCanvas( (canvas) => canvas.addPlugins(movePlugin()), { data: { selectedElementIds: ["a"], zoom: 1 }, contextProperties: { getElementId: (item: typeof element) => item.id, isMovable: () => true, filterElements: (options) => filterElements([element], options), getStateSnapshot: () => ({ snapshot: "state" }), moveElements: (params) => moved.push(params), }, } ); harness.handlers.onPointerDown?.(createPointerEvent({ x: 0, y: 0 })); harness.handlers.onPointerMove?.(createPointerEvent({ x: 5, y: 0 })); expect(harness.getMode().type).toBe("moving"); expect(harness.getMode()).toMatchObject({ delta: { x: 5, y: 0 } }); harness.handlers.onPointerMove?.(createPointerEvent({ x: 10, y: 0 })); expect(moved).toEqual([ { elementIds: ["a"], delta: { x: 10, y: 0 }, snapshot: { snapshot: "state" }, options: expect.any(Object), }, ]); harness.handlers.onPointerUp?.(createPointerEvent({ x: 10, y: 0 })); expect(harness.getMode().type).toBe("none"); }); test("respects movement threshold and adjustMoveDelta", () => { const element = { id: "a", rect: { x: 0, y: 0, width: 10, height: 10 } }; const moved: any[] = []; const harness = createTestCanvas((canvas) => canvas.addPlugins(movePlugin({ movementThreshold: 10 })), { data: { selectedElementIds: ["a"], zoom: 1 }, contextProperties: { getElementId: (item: typeof element) => item.id, isMovable: () => true, filterElements: (options) => filterElements([element], options), getStateSnapshot: () => ({ snapshot: "state" }), adjustMoveDelta: (_event, params) => ({ x: params.delta.x * 2, y: params.delta.y * 2 }), moveElements: (params) => moved.push(params), }, }); harness.handlers.onPointerDown?.(createPointerEvent({ x: 0, y: 0 })); harness.handlers.onPointerMove?.(createPointerEvent({ x: 5, y: 0 })); expect(harness.getMode().type).toBe("none"); harness.handlers.onPointerMove?.(createPointerEvent({ x: 20, y: 0 })); expect(harness.getMode()).toMatchObject({ delta: { x: 40, y: 0 } }); harness.handlers.onPointerMove?.(createPointerEvent({ x: 25, y: 0 })); expect(moved[0]).toMatchObject({ delta: { x: 50, y: 0 } }); }); test("duplicates elements when alt is held", () => { const element = { id: "a", rect: { x: 0, y: 0, width: 10, height: 10 } }; const baseSnapshot: typeof element[] = [element]; const duplicatedSnapshot: typeof element[] = [ { ...element, id: "b" }, ]; let duplicateCalls = 0; const harness = createTestCanvas((canvas) => canvas.addPlugins(movePlugin()), { data: { selectedElementIds: ["a"], zoom: 1 }, contextProperties: { getElementId: (item: typeof element) => item.id, isMovable: () => true, filterElements: (options) => filterElements([element], options), getStateSnapshot: () => baseSnapshot, duplicateElements: (_event, options) => { duplicateCalls += 1; expect(options.elementIds).toEqual(["a"]); return { elementIds: ["b"], snapshot: duplicatedSnapshot, }; }, moveElements: () => undefined, }, }); harness.handlers.onPointerDown?.(createPointerEvent({ x: 0, y: 0 })); harness.handlers.onPointerMove?.(createPointerEvent({ x: 5, y: 0, altKey: true })); expect(duplicateCalls).toBe(1); expect(harness.getData().selectedElementIds).toEqual(["b"]); expect(harness.getMode()).toMatchObject({ type: "moving", snapshot: duplicatedSnapshot, }); }); });