| 1 | import assert from 'node:assert/strict'; |
| 2 | import { test } from 'node:test'; |
| 3 | import { PressTracker, type Timers } from '../src/lib/longpress.ts'; |
| 4 | |
| 5 | function clock() { |
| 6 | let now = 0; |
| 7 | let next = 1; |
| 8 | const queue = new Map<number, { at: number; fn: () => void }>(); |
| 9 | const timers: Timers = { |
| 10 | set(fn, ms) { |
| 11 | const id = next++; |
| 12 | queue.set(id, { at: now + ms, fn }); |
| 13 | return id; |
| 14 | }, |
| 15 | clear(handle) { |
| 16 | queue.delete(handle as number); |
| 17 | } |
| 18 | }; |
| 19 | const advance = (ms: number) => { |
| 20 | now += ms; |
| 21 | for (const [id, t] of [...queue]) { |
| 22 | if (t.at <= now) { |
| 23 | queue.delete(id); |
| 24 | t.fn(); |
| 25 | } |
| 26 | } |
| 27 | }; |
| 28 | return { timers, advance }; |
| 29 | } |
| 30 | |
| 31 | function setup() { |
| 32 | const c = clock(); |
| 33 | const fires: Array<[number, number]> = []; |
| 34 | const tracker = new PressTracker((x, y) => fires.push([x, y]), c.timers); |
| 35 | return { ...c, tracker, fires }; |
| 36 | } |
| 37 | |
| 38 | test('fires with the press origin after the delay', () => { |
| 39 | const { tracker, advance, fires } = setup(); |
| 40 | tracker.start(10, 20); |
| 41 | advance(449); |
| 42 | assert.deepEqual(fires, []); |
| 43 | advance(1); |
| 44 | assert.deepEqual(fires, [[10, 20]]); |
| 45 | assert.equal(tracker.fired, true); |
| 46 | }); |
| 47 | |
| 48 | test('release before the delay cancels', () => { |
| 49 | const { tracker, advance, fires } = setup(); |
| 50 | tracker.start(0, 0); |
| 51 | advance(200); |
| 52 | tracker.cancel(); |
| 53 | advance(1000); |
| 54 | assert.deepEqual(fires, []); |
| 55 | assert.equal(tracker.fired, false); |
| 56 | }); |
| 57 | |
| 58 | test('small jitter is tolerated, a drag cancels', () => { |
| 59 | const a = setup(); |
| 60 | a.tracker.start(0, 0); |
| 61 | a.tracker.move(4, 4); |
| 62 | a.advance(450); |
| 63 | assert.equal(a.fires.length, 1); |
| 64 | const b = setup(); |
| 65 | b.tracker.start(0, 0); |
| 66 | b.tracker.move(9, 0); |
| 67 | b.advance(450); |
| 68 | assert.equal(b.fires.length, 0); |
| 69 | }); |
| 70 | |
| 71 | test('a new press clears the fired flag', () => { |
| 72 | const { tracker, advance } = setup(); |
| 73 | tracker.start(0, 0); |
| 74 | advance(450); |
| 75 | tracker.start(5, 5); |
| 76 | assert.equal(tracker.fired, false); |
| 77 | assert.equal(tracker.pending, true); |
| 78 | }); |
| 79 | |
| 80 | test('flush fires a pending press once and ignores an idle tracker', () => { |
| 81 | const { tracker, advance, fires } = setup(); |
| 82 | tracker.flush(); |
| 83 | assert.equal(fires.length, 0); |
| 84 | tracker.start(3, 4); |
| 85 | tracker.flush(); |
| 86 | advance(1000); |
| 87 | assert.deepEqual(fires, [[3, 4]]); |
| 88 | assert.equal(tracker.fired, true); |
| 89 | }); |