Recently Written · git

scan-subread

PDF reader that follows an audiobook with the SubRead suite

git clone https://github.com/equwal/scan-subread

Log | Files | Refs


test/player-state.test.ts (2621 bytes)

1 import fc from 'fast-check';
2 import { describe, expect, it } from 'vitest';
3 import { isPlayerError, parseState, type PlayerState } from '../src/player-state';
4 
5 function state(line: string): PlayerState {
6   const parsed = parseState(line);
7   if (isPlayerError(parsed)) throw new Error(`error ${parsed.error} for ${line}`);
8   return parsed;
9 }
10 
11 describe('parseState', () => {
12   it('reads a playing report', () => {
13     expect(state('playing=1;position=96153;speed=1.5;package=de.ph1b.audiobook')).toEqual({
14       playing: true,
15       positionMs: 96153,
16       speed: 1.5,
17       package: 'de.ph1b.audiobook',
18     });
19   });
20 
21   it('reads a paused report', () => {
22     const s = state('playing=0;position=0;speed=1.0;package=p');
23     expect(s.playing).toBe(false);
24     expect(s.positionMs).toBe(0);
25   });
26 
27   it('gives no position for a player without one', () => {
28     const s = state('playing=1;position=-1;speed=1.0;package=p');
29     expect(s.playing).toBe(true);
30     expect(s.positionMs).toBeNull();
31   });
32 
33   it('returns the error name of a problem line', () => {
34     expect(parseState('error=no_player')).toEqual({ error: 'no_player' });
35     expect(parseState('error=no_notification_access')).toEqual({
36       error: 'no_notification_access',
37     });
38     expect(parseState('error=no_overlay')).toEqual({ error: 'no_overlay' });
39   });
40 
41   it('rejects an empty or broken line', () => {
42     expect(parseState(null)).toEqual({ error: 'empty' });
43     expect(parseState(undefined)).toEqual({ error: 'empty' });
44     expect(parseState('')).toEqual({ error: 'empty' });
45     expect(parseState('hello')).toEqual({ error: 'malformed' });
46     expect(parseState('playing=1;position=abc')).toEqual({ error: 'malformed' });
47     expect(parseState('playing=1;position=')).toEqual({ error: 'malformed' });
48   });
49 
50   it('falls back to speed 1 for a missing or zero speed', () => {
51     expect(state('playing=1;position=5').speed).toBe(1);
52     expect(state('playing=1;position=5;speed=0').speed).toBe(1);
53     expect(state('playing=1;position=5;speed=-2').speed).toBe(1);
54     expect(state('playing=1;position=5').package).toBe('');
55   });
56 
57   it('round trips any state line', () => {
58     const pkg = fc.stringMatching(/^[a-z][a-z0-9_.]{0,30}$/);
59     const speed = fc.double({ min: 0.25, max: 4, noNaN: true, noDefaultInfinity: true });
60     fc.assert(
61       fc.property(fc.boolean(), fc.nat({ max: 10 ** 9 }), speed, pkg, (playing, ms, sp, p) => {
62         const line = `playing=${playing ? 1 : 0};position=${ms};speed=${sp};package=${p}`;
63         expect(state(line)).toEqual({ playing, positionMs: ms, speed: sp, package: p });
64       }),
65     );
66   });
67 });