diff --git a/blocksuite/affine/gfx/turbo-renderer/src/turbo-renderer.ts b/blocksuite/affine/gfx/turbo-renderer/src/turbo-renderer.ts index b92c405d79..e6333c18a1 100644 --- a/blocksuite/affine/gfx/turbo-renderer/src/turbo-renderer.ts +++ b/blocksuite/affine/gfx/turbo-renderer/src/turbo-renderer.ts @@ -73,11 +73,11 @@ export const TurboRendererConfigFactory = export class ViewportTurboRendererExtension extends GfxExtension { static override key = 'viewportTurboRenderer'; - private readonly state$ = new BehaviorSubject('inactive'); + public readonly state$ = new BehaviorSubject('inactive'); public readonly canvas: HTMLCanvasElement = document.createElement('canvas'); + public layoutCacheData: ViewportLayoutTree | null = null; private readonly worker: Worker; private readonly disposables = new DisposableGroup(); - private layoutCacheData: ViewportLayoutTree | null = null; private layoutVersion = 0; private bitmap: ImageBitmap | null = null; private viewportElement: GfxViewportElement | null = null; diff --git a/blocksuite/integration-test/src/__tests__/edgeless/viewport-renderer.spec.ts b/blocksuite/integration-test/src/__tests__/edgeless/turbo-renderer.spec.ts similarity index 50% rename from blocksuite/integration-test/src/__tests__/edgeless/viewport-renderer.spec.ts rename to blocksuite/integration-test/src/__tests__/edgeless/turbo-renderer.spec.ts index b567fbd183..3ac3b4f58e 100644 --- a/blocksuite/integration-test/src/__tests__/edgeless/viewport-renderer.spec.ts +++ b/blocksuite/integration-test/src/__tests__/edgeless/turbo-renderer.spec.ts @@ -2,6 +2,7 @@ * Please refer to integration-test/README.md for commands to run tests. */ import { ParagraphLayoutHandlerExtension } from '@blocksuite/affine/blocks/paragraph'; +import { noop } from '@blocksuite/affine/global/utils'; import { TurboRendererConfigFactory, ViewportTurboRendererExtension, @@ -38,31 +39,71 @@ describe('viewport turbo renderer', () => { test('should access turbo renderer instance', async () => { const renderer = getRenderer(); + expect(renderer).toBeDefined(); expect(renderer instanceof ViewportTurboRendererExtension).toBe(true); expect(renderer.canvas).toBeInstanceOf(HTMLCanvasElement); }); test('initial state should be pending', async () => { const renderer = getRenderer(); - expect(renderer.currentState).toBe('pending'); + expect(renderer.state$.value).toBe('pending'); }); test('zooming should change state to zooming', async () => { const renderer = getRenderer(); renderer.viewport.zooming$.next(true); await wait(); - expect(renderer.currentState).toBe('zooming'); + expect(renderer.state$.value).toBe('zooming'); renderer.viewport.zooming$.next(false); await wait(); - expect(renderer.currentState).not.toBe('zooming'); + expect(renderer.state$.value).not.toBe('zooming'); }); - test('state should become ready after rendering', async () => { + test('state transitions between pending and ready', async () => { + const renderer = getRenderer(); + + // Initial state should be pending after adding content + addSampleNotes(doc, 1); + await wait(100); // Short wait for initial processing + expect(renderer.state$.value).toBe('pending'); + + // Ensure zooming is off and wait for debounce + buffer + renderer.viewport.zooming$.next(false); + await wait(renderer.options.debounceTime + 500); + expect(renderer.state$.value).toBe('ready'); + }); + + test('initial layout cache data should be null', () => { + const renderer = getRenderer(); + expect(renderer.layoutCacheData).toBeNull(); + }); + + test('invalidation should reset layout cache data to null', async () => { + const renderer = getRenderer(); addSampleNotes(doc, 1); await wait(100); + + // Access getter to populate cache + const _cache = renderer.layoutCache; + noop(_cache); + expect(renderer.layoutCacheData).not.toBeNull(); + + // Invalidate + addSampleNotes(doc, 1); + await wait(100); + + expect(renderer.layoutCacheData).toBeNull(); + }); + + test('accessing layoutCache getter should populate cache data', async () => { const renderer = getRenderer(); - renderer.viewport.zooming$.next(false); - await wait(renderer.options.debounceTime + 1000); - expect(renderer.currentState).toBe('ready'); + addSampleNotes(doc, 1); + await wait(); + expect(renderer.layoutCacheData).toBeNull(); // Check internal state before access + + const _cache = renderer.layoutCache; // Access getter to populate cache + noop(_cache); + expect(renderer.layoutCacheData).not.toBeNull(); // Check internal state after access + expect(renderer.layoutCache?.roots.length).toBeGreaterThan(0); // Check public getter result }); });