fuzzyworld

explodorama / files / test / padHold.test.ts

padHold.test.ts

Blame: the commit that last changed each line · file · history

92cc4ee3 2026-11-02 Pavel Safronov1import { describe, expect, it } from 'vitest'
2import { cancelHold, HOLD_SECONDS, holdProgress, newHold, startHold, stepHold } from '../src/ui/padHold'
3
4const FULL = HOLD_SECONDS * 1000
5const down = (...faces: number[]) => (f: number) => faces.includes(f)
6
7describe('padHold', () => {
8 it('fills linearly and reaches 1 at HOLD_SECONDS', () => {
9 expect(holdProgress(1000, 1000)).toBe(0)
10 expect(holdProgress(1000, 1000 + FULL / 2)).toBeCloseTo(0.5)
11 expect(holdProgress(1000, 1000 + FULL)).toBe(1)
12 expect(holdProgress(1000, 1000 + FULL * 5)).toBe(1)
13 })
14
15 it('reports progress while held and fires once, the moment the ring fills', () => {
16 const h = newHold()
17 expect(startHold(h, 0, 0)).toBe(true)
18 expect(stepHold(h, down(0), FULL / 2)).toEqual([{ kind: 'progress', face: 0, progress: 0.5 }])
19 expect(stepHold(h, down(0), FULL)).toEqual([{ kind: 'progress', face: 0, progress: 1 }, { kind: 'fire', face: 0 }])
20 })
21
22 it('does not fire again while still held, and the release only clears the ring', () => {
23 const h = newHold()
24 startHold(h, 0, 0)
25 stepHold(h, down(0), FULL)
26 expect(stepHold(h, down(0), FULL + 16)).toEqual([])
27 expect(stepHold(h, down(0), FULL * 10)).toEqual([])
28 expect(stepHold(h, down(), FULL * 10 + 16)).toEqual([{ kind: 'release', face: 0 }])
29 expect(stepHold(h, down(), FULL * 10 + 32)).toEqual([])
30 })
31
32 it('keeps the button used up until it is let go: no new hold, not even the same button', () => {
33 const h = newHold()
34 startHold(h, 0, 0)
35 stepHold(h, down(0), FULL)
36 expect(startHold(h, 0, FULL + 10)).toBe(false)
37 expect(startHold(h, 1, FULL + 20)).toBe(false)
38 stepHold(h, down(0), FULL + 30)
39 stepHold(h, down(), FULL + 40)
40 expect(startHold(h, 0, FULL + 50)).toBe(true)
41 })
42
43 it('cancels, with no action, on an early release', () => {
44 const h = newHold()
45 startHold(h, 3, 0)
46 expect(stepHold(h, down(), FULL - 1)).toEqual([{ kind: 'cancel', face: 3 }])
47 expect(stepHold(h, down(), FULL + 100)).toEqual([])
48 })
49
50 it('ignores a second button while one is held, and allows one after', () => {
51 const h = newHold()
52 startHold(h, 0, 0)
53 expect(startHold(h, 1, 100)).toBe(false)
54 expect(stepHold(h, down(0, 1), 200)).toEqual([{ kind: 'progress', face: 0, progress: 0.4 }])
55 stepHold(h, down(), 300)
56 expect(startHold(h, 1, 400)).toBe(true)
57 })
58
59 it('cancel (blur, a swapped pad) ends a hold with no action', () => {
60 const h = newHold()
61 expect(cancelHold(h)).toEqual([])
62 startHold(h, 2, 0)
63 expect(cancelHold(h)).toEqual([{ kind: 'cancel', face: 2 }])
64 expect(stepHold(h, down(2), FULL)).toEqual([])
65 })
66
67 it('cancel on a used-up button frees it, with no action', () => {
68 const h = newHold()
69 startHold(h, 2, 0)
70 stepHold(h, down(2), FULL)
71 expect(cancelHold(h)).toEqual([{ kind: 'cancel', face: 2 }])
72 expect(startHold(h, 2, FULL + 1)).toBe(true)
73 })
74})