UNPKG

wavesurfer.js

Version:
235 lines (234 loc) 10.7 kB
import { setupStateEventEmission, setupSignalEventEmission, setupDebouncedEventEmission, setupConditionalEventEmission, } from '../state-event-emitter'; import { createWaveSurferState } from '../../state/wavesurfer-state'; import { signal } from '../store'; describe('state-event-emitter', () => { let emitter; beforeEach(() => { emitter = { emit: jest.fn() }; jest.useFakeTimers(); }); afterEach(() => { jest.useRealTimers(); }); describe('setupStateEventEmission', () => { it('should emit play event when isPlaying becomes true', () => { const { state, actions } = createWaveSurferState(); const cleanup = setupStateEventEmission(state, emitter); // Initial state triggers pause (isPlaying starts false) expect(emitter.emit).toHaveBeenCalledWith('pause'); emitter.emit.mockClear(); actions.setPlaying(true); expect(emitter.emit).toHaveBeenCalledWith('play'); cleanup(); }); it('should emit pause event when isPlaying becomes false', () => { const { state, actions } = createWaveSurferState(); actions.setPlaying(true); emitter.emit.mockClear(); const cleanup = setupStateEventEmission(state, emitter); actions.setPlaying(false); expect(emitter.emit).toHaveBeenCalledWith('pause'); cleanup(); }); it('should emit timeupdate when currentTime changes', () => { const { state, actions } = createWaveSurferState(); const cleanup = setupStateEventEmission(state, emitter); emitter.emit.mockClear(); actions.setCurrentTime(42); expect(emitter.emit).toHaveBeenCalledWith('timeupdate', 42); cleanup(); }); it('should emit audioprocess when playing and time changes', () => { const { state, actions } = createWaveSurferState(); actions.setPlaying(true); const cleanup = setupStateEventEmission(state, emitter); emitter.emit.mockClear(); actions.setCurrentTime(10); expect(emitter.emit).toHaveBeenCalledWith('timeupdate', 10); expect(emitter.emit).toHaveBeenCalledWith('audioprocess', 10); cleanup(); }); it('should not emit audioprocess when paused', () => { const { state, actions } = createWaveSurferState(); actions.setPlaying(false); const cleanup = setupStateEventEmission(state, emitter); emitter.emit.mockClear(); actions.setCurrentTime(10); expect(emitter.emit).toHaveBeenCalledWith('timeupdate', 10); expect(emitter.emit).not.toHaveBeenCalledWith('audioprocess', expect.anything()); cleanup(); }); it('should emit seeking event when isSeeking becomes true', () => { const { state, actions } = createWaveSurferState(); actions.setCurrentTime(50); const cleanup = setupStateEventEmission(state, emitter); emitter.emit.mockClear(); actions.setSeeking(true); expect(emitter.emit).toHaveBeenCalledWith('seeking', 50); cleanup(); }); it('should emit ready event when state becomes ready', () => { const { state, actions } = createWaveSurferState(); const cleanup = setupStateEventEmission(state, emitter); emitter.emit.mockClear(); // Set duration and audio buffer to make state ready actions.setDuration(100); // Create a mock AudioBuffer since jsdom doesn't support it const mockAudioBuffer = { duration: 100, length: 44100, sampleRate: 44100, numberOfChannels: 2, }; actions.setAudioBuffer(mockAudioBuffer); expect(emitter.emit).toHaveBeenCalledWith('ready', 100); cleanup(); }); it('should only emit ready event once', () => { const { state, actions } = createWaveSurferState(); const cleanup = setupStateEventEmission(state, emitter); // Make ready actions.setDuration(100); const mockAudioBuffer = { duration: 100, length: 44100, sampleRate: 44100, numberOfChannels: 2, }; actions.setAudioBuffer(mockAudioBuffer); const readyCallCount = emitter.emit.mock.calls.filter((call) => call[0] === 'ready').length; emitter.emit.mockClear(); // Change duration again actions.setDuration(150); // Should not emit ready again expect(emitter.emit).not.toHaveBeenCalledWith('ready', expect.anything()); expect(readyCallCount).toBe(1); cleanup(); }); it('should emit zoom event when zoom changes', () => { const { state, actions } = createWaveSurferState(); const cleanup = setupStateEventEmission(state, emitter); emitter.emit.mockClear(); actions.setZoom(2); expect(emitter.emit).toHaveBeenCalledWith('zoom', 2); cleanup(); }); it('should not emit zoom event for zoom=0', () => { const { state, actions } = createWaveSurferState(); const cleanup = setupStateEventEmission(state, emitter); emitter.emit.mockClear(); actions.setZoom(0); expect(emitter.emit).not.toHaveBeenCalledWith('zoom', 0); cleanup(); }); it('should cleanup all subscriptions', () => { const { state, actions } = createWaveSurferState(); const cleanup = setupStateEventEmission(state, emitter); cleanup(); emitter.emit.mockClear(); // These should not trigger events after cleanup actions.setPlaying(true); actions.setCurrentTime(42); actions.setZoom(2); expect(emitter.emit).not.toHaveBeenCalled(); }); }); describe('setupSignalEventEmission', () => { it('should emit custom events from signal changes', () => { const volumeSignal = signal(1); const cleanup = setupSignalEventEmission(volumeSignal, emitter, (vol) => ['volume', vol]); // Initial emission expect(emitter.emit).toHaveBeenCalledWith('volume', 1); emitter.emit.mockClear(); volumeSignal.set(0.5); expect(emitter.emit).toHaveBeenCalledWith('volume', 0.5); cleanup(); }); it('should cleanup subscription', () => { const testSignal = signal(0); const cleanup = setupSignalEventEmission(testSignal, emitter, (val) => ['test', val]); cleanup(); emitter.emit.mockClear(); testSignal.set(1); expect(emitter.emit).not.toHaveBeenCalled(); }); }); describe('setupDebouncedEventEmission', () => { it('should debounce event emission', () => { const scrollSignal = signal(0); const cleanup = setupDebouncedEventEmission(scrollSignal, emitter, (pos) => ['scroll', pos], 100); // Initial emission should be debounced emitter.emit.mockClear(); scrollSignal.set(10); expect(emitter.emit).not.toHaveBeenCalled(); scrollSignal.set(20); expect(emitter.emit).not.toHaveBeenCalled(); scrollSignal.set(30); expect(emitter.emit).not.toHaveBeenCalled(); // Fast forward time jest.advanceTimersByTime(100); // Should emit only the last value expect(emitter.emit).toHaveBeenCalledTimes(1); expect(emitter.emit).toHaveBeenCalledWith('scroll', 30); cleanup(); }); it('should restart debounce timer on each change', () => { const testSignal = signal(0); const cleanup = setupDebouncedEventEmission(testSignal, emitter, (val) => ['test', val], 100); emitter.emit.mockClear(); testSignal.set(1); jest.advanceTimersByTime(50); testSignal.set(2); jest.advanceTimersByTime(50); // Should not have emitted yet (timer restarted) expect(emitter.emit).not.toHaveBeenCalled(); jest.advanceTimersByTime(50); // Now should emit expect(emitter.emit).toHaveBeenCalledWith('test', 2); cleanup(); }); it('should cleanup pending timeout', () => { const testSignal = signal(0); const cleanup = setupDebouncedEventEmission(testSignal, emitter, (val) => ['test', val], 100); emitter.emit.mockClear(); testSignal.set(1); cleanup(); jest.advanceTimersByTime(100); // Should not emit after cleanup expect(emitter.emit).not.toHaveBeenCalled(); }); }); describe('setupConditionalEventEmission', () => { it('should only emit when condition is true', () => { const stateSignal = signal(0); const cleanup = setupConditionalEventEmission(stateSignal, emitter, (val) => val > 5, (val) => ['threshold', val]); emitter.emit.mockClear(); stateSignal.set(3); expect(emitter.emit).not.toHaveBeenCalled(); stateSignal.set(7); expect(emitter.emit).toHaveBeenCalledWith('threshold', 7); cleanup(); }); it('should re-evaluate condition on each change', () => { const testSignal = signal(false); const cleanup = setupConditionalEventEmission(testSignal, emitter, (val) => val === true, () => ['activated']); emitter.emit.mockClear(); testSignal.set(false); expect(emitter.emit).not.toHaveBeenCalled(); testSignal.set(true); expect(emitter.emit).toHaveBeenCalledWith('activated'); emitter.emit.mockClear(); testSignal.set(false); expect(emitter.emit).not.toHaveBeenCalled(); cleanup(); }); it('should cleanup subscription', () => { const testSignal = signal(0); const cleanup = setupConditionalEventEmission(testSignal, emitter, (val) => val > 0, (val) => ['test', val]); cleanup(); emitter.emit.mockClear(); testSignal.set(10); expect(emitter.emit).not.toHaveBeenCalled(); }); }); });