import { describe, expect, test } from "bun:test"; import { commandIds } from "@commands/ids"; import type { InputDocument as ImageDocument } from "./read-model"; import { handleArtboardSelection } from "./selection"; import type { PointerInputEvent } from "./pointer"; const ignoredState = undefined as never; const viewport = { center: { x: 0, y: 0 }, zoom: 1, rotation: 0, size: { w: 200, h: 200 } }; describe("selection input", () => { test("selects artboard hit by left click", () => { const document = createDocument({ artboards: [{ id: "a1", name: "Artboard", bounds: { x: -50, y: -50, w: 100, h: 100 }, backgroundColor: "transparent", visible: true, locked: false, layers: [] }], }); const dispatched: unknown[] = []; const consumed = handleArtboardSelection({ event: pointerEvent({ position: { x: 100, y: 100 }, buttons: 1 }), document, viewport, dispatch: (commandId, payload) => { dispatched.push({ commandId, payload }); return ignoredState; }, }); expect(consumed).toBe(true); expect(dispatched).toEqual([{ commandId: commandIds.selectionSet, payload: { artboardId: "a1", layerIds: [] } }]); }); test("selects topmost image layer before artboard", () => { const document = createDocument({ assets: [{ id: "asset-1", name: "Image", mimeType: "image/png", source: "blob:test", intrinsicSize: { w: 50, h: 50 } }], artboards: [ { id: "a1", name: "Artboard", bounds: { x: -100, y: -100, w: 200, h: 200 }, backgroundColor: "transparent", visible: true, locked: false, layers: [ { id: "l1", type: "image", name: "Image", visible: true, locked: false, opacity: 1, assetId: "asset-1", transform: { position: { x: -25, y: -25 }, scale: { x: 1, y: 1 }, rotation: 0 }, }, ], }, ], }); const dispatched: unknown[] = []; const consumed = handleArtboardSelection({ event: pointerEvent({ position: { x: 100, y: 100 }, buttons: 1 }), document, viewport, dispatch: (commandId, payload) => { dispatched.push({ commandId, payload }); return ignoredState; }, }); expect(consumed).toBe(true); expect(dispatched).toEqual([{ commandId: commandIds.selectionSet, payload: { artboardId: "a1", layerIds: ["l1"] } }]); }); test("ignores mask layers during canvas hit testing", () => { const document = createDocument({ assets: [ { id: "target-asset", name: "Target", mimeType: "image/png", source: "target", intrinsicSize: { w: 50, h: 50 } }, { id: "mask-asset", name: "Mask", mimeType: "image/png", source: "mask", intrinsicSize: { w: 50, h: 50 } }, ], artboards: [ { id: "a1", name: "Artboard", bounds: { x: -100, y: -100, w: 200, h: 200 }, backgroundColor: "transparent", visible: true, locked: false, layers: [ { id: "target", type: "image", name: "Target", visible: true, locked: false, opacity: 1, assetId: "target-asset", transform: { position: { x: -25, y: -25 }, scale: { x: 1, y: 1 }, rotation: 0 }, clippingMask: { maskLayerId: "mask" }, }, { id: "mask", type: "raster", name: "Mask", visible: true, locked: false, opacity: 1, assetId: "mask-asset", transform: { position: { x: -25, y: -25 }, scale: { x: 1, y: 1 }, rotation: 0 }, }, ], }, ], }); const dispatched: unknown[] = []; const consumed = handleArtboardSelection({ event: pointerEvent({ position: { x: 100, y: 100 }, buttons: 1 }), document, viewport, dispatch: (commandId, payload) => { dispatched.push({ commandId, payload }); return ignoredState; }, }); expect(consumed).toBe(true); expect(dispatched).toEqual([{ commandId: commandIds.selectionSet, payload: { artboardId: "a1", layerIds: ["target"] } }]); }); test("clears selection when clicking outside artboards", () => { const document = createDocument(); const dispatched: unknown[] = []; const consumed = handleArtboardSelection({ event: pointerEvent({ position: { x: 100, y: 100 }, buttons: 1 }), document, viewport, dispatch: (commandId, payload) => { dispatched.push({ commandId, payload }); return ignoredState; }, }); expect(consumed).toBe(true); expect(dispatched).toEqual([{ commandId: commandIds.selectionClear, payload: undefined }]); }); }); function createDocument(overrides: Partial = {}): ImageDocument { return { id: "d1", name: "Test", version: 1, assets: [], artboards: [], ...overrides, }; } function pointerEvent(overrides: Partial): PointerInputEvent { return { pointerId: 1, pointerType: "mouse", position: { x: 0, y: 0 }, buttons: 0, altKey: false, ctrlKey: false, metaKey: false, shiftKey: false, ...overrides, }; }