import type { Application } from 'pixi.js'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { initRenderLoop, isRendering, renderCount, requestRender, resetRenderLoop, } from '../../src/ui/render'; /** A stand-in for the Pixi application: counts draws and remembers what was put on the ticker. */ function fakeApp() { const added: { fn: () => void; priority?: number }[] = []; const removed: { fn: unknown; context: unknown }[] = []; const app = { drawn: 0, render(): void { app.drawn += 1; }, ticker: { add(fn: () => void, _context?: unknown, priority?: number) { added.push({ fn, priority }); return this; }, remove(fn: unknown, context?: unknown) { removed.push({ fn, context }); return this; }, }, }; return { app, added, removed, tick: () => added.forEach((entry) => entry.fn()) }; } describe('render loop', () => { let now = 0; beforeEach(() => { now = 1000; vi.spyOn(performance, 'now').mockImplementation(() => now); resetRenderLoop(); }); afterEach(() => vi.restoreAllMocks()); it('is asleep until something asks for a frame', () => { expect(isRendering()).toBe(false); }); it('stays awake for a window after the request, not for a single frame', () => { requestRender(); expect(isRendering()).toBe(true); now += 199; expect(isRendering()).toBe(true); now += 2; expect(isRendering()).toBe(false); }); it('a later request extends the window rather than queueing a second one', () => { requestRender(); now += 150; requestRender(); now += 150; expect(isRendering()).toBe(true); }); it('takes the renderer off the ticker and drives it itself', () => { const { app, removed, tick } = fakeApp(); initRenderLoop(app as unknown as Application); expect(removed).toEqual([{ fn: app.render, context: app }]); // `initRenderLoop` opens the first window, so the first frames are drawn. tick(); tick(); expect(app.drawn).toBe(2); expect(renderCount()).toBe(2); }); it('draws nothing while nothing changed, and again after a request', () => { const { app, tick } = fakeApp(); initRenderLoop(app as unknown as Application); now += 500; for (let i = 0; i < 30; i++) tick(); expect(app.drawn).toBe(0); expect(renderCount()).toBe(0); requestRender(); tick(); expect(app.drawn).toBe(1); // And it falls asleep again by itself. now += 500; tick(); expect(app.drawn).toBe(1); }); });