mirror of
https://github.com/igorski/bitmappery.git
synced 2026-06-16 19:25:38 +02:00
291 lines
10 KiB
TypeScript
291 lines
10 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { createMockCanvasElement, createMockZoomableCanvas, createStore, flushPromises } from "../../mocks";
|
|
import DocumentFactory from "@/model/factories/document-factory";
|
|
import LayerFactory from "@/model/factories/layer-factory";
|
|
import LayerRenderer from "@/rendering/actors/layer-renderer";
|
|
import { startLayerDrag } from "@/model/actions/layer-drag-start";
|
|
|
|
const mockEnqueueState = vi.fn();
|
|
vi.mock( "@/model/factories/history-state-factory", () => ({
|
|
enqueueState: ( ...args: any[] ) => mockEnqueueState( ...args ),
|
|
}));
|
|
|
|
const mockGetRendererForLayer = vi.fn();
|
|
vi.mock( "@/model/factories/renderer-factory", () => ({
|
|
getRendererForLayer: vi.fn(( ...args ) => mockGetRendererForLayer( ...args )),
|
|
}));
|
|
|
|
const mockPointerUp = vi.fn();
|
|
const mockPointerDown = vi.fn();
|
|
vi.mock( "@/utils/renderer-util", () => ({
|
|
pointerUp: ( ...args: any[] ) => mockPointerUp( ...args ),
|
|
pointerDown: ( ...args: any[] ) => mockPointerDown( ...args ),
|
|
}));
|
|
|
|
const mockCanvasInstance = createMockZoomableCanvas();
|
|
vi.mock( "@/services/canvas-service", () => ({
|
|
getCanvasInstance: vi.fn(() => mockCanvasInstance ),
|
|
}));
|
|
|
|
vi.mock( "@/utils/canvas-util", async ( importOriginal ) => ({
|
|
...await importOriginal(),
|
|
cloneCanvas: vi.fn( cvs => cvs ),
|
|
}));
|
|
|
|
const mockSelectionContent = createMockCanvasElement();
|
|
const mockUpdatedSourceContent = createMockCanvasElement();
|
|
vi.mock( "@/utils/document-util", () => ({
|
|
copySelection: vi.fn(() => Promise.resolve({
|
|
type: "image",
|
|
content: {
|
|
bitmap: mockSelectionContent,
|
|
},
|
|
})),
|
|
deleteSelectionContent: vi.fn(() => mockUpdatedSourceContent ),
|
|
}));
|
|
|
|
describe( "layer drag start action", () => {
|
|
const layerSource = createMockCanvasElement();
|
|
const layer = LayerFactory.create();
|
|
const activeDocument = DocumentFactory.create({ layers: [ LayerFactory.create(), layer ]});
|
|
activeDocument.activeSelection = [[ { x: 30, y: 20 }, { x: 40, y: 20 }, { x: 40, y: 30 } ]];
|
|
const layerRenderer = new LayerRenderer( layer );
|
|
const store = createStore();
|
|
|
|
beforeEach(() => {
|
|
mockGetRendererForLayer.mockReturnValue( layerRenderer );
|
|
store.getters.activeDocument = activeDocument;
|
|
mockCanvasInstance.draggingSprite = null;
|
|
layer.source = layerSource;
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.resetAllMocks();
|
|
});
|
|
|
|
it( "should set the dragging Sprite reference onto the Zoomable canvas", () => {
|
|
startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
expect( mockCanvasInstance.draggingSprite ).toEqual( layerRenderer );
|
|
});
|
|
|
|
describe( "when there is no selection active", () => {
|
|
it( "should return false to indicate no action was handled", () => {
|
|
const result = startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
expect( result ).toBe( false );
|
|
});
|
|
|
|
it( "should not perform any state changing actions", async () => {
|
|
startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
await flushPromises();
|
|
|
|
expect( store.commit ).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it( "should not enqueue a history state", async () => {
|
|
startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
await flushPromises();
|
|
|
|
expect( mockEnqueueState ).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe( "when there is a selection active", () => {
|
|
beforeEach(() => {
|
|
store.getters.hasSelection = true;
|
|
});
|
|
|
|
it( "should return true to indicate an action was handled", () => {
|
|
const result = startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
expect( result ).toBe( true );
|
|
});
|
|
|
|
it( "should insert a new Layer with the cut Selection content", async () => {
|
|
startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
await flushPromises();
|
|
|
|
expect( store.commit ).toHaveBeenCalledWith( "insertLayerAtIndex", { index: 2, layer: expect.any( Object )});
|
|
|
|
// @ts-expect-error TS2339 Property 'mock' does not exist on type 'Commit'.
|
|
const insertedLayer = store.commit.mock.calls[ 0 ][ 1 ].layer;
|
|
|
|
expect( insertedLayer.source ).toEqual( mockSelectionContent );
|
|
});
|
|
|
|
it( "should position the new Layer to the top-left Selection coordinate", async () => {
|
|
startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
await flushPromises();
|
|
|
|
expect( store.commit ).toHaveBeenCalledWith( "insertLayerAtIndex", { index: 2, layer: expect.any( Object )});
|
|
|
|
// @ts-expect-error TS2339 Property 'mock' does not exist on type 'Commit'.
|
|
const insertedLayer = store.commit.mock.calls[ 0 ][ 1 ].layer;
|
|
|
|
expect( insertedLayer.left ).toEqual( 30 );
|
|
expect( insertedLayer.top ).toEqual( 20 );
|
|
});
|
|
|
|
it( "should reuse the same Layer rel as the source Layer to support timeline Documents", async () => {
|
|
const timelineLayer = { ...layer };
|
|
timelineLayer.rel = {
|
|
type: "tile",
|
|
id: 1,
|
|
};
|
|
startLayerDrag( store, timelineLayer, 10, 10, true );
|
|
|
|
await flushPromises();
|
|
|
|
// @ts-expect-error TS2339 Property 'mock' does not exist on type 'Commit'.
|
|
const insertedLayer = store.commit.mock.calls[ 0 ][ 1 ].layer;
|
|
|
|
expect( insertedLayer.rel ).toEqual({
|
|
type: "tile",
|
|
id: 1,
|
|
});
|
|
});
|
|
|
|
it( "should update the source of the existing Layer renderer with the cut content", async () => {
|
|
startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
await flushPromises();
|
|
|
|
expect( layer.source ).toEqual( mockUpdatedSourceContent );
|
|
});
|
|
|
|
it( "should switch the active dragging states of the original and newly created Layer renderers when triggered from a pointer event", async () => {
|
|
startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
await flushPromises();
|
|
|
|
expect( mockPointerUp ).toHaveBeenCalledWith( layerRenderer, 10, 10 );
|
|
expect( mockPointerDown ).toHaveBeenCalled();
|
|
});
|
|
|
|
it( "should not switch the active dragging states of the original and newly created Layer renderers when not triggered from a pointer event", async () => {
|
|
startLayerDrag( store, layer, 10, 10, false );
|
|
|
|
await flushPromises();
|
|
|
|
expect( mockPointerUp ).not.toHaveBeenCalled();
|
|
expect( mockPointerDown ).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it( "should unset the currently active Selection", async () => {
|
|
startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
await flushPromises();
|
|
|
|
expect( store.commit ).toHaveBeenCalledWith( "setActiveSelection", [] );
|
|
});
|
|
|
|
it( "should enqueue a history state", async () => {
|
|
startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
await flushPromises();
|
|
|
|
expect( mockEnqueueState ).toHaveBeenCalled();
|
|
});
|
|
|
|
describe( "and undo-ing the action", () => {
|
|
it( "should remove the inserted Layer", async () => {
|
|
startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
await flushPromises();
|
|
|
|
const { undo } = mockEnqueueState.mock.calls[ 0 ][ 1 ];
|
|
vi.resetAllMocks();
|
|
|
|
undo();
|
|
|
|
await flushPromises();
|
|
|
|
expect( store.commit ).toHaveBeenCalledWith( "removeLayer", 2 );
|
|
});
|
|
|
|
it( "should update the source of the existing Layer renderer with the original content", async () => {
|
|
startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
await flushPromises();
|
|
|
|
const { undo } = mockEnqueueState.mock.calls[ 0 ][ 1 ];
|
|
vi.resetAllMocks();
|
|
|
|
undo();
|
|
|
|
await flushPromises();
|
|
|
|
expect( layer.source ).toEqual( layerSource );
|
|
});
|
|
|
|
it( "should restore the active Selection", async () => {
|
|
startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
await flushPromises();
|
|
|
|
const { undo } = mockEnqueueState.mock.calls[ 0 ][ 1 ];
|
|
vi.resetAllMocks();
|
|
|
|
undo();
|
|
|
|
await flushPromises();
|
|
|
|
expect( store.commit ).toHaveBeenCalledWith( "setActiveSelection", activeDocument.activeSelection );
|
|
});
|
|
|
|
it( "should unset the active dragging states of the newly created Layer renderer when triggered from a pointer event", async () => {
|
|
startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
await flushPromises();
|
|
|
|
const { undo } = mockEnqueueState.mock.calls[ 0 ][ 1 ];
|
|
vi.resetAllMocks();
|
|
|
|
undo();
|
|
|
|
await flushPromises();
|
|
|
|
expect( mockPointerUp ).toHaveBeenCalled();
|
|
});
|
|
|
|
it( "should not unset the active dragging states of the newly created Layer renderer when not triggered from a pointer event", async () => {
|
|
startLayerDrag( store, layer, 10, 10, false );
|
|
|
|
await flushPromises();
|
|
|
|
const { undo } = mockEnqueueState.mock.calls[ 0 ][ 1 ];
|
|
vi.resetAllMocks();
|
|
|
|
undo();
|
|
|
|
await flushPromises();
|
|
|
|
expect( mockPointerUp ).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe( "and redo-ing the action", () => {
|
|
it( "should not invoke the pointer down event on the recreated Layer", async () => {
|
|
startLayerDrag( store, layer, 10, 10, true );
|
|
|
|
await flushPromises();
|
|
|
|
const { undo, redo } = mockEnqueueState.mock.calls[ 0 ][ 1 ];
|
|
|
|
undo();
|
|
vi.resetAllMocks();
|
|
|
|
redo();
|
|
|
|
await flushPromises();
|
|
|
|
expect( mockPointerDown ).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|
|
}); |