import { describe, expect, it } from 'vitest'; import { EventBus } from '../src/core/EventBus'; import { StubBeatClock } from '../src/core/StubBeatClock'; describe('StubBeatClock', () => { it('emits sequential beats at the configured BPM', () => { const bus = new EventBus(); const beats: { beatIndex: number; audioTimeMs: number }[] = []; bus.on('beat:tick', (b) => beats.push(b)); const clock = new StubBeatClock(bus, 120); // 500ms interval clock.start(); for (let i = 0; i < 100; i++) clock.update(37); // ragged frame times expect(beats.length).toBe(Math.floor((100 * 37) / 500)); expect(beats.map((b) => b.beatIndex)).toEqual(beats.map((_, i) => i)); // beat timestamps land on exact multiples of the interval for (const [i, b] of beats.entries()) expect(b.audioTimeMs).toBeCloseTo((i + 1) * 500, 6); }); it('does not emit when stopped', () => { const bus = new EventBus(); let count = 0; bus.on('beat:tick', () => count++); const clock = new StubBeatClock(bus, 128); clock.update(10_000); expect(count).toBe(0); }); });