import { describe, it, expect, beforeEach } from 'vitest'; /** * Tests for input history logic extracted from useInputHistory. * We test the pure state transitions directly rather than using * React testing utilities to avoid react-dom version conflicts. */ const MAX_HISTORY = 50; function createHistoryState() { let history: string[] = []; let historyIndex = -1; let savedInput = ''; function addToHistory(input: string): void { if (!input.trim()) return; if (history[0] === input) return; history = [input, ...history].slice(0, MAX_HISTORY); historyIndex = -1; } function navigateUp(currentInput: string): string | null { if (history.length === 0) return null; if (historyIndex === -1) { savedInput = currentInput; } const nextIndex = Math.min(historyIndex + 1, history.length - 1); historyIndex = nextIndex; return history[nextIndex] ?? null; } function navigateDown(): string | null { if (historyIndex <= 0) { historyIndex = -1; return savedInput; } const nextIndex = historyIndex - 1; historyIndex = nextIndex; return history[nextIndex] ?? null; } function resetNavigation(): void { historyIndex = -1; } function getHistoryLength(): number { return history.length; } return { addToHistory, navigateUp, navigateDown, resetNavigation, getHistoryLength }; } describe('useInputHistory (logic)', () => { let h: ReturnType; beforeEach(() => { h = createHistoryState(); }); it('adds to history on submit', () => { h.addToHistory('hello'); h.addToHistory('world'); // navigateUp should return 'world' first (most recent) const val = h.navigateUp(''); expect(val).toBe('world'); }); it('does not add empty strings to history', () => { h.addToHistory(''); h.addToHistory(' '); const val = h.navigateUp(''); expect(val).toBeNull(); }); it('navigateDown after up returns saved input', () => { h.addToHistory('first'); const up = h.navigateUp('current'); expect(up).toBe('first'); const down = h.navigateDown(); expect(down).toBe('current'); }); it('does not add duplicate consecutive entries', () => { h.addToHistory('same'); h.addToHistory('same'); expect(h.getHistoryLength()).toBe(1); }); it('caps history at MAX_HISTORY entries', () => { for (let i = 0; i < 55; i++) { h.addToHistory(`entry-${i}`); } expect(h.getHistoryLength()).toBe(50); // Navigate to the oldest entry let val: string | null = null; for (let i = 0; i < 60; i++) { val = h.navigateUp(''); } // Oldest entry at index 49 = entry-5 (entries 54 down to 5, 50 total) expect(val).toBe('entry-5'); }); it('navigateUp returns null when history is empty', () => { const val = h.navigateUp('something'); expect(val).toBeNull(); }); it('navigateUp cycles through multiple entries', () => { h.addToHistory('a'); h.addToHistory('b'); h.addToHistory('c'); expect(h.navigateUp('')).toBe('c'); expect(h.navigateUp('c')).toBe('b'); expect(h.navigateUp('b')).toBe('a'); }); it('resetNavigation resets index to -1', () => { h.addToHistory('test'); h.navigateUp(''); h.resetNavigation(); // After reset, navigateUp from index -1 returns most recent again const val = h.navigateUp(''); expect(val).toBe('test'); }); });