import assert from 'node:assert/strict'; import { mkdtemp, rm } from 'node:fs/promises'; import os from 'node:os'; import path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { build } from 'esbuild'; // Pure scheduler verification. No Electron, DOM interaction, backend, or model. const root = fileURLToPath(new URL('../', import.meta.url)); const directory = await mkdtemp(path.join(os.tmpdir(), 'prospect-particle-loop-')); try { const output = path.join(directory, 'particle-loop.mjs'); await build({ entryPoints: [path.join(root, 'renderer/components/particleLoop.ts')], platform: 'node', bundle: true, format: 'esm', outfile: output }); const { createParticleLoop } = await import(pathToFileURL(output).href); const queued = new Map(); let handle = 0; let render = true; let animate = true; const draws = []; const loop = createParticleLoop({ draw: (elapsed, still) => draws.push({ elapsed, still }), canRender: () => render, canAnimate: () => animate, requestFrame: callback => { const id = ++handle; queued.set(id, callback); return id; }, cancelFrame: id => queued.delete(id), }); const step = time => { const entry = queued.entries().next().value; assert.ok(entry, 'one frame should be scheduled'); queued.delete(entry[0]); entry[1](time); }; const checks = []; loop.sync(); assert.equal(queued.size, 1); checks.push('active starts exactly one scheduled frame'); step(0); const first = draws.length; step(16); assert.equal(draws.length, first); step(34); assert.equal(draws.length, first + 1); step(50); assert.equal(draws.length, first + 1); step(68); assert.equal(draws.length, first + 2); checks.push('draws are capped at 30 fps'); for (let index = 0; index < 5; index += 1) loop.sync(); assert.equal(queued.size, 1); checks.push('repeated state sync never duplicates the animation loop'); animate = false; loop.sync(); assert.equal(queued.size, 0); assert.equal(draws.at(-1).still, true); checks.push('paused or reduced motion leaves a static frame and no scheduled work'); render = false; const before = draws.length; loop.sync(); assert.equal(queued.size, 0); assert.equal(draws.length, before); checks.push('hidden or offscreen canvas does not draw'); render = true; loop.sync(); assert.equal(draws.at(-1).still, true); assert.equal(queued.size, 0); checks.push('visible reduced-motion frame is restored without starting work'); animate = true; loop.sync(); const elapsed = draws.at(-1).elapsed; step(100000); assert.equal(draws.at(-1).elapsed, elapsed); step(100034); assert.equal(draws.at(-1).elapsed, elapsed + 34); checks.push('resume retains elapsed motion without a hidden-time jump'); loop.dispose(); assert.equal(queued.size, 0); const end = draws.length; loop.sync(); assert.equal(draws.length, end); assert.equal(queued.size, 0); checks.push('dispose cancels work permanently'); process.stdout.write(JSON.stringify({ kind: 'pure scheduler unit check', passed: checks.length, checks, externalActions: 0 }, null, 2) + '\n'); } finally { await rm(directory, { recursive: true, force: true }); }