diff options
| author | Evan Senter <[email protected]> | 2025-04-18 18:29:27 +0100 |
|---|---|---|
| committer | Evan Senter <[email protected]> | 2025-04-18 18:36:33 +0100 |
| commit | dbf4c3a37c55b8e14c9fefd1f839d3555072e17b (patch) | |
| tree | 8ae6e7136eaec78573cd86fb982e4a725079bfe7 /packages/cli/src/ui/hooks | |
| parent | f330a87e50fb6e8e4f1949851f8f6b6cf53d7776 (diff) | |
Revert "Including a test harness for it, and making sure the cursor is always at the end."
This reverts commit 97db77997fd6369031d2f1cf750051999fb0b5b5.
Diffstat (limited to 'packages/cli/src/ui/hooks')
| -rw-r--r-- | packages/cli/src/ui/hooks/useInputHistory.test.ts | 177 | ||||
| -rw-r--r-- | packages/cli/src/ui/hooks/useInputHistory.ts | 92 |
2 files changed, 0 insertions, 269 deletions
diff --git a/packages/cli/src/ui/hooks/useInputHistory.test.ts b/packages/cli/src/ui/hooks/useInputHistory.test.ts deleted file mode 100644 index 34af31b2..00000000 --- a/packages/cli/src/ui/hooks/useInputHistory.test.ts +++ /dev/null @@ -1,177 +0,0 @@ -// packages/cli/src/ui/hooks/useInputHistory.test.ts -import { renderHook, act } from '@testing-library/react'; -import { useInput } from 'ink'; -import { vi, describe, test, expect, beforeEach, Mock } from 'vitest'; -import { useInputHistory } from './useInputHistory.js'; - -// Mock the useInput hook from Ink -vi.mock('ink', async (importOriginal) => { - const originalInk = await importOriginal<typeof import('ink')>(); - return { - ...originalInk, // Keep other exports - useInput: vi.fn(), // Mock useInput - }; -}); - -// Helper type for the mocked useInput callback -type UseInputCallback = (input: string, key: any) => void; - -describe('useInputHistory Hook', () => { - let mockUseInputCallback: UseInputCallback | undefined; - const mockUserMessages = ['msg1', 'msg2', 'msg3']; // Sample history - - beforeEach(() => { - // Reset the mock before each test and capture the callback - (useInput as Mock).mockImplementation((callback, options) => { - // Only store the callback if the hook is active in the test - if (options?.isActive !== false) { - mockUseInputCallback = callback; - } else { - mockUseInputCallback = undefined; - } - }); - }); - - // Helper function to simulate key press by invoking the captured callback - const simulateKeyPress = (key: object, input: string = '') => { - act(() => { - if (mockUseInputCallback) { - mockUseInputCallback(input, key); - } else { - // Optionally throw an error if trying to press key when inactive - // console.warn('Simulated key press while useInput was inactive'); - } - }); - }; - - test('should initialize with empty query', () => { - const { result } = renderHook(() => - useInputHistory({ userMessages: [], isActive: true }), - ); - expect(result.current.query).toBe(''); - }); - - test('up arrow should do nothing if history is empty', () => { - const { result } = renderHook(() => - useInputHistory({ userMessages: [], isActive: true }), - ); - simulateKeyPress({ upArrow: true }); - expect(result.current.query).toBe(''); - }); - - test('up arrow should recall the last message', () => { - const { result } = renderHook(() => - useInputHistory({ userMessages: mockUserMessages, isActive: true }), - ); - simulateKeyPress({ upArrow: true }); - expect(result.current.query).toBe('msg3'); // Last message - }); - - test('repeated up arrows should navigate history', () => { - const { result } = renderHook(() => - useInputHistory({ userMessages: mockUserMessages, isActive: true }), - ); - simulateKeyPress({ upArrow: true }); // -> msg3 - simulateKeyPress({ upArrow: true }); // -> msg2 - expect(result.current.query).toBe('msg2'); - simulateKeyPress({ upArrow: true }); // -> msg1 - expect(result.current.query).toBe('msg1'); - simulateKeyPress({ upArrow: true }); // -> stays on msg1 - expect(result.current.query).toBe('msg1'); - }); - - test('down arrow should navigate history forward', () => { - const { result } = renderHook(() => - useInputHistory({ userMessages: mockUserMessages, isActive: true }), - ); - simulateKeyPress({ upArrow: true }); // -> msg3 - simulateKeyPress({ upArrow: true }); // -> msg2 - simulateKeyPress({ upArrow: true }); // -> msg1 - expect(result.current.query).toBe('msg1'); - - simulateKeyPress({ downArrow: true }); // -> msg2 - expect(result.current.query).toBe('msg2'); - simulateKeyPress({ downArrow: true }); // -> msg3 - expect(result.current.query).toBe('msg3'); - }); - - test('down arrow should restore original query', () => { - const { result } = renderHook(() => - useInputHistory({ userMessages: mockUserMessages, isActive: true }), - ); - - // Set initial query - act(() => { - result.current.setQuery('original typing'); - }); - expect(result.current.query).toBe('original typing'); - - simulateKeyPress({ upArrow: true }); // -> msg3 - expect(result.current.query).toBe('msg3'); - - simulateKeyPress({ downArrow: true }); // -> original typing - expect(result.current.query).toBe('original typing'); - - // Pressing down again should do nothing - simulateKeyPress({ downArrow: true }); - expect(result.current.query).toBe('original typing'); - }); - - test('typing should reset navigation', () => { - const { result } = renderHook(() => - useInputHistory({ userMessages: mockUserMessages, isActive: true }), - ); - - simulateKeyPress({ upArrow: true }); // -> msg3 - expect(result.current.query).toBe('msg3'); - - // Simulate typing 'x' (Note: we manually call setQuery here, as useInput is mocked) - act(() => { - result.current.setQuery(result.current.query + 'x'); - }); - // Also simulate the input event that would trigger the reset - simulateKeyPress({}, 'x'); - expect(result.current.query).toBe('msg3x'); - - simulateKeyPress({ upArrow: true }); // Should restart navigation -> msg3 - expect(result.current.query).toBe('msg3'); - }); - - test('calling resetHistoryNav should clear navigation state', () => { - const { result } = renderHook(() => - useInputHistory({ userMessages: mockUserMessages, isActive: true }), - ); - - // Set initial query and navigate - act(() => { - result.current.setQuery('original'); - }); - simulateKeyPress({ upArrow: true }); // -> msg3 - expect(result.current.query).toBe('msg3'); - - // Reset - act(() => { - result.current.resetHistoryNav(); - }); - - // Press down - should restore original query ('original') because nav was reset - // However, our current resetHistoryNav also clears originalQueryBeforeNav. - // Let's test that down does nothing because historyIndex is -1 - simulateKeyPress({ downArrow: true }); - expect(result.current.query).toBe('msg3'); // Stays msg3 because downArrow doesn't run when index is -1 - - // Press up - should start nav again from the top - simulateKeyPress({ upArrow: true }); - expect(result.current.query).toBe('msg3'); - }); - - test('should not trigger callback if isActive is false', () => { - renderHook(() => - useInputHistory({ userMessages: mockUserMessages, isActive: false }), - ); - // mockUseInputCallback should be undefined because isActive was false - expect(mockUseInputCallback).toBeUndefined(); - // Attempting to simulate should not throw error (or check internal state if possible) - expect(() => simulateKeyPress({ upArrow: true })).not.toThrow(); - }); -}); diff --git a/packages/cli/src/ui/hooks/useInputHistory.ts b/packages/cli/src/ui/hooks/useInputHistory.ts deleted file mode 100644 index bdd02e36..00000000 --- a/packages/cli/src/ui/hooks/useInputHistory.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { useState, useCallback } from 'react'; -import { useInput } from 'ink'; - -interface UseInputHistoryProps { - userMessages: readonly string[]; // Use readonly string[] instead - isActive: boolean; // To enable/disable the useInput hook -} - -interface UseInputHistoryReturn { - query: string; - setQuery: React.Dispatch<React.SetStateAction<string>>; - resetHistoryNav: () => void; - inputKey: number; // Key to force input reset -} - -export function useInputHistory({ - userMessages, - isActive, -}: UseInputHistoryProps): UseInputHistoryReturn { - const [query, setQuery] = useState(''); - const [historyIndex, setHistoryIndex] = useState<number>(-1); // -1 means current query - const [originalQueryBeforeNav, setOriginalQueryBeforeNav] = - useState<string>(''); - const [inputKey, setInputKey] = useState<number>(0); // Add key state - - const resetHistoryNav = useCallback(() => { - setHistoryIndex(-1); - setOriginalQueryBeforeNav(''); - // Don't reset inputKey here, only on explicit nav actions - }, []); - - useInput( - (input, key) => { - // Do nothing if the hook is not active - if (!isActive) { - return; - } - - if (key.upArrow) { - if (userMessages.length === 0) return; - // Store current query if starting navigation - if (historyIndex === -1) { - setOriginalQueryBeforeNav(query); - } - const nextIndex = Math.min(historyIndex + 1, userMessages.length - 1); - if (nextIndex !== historyIndex) { - setHistoryIndex(nextIndex); - const newValue = userMessages[userMessages.length - 1 - nextIndex]; - setQuery(newValue); - setInputKey(k => k + 1); // Increment key on navigation change - } - } else if (key.downArrow) { - if (historyIndex < 0) return; // Already at the bottom - const nextIndex = Math.max(historyIndex - 1, -1); - setHistoryIndex(nextIndex); - if (nextIndex === -1) { - // Restore original query - setQuery(originalQueryBeforeNav); - setInputKey(k => k + 1); // Increment key on navigation change - } else { - // Set query based on reversed index - const newValue = userMessages[userMessages.length - 1 - nextIndex]; - setQuery(newValue); - setInputKey(k => k + 1); // Increment key on navigation change - } - } else { - // If user types anything other than arrows, reset history navigation state - // This check might be too broad, adjust if handling more special keys - if ( - input || - key.backspace || - key.delete || - key.leftArrow || - key.rightArrow - ) { - if (historyIndex !== -1) { - resetHistoryNav(); - // Note: The actual input change is handled by the component using setQuery/onChange - } - } - } - }, - { isActive }, // Pass isActive to useInput - ); - - return { - query, - setQuery, // Return setQuery directly for flexibility - resetHistoryNav, - inputKey, // Return the key - }; -} |
