107 lines
5.7 KiB
TypeScript
107 lines
5.7 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { createInitialAppState } from "@editor/initial-state";
|
|
import { documentAddArtboardCommand } from "./document";
|
|
import { transformBeginCommand, transformEndCommand, transformSetBoundsCommand, transformSetRotationCommand, transformUpdateCommand } from "./transform";
|
|
import { documentAddAssetCommand, documentAddRasterLayerCommand, documentAddLayerMaskCommand, documentSetLayerLockedCommand } from "./document";
|
|
|
|
function artboardState() {
|
|
return documentAddArtboardCommand.execute(
|
|
{ state: createInitialAppState("Test") },
|
|
{ id: "a1", name: "Artboard", bounds: { x: 0, y: 0, w: 100, h: 80 } },
|
|
);
|
|
}
|
|
|
|
describe("transform commands", () => {
|
|
test("moves artboard transform target", () => {
|
|
const started = transformBeginCommand.execute(
|
|
{ state: artboardState() },
|
|
{ target: { type: "artboard", id: "a1" }, handle: "body", point: { x: 0, y: 0 }, initialBounds: { x: 0, y: 0, w: 100, h: 80 } },
|
|
);
|
|
const updated = transformUpdateCommand.execute({ state: started }, { point: { x: 10, y: 20 } });
|
|
|
|
expect(updated.document.artboards[0]?.bounds).toEqual({ x: 10, y: 20, w: 100, h: 80 });
|
|
});
|
|
|
|
test("resizes artboard from southeast handle", () => {
|
|
const started = transformBeginCommand.execute(
|
|
{ state: artboardState() },
|
|
{ target: { type: "artboard", id: "a1" }, handle: "se", point: { x: 0, y: 0 }, initialBounds: { x: 0, y: 0, w: 100, h: 80 } },
|
|
);
|
|
const updated = transformUpdateCommand.execute({ state: started }, { point: { x: 10, y: 20 } });
|
|
|
|
expect(updated.document.artboards[0]?.bounds).toEqual({ x: 0, y: 0, w: 110, h: 100 });
|
|
});
|
|
|
|
test("shift constrains body moves to the dominant axis", () => {
|
|
const started = transformBeginCommand.execute(
|
|
{ state: artboardState() },
|
|
{ target: { type: "artboard", id: "a1" }, handle: "body", point: { x: 0, y: 0 }, initialBounds: { x: 0, y: 0, w: 100, h: 80 } },
|
|
);
|
|
const updated = transformUpdateCommand.execute({ state: started }, { point: { x: 10, y: 20 }, shiftKey: true });
|
|
|
|
expect(updated.document.artboards[0]?.bounds).toEqual({ x: 0, y: 20, w: 100, h: 80 });
|
|
});
|
|
|
|
test("shift constrains corner resizes to the original proportions", () => {
|
|
const started = transformBeginCommand.execute(
|
|
{ state: artboardState() },
|
|
{ target: { type: "artboard", id: "a1" }, handle: "se", point: { x: 0, y: 0 }, initialBounds: { x: 0, y: 0, w: 100, h: 80 } },
|
|
);
|
|
const updated = transformUpdateCommand.execute({ state: started }, { point: { x: 50, y: 1 }, shiftKey: true });
|
|
|
|
expect(updated.document.artboards[0]?.bounds).toEqual({ x: 0, y: 0, w: 150, h: 120 });
|
|
});
|
|
|
|
test("shift keeps the opposite corner anchored during proportional resize", () => {
|
|
const started = transformBeginCommand.execute(
|
|
{ state: artboardState() },
|
|
{ target: { type: "artboard", id: "a1" }, handle: "nw", point: { x: 0, y: 0 }, initialBounds: { x: 0, y: 0, w: 100, h: 80 } },
|
|
);
|
|
const updated = transformUpdateCommand.execute({ state: started }, { point: { x: -50, y: -1 }, shiftKey: true });
|
|
|
|
expect(updated.document.artboards[0]?.bounds).toEqual({ x: -50, y: -40, w: 150, h: 120 });
|
|
});
|
|
|
|
test("sets transform target bounds directly", () => {
|
|
const updated = transformSetBoundsCommand.execute(
|
|
{ state: artboardState() },
|
|
{ target: { type: "artboard", id: "a1" }, bounds: { x: 10, y: 20, w: 300, h: 200 } },
|
|
);
|
|
|
|
expect(updated.document.artboards[0]?.bounds).toEqual({ x: 10, y: 20, w: 300, h: 200 });
|
|
});
|
|
|
|
test("direct bounds edits enforce minimum size", () => {
|
|
const updated = transformSetBoundsCommand.execute(
|
|
{ state: artboardState() },
|
|
{ target: { type: "artboard", id: "a1" }, bounds: { x: 10, y: 20, w: -5, h: 0 } },
|
|
);
|
|
|
|
expect(updated.document.artboards[0]?.bounds).toEqual({ x: 4, y: 19, w: 1, h: 1 });
|
|
});
|
|
|
|
test("sets leaf rotation and keeps its attached mask aligned", () => {
|
|
let state = artboardState();
|
|
state = documentAddAssetCommand.execute({ state }, { asset: { id: "asset", name: "Asset", mimeType: "image/png", source: "asset", intrinsicSize: { w: 10, h: 10 } } });
|
|
state = documentAddRasterLayerCommand.execute({ state }, { artboardId: "a1", layer: { id: "layer", type: "raster", name: "Layer", visible: true, locked: false, opacity: 1, assetId: "asset", transform: { position: { x: 0, y: 0 }, scale: { x: 1, y: 1 }, rotation: 0 } } });
|
|
state = documentAddLayerMaskCommand.execute({ state }, { layerId: "layer", asset: { id: "mask-asset", name: "Mask", mimeType: "image/png", source: "mask", intrinsicSize: { w: 10, h: 10 } }, maskLayer: { id: "mask", type: "raster", name: "Mask", visible: true, locked: false, opacity: 1, assetId: "mask-asset", transform: { position: { x: 0, y: 0 }, scale: { x: 1, y: 1 }, rotation: 0 } } });
|
|
|
|
const rotated = transformSetRotationCommand.execute({ state }, { target: { type: "layer", id: "layer" }, rotation: Math.PI / 2 });
|
|
expect(rotated.document.artboards[0]?.layers.map((layer) => layer.transform.rotation)).toEqual([Math.PI / 2, Math.PI / 2]);
|
|
|
|
const locked = documentSetLayerLockedCommand.execute({ state: rotated }, { layerId: "layer", locked: true });
|
|
expect(transformSetRotationCommand.execute({ state: locked }, { target: { type: "layer", id: "layer" }, rotation: 0 })).toBe(locked);
|
|
});
|
|
|
|
test("ends transform session", () => {
|
|
const started = transformBeginCommand.execute(
|
|
{ state: artboardState() },
|
|
{ target: { type: "artboard", id: "a1" }, handle: "body", point: { x: 0, y: 0 }, initialBounds: { x: 0, y: 0, w: 100, h: 80 } },
|
|
);
|
|
|
|
const ended = transformEndCommand.execute({ state: started }, undefined);
|
|
|
|
expect(ended.editor.transformSession).toBeUndefined();
|
|
});
|
|
});
|