/** * @vitest-environment happy-dom */ import { describe, it, expect, vi } from 'vitest'; import { render } from 'ink-testing-library'; import React from 'react'; import { Select } from '../../../components/common/Select.js'; // Helper to wait for async updates const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); interface TestItem { label: string; value: string; } describe('Select', () => { const mockItems: TestItem[] = [ { label: 'Option 1', value: 'opt1' }, { label: 'Option 2', value: 'opt2' }, { label: 'Option 3', value: 'opt3' }, { label: 'Option 4', value: 'opt4' }, { label: 'Option 5', value: 'opt5' }, ]; describe('Rendering', () => { it('should render all items', () => { const onSelect = vi.fn(); const { lastFrame } = render(); // Cyan color code indicates selected item expect(lastFrame()).toContain('›'); }); it('should highlight item at initialIndex', () => { const onSelect = vi.fn(); const { lastFrame } = render( ); expect(lastFrame()).toBeDefined(); }); it('should respect limit prop for scrolling', () => { const onSelect = vi.fn(); const { lastFrame } = render(); // Verify component renders (implementation uses Math.max/min for boundaries) expect(lastFrame()).toBeDefined(); expect(lastFrame()).toContain('Option 1'); }); it('should start at first item by default', () => { const onSelect = vi.fn(); const { lastFrame } = render( ); // Should start at last item (index 4) const output = lastFrame(); expect(output).toContain('Option 5'); expect(output).toContain('›'); }); it('should handle initialIndex at 0', () => { const onSelect = vi.fn(); const { lastFrame } = render( ); // Component should handle input without errors expect(() => stdin.write('\u001B[B')).not.toThrow(); expect(() => stdin.write('\u001B[A')).not.toThrow(); expect(() => stdin.write('j')).not.toThrow(); expect(() => stdin.write('k')).not.toThrow(); }); it('should support vim-style navigation keys (j/k)', () => { const onSelect = vi.fn(); const { stdin } = render(); // Should accept arrow keys expect(() => stdin.write('\u001B[A')).not.toThrow(); // Up expect(() => stdin.write('\u001B[B')).not.toThrow(); // Down }); }); describe('Selection', () => { it('should call onSelect when Enter is pressed', () => { const onSelect = vi.fn(); const { stdin } = render(); // onSelect should be configured to receive item objects // Actual keyboard testing is limited by ink-testing-library expect(onSelect).toBeInstanceOf(Function); }); it('should handle Enter key without errors', () => { const onSelect = vi.fn(); const { stdin } = render(); const output = lastFrame(); // Should show limited items initially expect(output).toContain('Option 1'); expect(output).toContain('Option 2'); expect(output).toContain('Option 3'); }); it('should handle limit smaller than items length', () => { const onSelect = vi.fn(); const { lastFrame } = render(); // Should show all items without error const output = lastFrame(); expect(output).toContain('Option 1'); expect(output).toContain('Option 5'); }); }); describe('Key propagation (Critical Feature)', () => { it('should not interfere with other keys like q', () => { const onSelect = vi.fn(); const { stdin } = render(); stdin.write('m'); stdin.write('n'); stdin.write('c'); // None of these should trigger selection expect(onSelect).not.toHaveBeenCalled(); }); }); });