mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
6355946610
Two thematic cuts in one PR:
- `src/store/engineState.ts` — `isAudioPaused` (warm-vs-cold-resume
decision flag) + `playGeneration` (monotonically bumped guard
counter used by long-running async callbacks to detect concurrent
playTrack and bail). Get/set accessors + `bumpPlayGeneration()`
that returns the new value (mirrors the original `++playGeneration`
pattern).
- `src/store/seekDebounce.ts` — seek-slider debounce timer behind
`armSeekDebounce(delayMs, onFire)` / `clearSeekDebounce()` /
`isSeekDebouncePending()`. Collapses the recurring inline
`if (seekDebounce) { clearTimeout(...); seekDebounce = null; }`
triple into single calls.
Sed-driven bulk rewrite for the >60 direct-access sites preserved
semantics verbatim (one JSDoc reference to `isAudioPaused` kept as
comment text). 14 tests across the two modules.
playerStore 2869 → 2867 LOC net (small delta — the imports are bigger
than the savings, but the mutables are no longer reachable from
outside their owning modules).
57 lines
1.5 KiB
TypeScript
57 lines
1.5 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import {
|
|
_resetSeekDebounceForTest,
|
|
armSeekDebounce,
|
|
clearSeekDebounce,
|
|
isSeekDebouncePending,
|
|
} from './seekDebounce';
|
|
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
});
|
|
|
|
afterEach(() => {
|
|
_resetSeekDebounceForTest();
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
describe('seekDebounce', () => {
|
|
it('starts not pending', () => {
|
|
expect(isSeekDebouncePending()).toBe(false);
|
|
});
|
|
|
|
it('armSeekDebounce flips pending true and fires the callback after the delay', () => {
|
|
const cb = vi.fn();
|
|
armSeekDebounce(100, cb);
|
|
expect(isSeekDebouncePending()).toBe(true);
|
|
vi.advanceTimersByTime(99);
|
|
expect(cb).not.toHaveBeenCalled();
|
|
vi.advanceTimersByTime(1);
|
|
expect(cb).toHaveBeenCalledTimes(1);
|
|
expect(isSeekDebouncePending()).toBe(false);
|
|
});
|
|
|
|
it('arming again before fire replaces the callback', () => {
|
|
const first = vi.fn();
|
|
const second = vi.fn();
|
|
armSeekDebounce(100, first);
|
|
armSeekDebounce(100, second);
|
|
vi.advanceTimersByTime(100);
|
|
expect(first).not.toHaveBeenCalled();
|
|
expect(second).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('clearSeekDebounce cancels a pending fire', () => {
|
|
const cb = vi.fn();
|
|
armSeekDebounce(100, cb);
|
|
clearSeekDebounce();
|
|
expect(isSeekDebouncePending()).toBe(false);
|
|
vi.advanceTimersByTime(1000);
|
|
expect(cb).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('clearSeekDebounce is a no-op when nothing is pending', () => {
|
|
expect(() => clearSeekDebounce()).not.toThrow();
|
|
});
|
|
});
|