import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { createTapConfirm } from './tapconfirm'; describe('createTapConfirm', () => { beforeEach(() => vi.useFakeTimers()); afterEach(() => vi.useRealTimers()); it('arms a window and reverts after the duration', () => { const changes: boolean[] = []; const c = createTapConfirm({ durationMs: 2000, onConfirm: () => {}, onChange: (x) => changes.push(x) }); c.arm(); expect(c.confirming).toBe(true); expect(changes).toEqual([true]); vi.advanceTimersByTime(1999); expect(c.confirming).toBe(true); vi.advanceTimersByTime(1); expect(c.confirming).toBe(false); expect(changes).toEqual([true, false]); }); it('confirms within the window exactly once and stops the revert timer', () => { const onConfirm = vi.fn(); const c = createTapConfirm({ durationMs: 2000, onConfirm }); c.arm(); c.confirm(); expect(onConfirm).toHaveBeenCalledTimes(1); expect(c.confirming).toBe(false); vi.advanceTimersByTime(5000); // the revert timer must not fire after a confirm expect(onConfirm).toHaveBeenCalledTimes(1); }); it('ignores confirm when the window is not open', () => { const onConfirm = vi.fn(); const c = createTapConfirm({ durationMs: 2000, onConfirm }); c.confirm(); expect(onConfirm).not.toHaveBeenCalled(); expect(c.confirming).toBe(false); }); it('treats arm as idempotent while already confirming', () => { const changes: boolean[] = []; const c = createTapConfirm({ durationMs: 2000, onConfirm: () => {}, onChange: (x) => changes.push(x) }); c.arm(); c.arm(); expect(changes).toEqual([true]); }); it('cancel closes the window without confirming', () => { const onConfirm = vi.fn(); const c = createTapConfirm({ durationMs: 2000, onConfirm }); c.arm(); c.cancel(); expect(c.confirming).toBe(false); vi.advanceTimersByTime(5000); expect(onConfirm).not.toHaveBeenCalled(); }); it('dispose clears a pending timer without a revert callback', () => { const changes: boolean[] = []; const c = createTapConfirm({ durationMs: 2000, onConfirm: () => {}, onChange: (x) => changes.push(x) }); c.arm(); c.dispose(); vi.advanceTimersByTime(5000); expect(changes).toEqual([true]); }); });