test/play-clock.test.ts (1701 bytes)
1 import fc from 'fast-check'; 2 import { describe, expect, it } from 'vitest'; 3 import { positionAt, type PlayClock } from '../src/play-clock'; 4 5 const ms = fc.nat({ max: 10 ** 8 }); 6 const speed = fc.double({ min: 0.25, max: 4, noNaN: true, noDefaultInfinity: true }); 7 const clock = fc.record({ positionMs: ms, reportedAtMs: ms, speed, playing: fc.boolean() }); 8 9 describe('positionAt', () => { 10 it('moves with the clock of the device at the speed', () => { 11 const c: PlayClock = { positionMs: 1000, reportedAtMs: 500, speed: 2, playing: true }; 12 expect(positionAt(c, 500)).toBe(1000); 13 expect(positionAt(c, 600)).toBe(1200); 14 }); 15 16 it('holds the position while paused', () => { 17 fc.assert( 18 fc.property(clock, ms, (c, now) => { 19 const paused = { ...c, playing: false }; 20 expect(positionAt(paused, now)).toBe(c.positionMs); 21 }), 22 ); 23 }); 24 25 it('never goes back while playing', () => { 26 fc.assert( 27 fc.property(clock, ms, ms, (c, t1, t2) => { 28 const playing = { ...c, playing: true }; 29 const [a, b] = t1 <= t2 ? [t1, t2] : [t2, t1]; 30 expect(positionAt(playing, b)).toBeGreaterThanOrEqual(positionAt(playing, a)); 31 }), 32 ); 33 }); 34 35 it('scales the distance with the speed', () => { 36 fc.assert( 37 fc.property(clock, ms, fc.double({ min: 1, max: 4, noNaN: true }), (c, dt, k) => { 38 const playing = { ...c, playing: true }; 39 const faster = { ...playing, speed: playing.speed * k }; 40 const now = c.reportedAtMs + dt; 41 const d1 = positionAt(playing, now) - c.positionMs; 42 const d2 = positionAt(faster, now) - c.positionMs; 43 expect(d2).toBeCloseTo(d1 * k, 3); 44 }), 45 ); 46 }); 47 });