1import test from 'node:test';2import assert from 'node:assert/strict';3import {4 createPresentationClock,5 installPresentationClock,6 parseSlowmo,7} from '../src/presentation-clock.js';89test('parseSlowmo accepts positive review multipliers and clamps unsafe values', () => {10 assert.equal(parseSlowmo(''), 1);11 assert.equal(parseSlowmo('?slowmo=8'), 8);12 assert.equal(parseSlowmo('?slowmo=0'), 1);13 assert.equal(parseSlowmo('?slowmo=-2'), 1);14 assert.equal(parseSlowmo('?slowmo=999999'), 1000);15 assert.equal(parseSlowmo('?slowmo=nope'), 1);16});1718test('presentation clock slows virtual now and RAF timestamps together', () => {19 let raw = 100;20 let rafCallback;21 const clock = createPresentationClock({22 slowmo: 4,23 now: () => raw,24 raf: callback => { rafCallback = callback; return 17; },25 cancelRaf: () => {},26 setTimeoutFn: () => 1,27 clearTimeoutFn: () => {},28 setIntervalFn: () => 2,29 clearIntervalFn: () => {},30 });3132 raw = 500;33 assert.equal(clock.realNow(), 500);34 assert.equal(clock.now(), 200);35 let frame;36 assert.equal(clock.requestAnimationFrame(value => { frame = value; }), 17);37 rafCallback();38 assert.equal(frame, 200);39});4041test('presentation timers stretch delays while native timer APIs stay available', () => {42 const timeouts = [];43 const intervals = [];44 const clock = createPresentationClock({45 slowmo: 8,46 now: () => 0,47 raf: callback => { callback(); return 1; },48 cancelRaf: () => {},49 setTimeoutFn: (callback, delay, ...args) => { timeouts.push({ callback, delay, args }); return 3; },50 clearTimeoutFn: handle => { assert.equal(handle, 3); },51 setIntervalFn: (callback, delay, ...args) => { intervals.push({ callback, delay, args }); return 4; },52 clearIntervalFn: handle => { assert.equal(handle, 4); },53 });54 const callback = () => {};55 assert.equal(clock.setTimeout(callback, 90, 'x'), 3);56 assert.equal(timeouts[0].delay, 720);57 assert.deepEqual(timeouts[0].args, ['x']);58 assert.equal(clock.setInterval(callback, 110), 4);59 assert.equal(intervals[0].delay, 880);60 clock.clearTimeout(3);61 clock.clearInterval(4);62});6364test('installPresentationClock patches presentation globals and restores them', () => {65 let raw = 10;66 const scheduled = [];67 const target = {68 performance: { now: () => raw },69 requestAnimationFrame(callback) { scheduled.push(callback); return 1; },70 cancelAnimationFrame() {},71 setTimeout(callback, delay) { scheduled.push({ callback, delay }); return 2; },72 clearTimeout() {},73 setInterval() { return 3; },74 clearInterval() {},75 };76 const nativeNow = target.performance.now;77 const nativeRaf = target.requestAnimationFrame;78 const installed = installPresentationClock({ target, slowmo: 5 });79 raw = 510;80 assert.equal(target.performance.now(), 110);81 let timestamp;82 target.requestAnimationFrame(value => { timestamp = value; });83 scheduled[0]();84 assert.equal(timestamp, 110);85 target.setTimeout(() => {}, 20);86 assert.equal(scheduled[1].delay, 100);87 assert.equal(installed.clock.realNow(), 510);88 installed.restore();89 assert.equal(target.performance.now, nativeNow);90 assert.equal(target.requestAnimationFrame, nativeRaf);91});