import assert from 'node:assert/strict'; import test from 'node:test'; import { FrameRenderer } from '../../YouleNexus/assets/framework/presentation/frame-renderer.ts'; test('coalesces frames and invalidates callbacks even if scheduler delivers late', () => { const callbacks: (() => void)[] = []; let cancelled = 0; let rendered = 0; const renderer = new FrameRenderer({ request(cb) { callbacks.push(cb); return () => { cancelled++; }; }, }, () => { rendered++; }, e => { throw e; }); renderer.request(); renderer.request(); assert.equal(callbacks.length, 1); renderer.flush(); assert.equal(rendered, 1); callbacks[0]!(); assert.equal(rendered, 1); renderer.request(); renderer.dispose(); renderer.dispose(); callbacks[1]!(); assert.equal(rendered, 1); assert.equal(cancelled, 2); }); test('releases the current frame before render can request the next frame', () => { const callbacks: (() => void)[] = []; let rendered = 0; let renderer!: FrameRenderer; renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => {}; }, }, () => { rendered += 1; renderer.request(); }, error => { throw error; }); renderer.request(); callbacks[0]!(); assert.equal(rendered, 1); assert.equal(callbacks.length, 2); callbacks[1]!(); assert.equal(rendered, 2); assert.equal(callbacks.length, 3); }); test('dispose during render prevents a reentrant frame request', () => { const callbacks: (() => void)[] = []; let rendered = 0; let renderer!: FrameRenderer; renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => {}; }, }, () => { rendered += 1; renderer.dispose(); renderer.request(); }, error => { throw error; }); renderer.request(); callbacks[0]!(); assert.equal(rendered, 1); assert.equal(callbacks.length, 1); renderer.flush(); assert.equal(rendered, 1); }); test('a throwing dispose cancellation still makes every callback stale', () => { const callbacks: (() => void)[] = []; const cancelError = new Error('cancel failed'); let rendered = 0; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => { throw cancelError; }; }, }, () => { rendered += 1; }, error => { throw error; }); renderer.request(); let thrown: unknown; try { renderer.dispose(); } catch (error) { thrown = error; } assert.equal(thrown, cancelError); callbacks[0]!(); assert.equal(rendered, 0); renderer.request(); assert.equal(callbacks.length, 1); }); test('a throwing flush cancellation invalidates the old frame before rethrowing', () => { const callbacks: (() => void)[] = []; const cancelError = new Error('cancel failed'); let rendered = 0; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => { throw cancelError; }; }, }, () => { rendered += 1; }, error => { throw error; }); renderer.request(); let thrown: unknown; try { renderer.flush(); } catch (error) { thrown = error; } assert.equal(thrown, cancelError); callbacks[0]!(); assert.equal(rendered, 0); renderer.request(); assert.equal(callbacks.length, 2); }); test('flush does not render when cancellation disposes the renderer', () => { let rendered = 0; let renderer!: FrameRenderer; renderer = new FrameRenderer({ request() { return () => { renderer.dispose(); }; }, }, () => { rendered += 1; }, error => { throw error; }); renderer.request(); renderer.flush(); assert.equal(rendered, 0); }); test('a cancellation that reenters flush supersedes the outer render', () => { let rendered = 0; let renderer!: FrameRenderer; renderer = new FrameRenderer({ request() { return () => { renderer.flush(); }; }, }, () => { rendered += 1; }, error => { throw error; }); renderer.request(); renderer.flush(); assert.equal(rendered, 1); }); test('reports an asynchronous render error once and terminates future frames', () => { const callbacks: (() => void)[] = []; const renderError = new Error('render failed'); const faults: unknown[] = []; let rendered = 0; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => {}; }, }, () => { rendered += 1; throw renderError; }, error => { faults.push(error); }); renderer.request(); let thrown: unknown; try { callbacks[0]!(); } catch (error) { thrown = error; } assert.equal(thrown, undefined); assert.deepEqual(faults, [renderError]); callbacks[0]!(); renderer.request(); assert.equal(rendered, 1); assert.deepEqual(faults, [renderError]); assert.equal(callbacks.length, 1); }); test('aggregates an asynchronous render error with reentrant cancellation failure', () => { const callbacks: (() => void)[] = []; const renderError = new Error('render failed'); const cancelError = new Error('cancel failed'); const faults: unknown[] = []; let rendered = 0; let renderer!: FrameRenderer; renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => { throw cancelError; }; }, }, () => { rendered += 1; renderer.request(); throw renderError; }, error => { faults.push(error); }); renderer.request(); let thrown: unknown; try { callbacks[0]!(); } catch (error) { thrown = error; } assert.equal(thrown, undefined); assert.equal(faults.length, 1); const fault = faults[0]; assert.ok(fault instanceof Error); assert.ok('errors' in fault); assert.deepEqual(fault.errors, [renderError, cancelError]); callbacks[1]!(); assert.equal(rendered, 1); assert.equal(faults.length, 1); }); test('flush rethrows a synchronous render error without reporting an async fault', () => { const callbacks: (() => void)[] = []; const renderError = new Error('render failed'); const faults: unknown[] = []; let rendered = 0; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => {}; }, }, () => { rendered += 1; throw renderError; }, error => { faults.push(error); }); renderer.request(); let thrown: unknown; try { renderer.flush(); } catch (error) { thrown = error; } assert.equal(thrown, renderError); assert.deepEqual(faults, []); callbacks[0]!(); assert.equal(rendered, 1); }); test('does not swallow an onFault callback error', () => { const callbacks: (() => void)[] = []; const renderError = new Error('render failed'); const callbackError = new Error('fault callback failed'); const reported: unknown[] = []; const renderer = new FrameRenderer({ request(callback) { callbacks.push(callback); return () => {}; }, }, () => { throw renderError; }, error => { reported.push(error); throw callbackError; }); renderer.request(); let thrown: unknown; try { callbacks[0]!(); } catch (error) { thrown = error; } assert.equal(thrown, callbackError); assert.deepEqual(reported, [renderError]); renderer.request(); assert.equal(callbacks.length, 1); });