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.
258 lines
8.7 KiB
TypeScript
258 lines
8.7 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi, type MockInstance } from "vitest";
|
|
import { flushPromises, mockZCanvas } from "../../mocks";
|
|
mockZCanvas();
|
|
|
|
import DocumentFactory from "@/model/factories/document-factory";
|
|
import LayerFactory from "@/model/factories/layer-factory";
|
|
import {
|
|
ENQUEUE_TIMEOUT,
|
|
createLayerThumbnail,
|
|
hasThumbnail,
|
|
flushThumbnailForLayer,
|
|
flushThumbnailCache,
|
|
getThumbnailForLayer,
|
|
isEnabled,
|
|
rebuildAllThumbnails,
|
|
setEnabled,
|
|
subscribe,
|
|
TRANSPARENT_IMAGE,
|
|
unsubscribe,
|
|
} from "@/rendering/cache/thumbnail-cache";
|
|
|
|
vi.mock( "@/utils/document-util", () => ({
|
|
createLayerSnapshot: vi.fn().mockResolvedValue(({})),
|
|
}));
|
|
|
|
vi.mock( "@/utils/canvas-util", async ( importOriginal ) => ({
|
|
...await importOriginal(),
|
|
imageToBase64: vi.fn().mockReturnValue( "data:image/png;base64," ),
|
|
resizeImage: vi.fn().mockResolvedValue( {} ),
|
|
}));
|
|
|
|
describe( "Thumbnail cache", () => {
|
|
const layer = LayerFactory.create();
|
|
const document = DocumentFactory.create();
|
|
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
vi.spyOn( global, "setTimeout" );
|
|
vi.spyOn( global, "clearTimeout" );
|
|
});
|
|
|
|
afterEach(() => {
|
|
flushThumbnailCache();
|
|
setEnabled( false );
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
describe( "when toggling the enabled state of the cache", () => {
|
|
it( "should by default be disabled", () => {
|
|
expect( isEnabled() ).toBe( false );
|
|
});
|
|
|
|
it( "should cache thumbnails when enabled", () => {
|
|
setEnabled( true );
|
|
|
|
expect( isEnabled() ).toBe( true );
|
|
});
|
|
|
|
it( "should unset the existing cache when disabling a previously enabled cache", async () => {
|
|
setEnabled( true );
|
|
|
|
await createLayerThumbnail( layer, document, true );
|
|
vi.advanceTimersByTime( ENQUEUE_TIMEOUT );
|
|
|
|
setEnabled( false );
|
|
|
|
expect( hasThumbnail( layer.id )).toBe( false );
|
|
});
|
|
|
|
it( "should not unset the enabled state when flushing the existing cache", async () => {
|
|
setEnabled( true );
|
|
|
|
await createLayerThumbnail( layer, document, true );
|
|
flushThumbnailCache();
|
|
|
|
expect( isEnabled() ).toBe( true );
|
|
});
|
|
});
|
|
|
|
describe( "when subscribing to updates of the cache", () => {
|
|
const SUBSCRIBE_TOKEN = "test";
|
|
const updateFn = vi.fn();
|
|
|
|
beforeEach(() => {
|
|
setEnabled( true );
|
|
subscribe( SUBSCRIBE_TOKEN, updateFn );
|
|
});
|
|
|
|
afterEach(() => {
|
|
unsubscribe( SUBSCRIBE_TOKEN );
|
|
updateFn.mockReset();
|
|
});
|
|
|
|
it( "should not receive an update on cache request as it is debounced", async () => {
|
|
await createLayerThumbnail( layer, document, true );
|
|
|
|
expect( updateFn ).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it( "should receive an update on debounced cache completion", async () => {
|
|
await createLayerThumbnail( layer, document, true );
|
|
|
|
vi.advanceTimersByTime( ENQUEUE_TIMEOUT );
|
|
await flushPromises();
|
|
|
|
expect( updateFn ).toHaveBeenCalledWith( layer.id, expect.any( String ));
|
|
});
|
|
|
|
describe( "when force caching is disabled", () => {
|
|
it( "should receive an update when requesting to cache a Layer that was not cached before", async () => {
|
|
await createLayerThumbnail( layer, document, false );
|
|
|
|
vi.advanceTimersByTime( ENQUEUE_TIMEOUT );
|
|
await flushPromises();
|
|
|
|
expect( updateFn ).toHaveBeenCalledWith( layer.id, expect.any( String ));
|
|
});
|
|
|
|
it( "should not receive an update upon requesting to cache a Layer that was cached before", async () => {
|
|
await createLayerThumbnail( layer, document, false );
|
|
|
|
vi.advanceTimersByTime( ENQUEUE_TIMEOUT );
|
|
await flushPromises();
|
|
|
|
expect( updateFn ).toHaveBeenCalledWith( layer.id, expect.any( String ));
|
|
updateFn.mockReset();
|
|
|
|
await createLayerThumbnail( layer, document, false );
|
|
|
|
vi.advanceTimersByTime( ENQUEUE_TIMEOUT );
|
|
await flushPromises();
|
|
|
|
expect( updateFn ).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
it( "should no longer receive updates on unsubscribe", async () => {
|
|
await createLayerThumbnail( layer, document, true );
|
|
unsubscribe( SUBSCRIBE_TOKEN );
|
|
|
|
vi.advanceTimersByTime( ENQUEUE_TIMEOUT );
|
|
await flushPromises();
|
|
|
|
expect( updateFn ).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it( "should immediately cache pending jobs for other layers when a new layer is requested", async () => {
|
|
const layer2 = LayerFactory.create();
|
|
|
|
await createLayerThumbnail( layer, document, true );
|
|
await createLayerThumbnail( layer2, document, true );
|
|
|
|
expect( updateFn ).toHaveBeenCalledTimes( 1 ); // first layer already processed
|
|
|
|
vi.advanceTimersByTime( ENQUEUE_TIMEOUT );
|
|
await flushPromises();
|
|
|
|
expect( updateFn ).toHaveBeenCalledTimes( 2 ); // second layer processed by queue
|
|
|
|
vi.advanceTimersByTime( ENQUEUE_TIMEOUT );
|
|
await flushPromises();
|
|
|
|
expect( updateFn ).toHaveBeenCalledTimes( 2 ); // no new jobs were added
|
|
});
|
|
});
|
|
|
|
describe( "when managing the thumbnail cache for a Layer", () => {
|
|
it( "should by default not have a thumbnail when there is no cache", () => {
|
|
expect( hasThumbnail( layer.id )).toBe( false );
|
|
});
|
|
|
|
it( "should not cache when the thumbnail cache is disabled", async () => {
|
|
await createLayerThumbnail( layer, document, true );
|
|
|
|
expect( hasThumbnail( layer.id )).toBe( false );
|
|
});
|
|
|
|
it( "should cache when the thumbnail cache is enabled", async () => {
|
|
setEnabled( true );
|
|
|
|
await createLayerThumbnail( layer, document, true );
|
|
|
|
expect( hasThumbnail( layer.id )).toBe( true );
|
|
});
|
|
|
|
it( "should be able to remove the cache for an individual Layer", async () => {
|
|
setEnabled( true );
|
|
|
|
const layer2 = LayerFactory.create();
|
|
|
|
await createLayerThumbnail( layer, document, true );
|
|
await createLayerThumbnail( layer2, document, true );
|
|
|
|
expect( hasThumbnail( layer.id )).toBe( true );
|
|
expect( hasThumbnail( layer2.id )).toBe( true );
|
|
|
|
flushThumbnailForLayer( layer );
|
|
|
|
expect( hasThumbnail( layer.id )).toBe( false );
|
|
expect( hasThumbnail( layer2.id )).toBe( true );
|
|
});
|
|
|
|
it( "should be able to remove the cache for all Layers", async () => {
|
|
setEnabled( true );
|
|
|
|
const layer2 = LayerFactory.create();
|
|
|
|
await createLayerThumbnail( layer, document, true );
|
|
await createLayerThumbnail( layer2, document, true );
|
|
|
|
expect( hasThumbnail( layer.id )).toBe( true );
|
|
expect( hasThumbnail( layer2.id )).toBe( true );
|
|
|
|
flushThumbnailCache();
|
|
|
|
expect( hasThumbnail( layer.id )).toBe( false );
|
|
expect( hasThumbnail( layer2.id )).toBe( false );
|
|
});
|
|
});
|
|
|
|
describe( "when caching and retrieving a Layers thumbnail", () => {
|
|
it( "should by default retrieve a transparent image when the thumbnail isn't cached yet", () => {
|
|
expect( getThumbnailForLayer( layer.id )).toEqual( TRANSPARENT_IMAGE );
|
|
});
|
|
|
|
it( "should return a thumbnail image when the caching has completed", async () => {
|
|
setEnabled( true );
|
|
|
|
await createLayerThumbnail( layer, document, true );
|
|
|
|
vi.advanceTimersByTime( ENQUEUE_TIMEOUT );
|
|
await flushPromises();
|
|
|
|
const thumb = getThumbnailForLayer( layer.id );
|
|
|
|
expect( thumb ).not.toEqual( TRANSPARENT_IMAGE );
|
|
expect( thumb ).toEqual( "data:image/png;base64," );
|
|
});
|
|
});
|
|
|
|
it( "should be able to rebuild all thumbnails for a Document", async () => {
|
|
setEnabled( true );
|
|
|
|
const layer1 = LayerFactory.create();
|
|
const layer2 = LayerFactory.create();
|
|
const layer3 = LayerFactory.create();
|
|
|
|
const doc = DocumentFactory.create({
|
|
layers: [ layer1, layer2, layer3 ],
|
|
});
|
|
|
|
await rebuildAllThumbnails( doc );
|
|
|
|
expect( hasThumbnail( layer1.id )).toBe( true );
|
|
expect( hasThumbnail( layer2.id )).toBe( true );
|
|
expect( hasThumbnail( layer3.id )).toBe( true );
|
|
});
|
|
}); |