mirror of
https://github.com/igorski/bitmappery.git
synced 2026-06-17 03:34:56 +02:00
Split `definitions/document.ts` into unique files per actor type. Create `model` folder to store the above types, their factories and the state changing actions.
136 lines
5.6 KiB
TypeScript
136 lines
5.6 KiB
TypeScript
import { it, afterEach, beforeEach, describe, expect, vi } from "vitest";
|
|
import { createMockCanvasElement, mockZCanvas } from "../../mocks";
|
|
|
|
mockZCanvas();
|
|
|
|
import { type Layer } from "@/model/types/layer";
|
|
import LayerFactory from "@/model/factories/layer-factory";
|
|
import LayerRenderer from "@/rendering/actors/layer-renderer";
|
|
import { replaceLayerSource } from "@/model/actions/layer-source-replace";
|
|
|
|
const mockGetRendererForLayer = vi.fn();
|
|
vi.mock( "@/model/factories/renderer-factory", () => ({
|
|
getRendererForLayer: vi.fn(( ...args ) => mockGetRendererForLayer( ...args )),
|
|
}));
|
|
|
|
const mockEnqueueState = vi.fn();
|
|
vi.mock( "@/model/factories/history-state-factory", () => ({
|
|
enqueueState: vi.fn(( ...args ) => mockEnqueueState( ...args )),
|
|
}));
|
|
|
|
describe( "Layer source replacement action", () => {
|
|
let layer: Layer;
|
|
let layerRenderer: LayerRenderer;
|
|
|
|
beforeEach(() => {
|
|
layer = LayerFactory.create({
|
|
source: createMockCanvasElement(),
|
|
mask: createMockCanvasElement(),
|
|
});
|
|
layerRenderer = new LayerRenderer( layer );
|
|
mockGetRendererForLayer.mockReturnValue( layerRenderer );
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.resetAllMocks();
|
|
});
|
|
|
|
describe( "When changing a Layers source", () => {
|
|
const MOCK_ORG_SOURCE = "blob:http://foo";
|
|
const MOCK_NEW_SOURCE = "blob:http://bar";
|
|
|
|
it( "should enqueue the state in history including a resources list", () => {
|
|
replaceLayerSource( layer, MOCK_ORG_SOURCE, MOCK_NEW_SOURCE, true );
|
|
expect( mockEnqueueState ).toHaveBeenCalledWith(
|
|
`layerSource_${layer.id}`, {
|
|
undo: expect.any( Function ),
|
|
redo: expect.any( Function ),
|
|
resources: [ MOCK_ORG_SOURCE, MOCK_NEW_SOURCE ],
|
|
}
|
|
);
|
|
});
|
|
|
|
describe( "and calling the undo or redo state", () => {
|
|
let setSourceSpy;
|
|
let loadedSource: string;
|
|
let undo: VoidFunction;
|
|
let redo: VoidFunction;
|
|
|
|
describe( "for a replace action outside of the Layer mask", () => {
|
|
|
|
beforeEach(() => {
|
|
replaceLayerSource( layer, MOCK_ORG_SOURCE, MOCK_NEW_SOURCE, false );
|
|
({ undo, redo } = mockEnqueueState.mock.calls[ 0 ][ 1 ]);
|
|
|
|
// instantly load an image on src assignment
|
|
setSourceSpy = vi.spyOn( global.Image.prototype, "src", "set" ).mockImplementation( function( _source: string ): void {
|
|
loadedSource = _source;
|
|
this.onload();
|
|
});
|
|
});
|
|
|
|
it.each([ "undo", "redo" ])
|
|
( `should set the layer source to the provided source value when restoring outside of the mask for the "%s" action`,
|
|
( action: string ) => {
|
|
const getMaskContextSpy = vi.spyOn( layer.mask, "getContext" );
|
|
|
|
( action === "undo" ) ? undo() : redo();
|
|
|
|
expect( getMaskContextSpy ).not.toHaveBeenCalled();
|
|
|
|
expect( layer.source.getContext( "2d" ).drawImage ).toHaveBeenCalledWith( expect.any( global.Image ), 0, 0 );
|
|
|
|
expect( loadedSource ).toEqual( action === "undo" ? MOCK_ORG_SOURCE : MOCK_NEW_SOURCE );
|
|
});
|
|
|
|
it.each([ "undo", "redo" ])
|
|
( `should request a filter invalidation and recache from the Layers renderer for the "%s" action`, ( action: string ) => {
|
|
const recacheSpy = vi.spyOn( layerRenderer, "resetFilterAndRecache" );
|
|
|
|
( action === "undo" ) ? undo() : redo();
|
|
|
|
expect( recacheSpy ).toHaveBeenCalledWith();
|
|
});
|
|
});
|
|
|
|
describe( "for a replace action of the Layer mask", () => {
|
|
|
|
beforeEach(() => {
|
|
replaceLayerSource( layer, MOCK_ORG_SOURCE, MOCK_NEW_SOURCE, true );
|
|
({ undo, redo } = mockEnqueueState.mock.calls[ 0 ][ 1 ]);
|
|
|
|
// instantly load an image on src assignment
|
|
setSourceSpy = vi.spyOn( global.Image.prototype, "src", "set" ).mockImplementation( function( _source: string ): void {
|
|
loadedSource = _source;
|
|
this.onload();
|
|
});
|
|
});
|
|
|
|
it.each([ "undo", "redo" ])
|
|
( `should set the layer mask to the provided source value when restoring inside of the mask for the "%s" action`,
|
|
( action: string ) => {
|
|
const getSourceContextSpy = vi.spyOn( layer.source, "getContext" );
|
|
|
|
( action === "undo" ) ? undo() : redo();
|
|
|
|
expect( getSourceContextSpy ).not.toHaveBeenCalled();
|
|
|
|
expect( layer.mask.getContext( "2d" ).drawImage ).toHaveBeenCalledWith( expect.any( global.Image ), 0, 0 );
|
|
|
|
expect( loadedSource ).toEqual( action === "undo" ? MOCK_ORG_SOURCE : MOCK_NEW_SOURCE );
|
|
});
|
|
|
|
it.each([ "undo", "redo" ])
|
|
( `should request a filter invalidation and recache from the Layers renderer for the "%s" action`,
|
|
( action: string ) => {
|
|
const recacheSpy = vi.spyOn( layerRenderer, "resetFilterAndRecache" );
|
|
|
|
( action === "undo" ) ? undo() : redo();
|
|
|
|
expect( recacheSpy ).toHaveBeenCalledWith();
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|