mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-14 21:37:20 -07:00
refactor(cli): integrate real config loading into async test utils (#23040)
This commit is contained in:
committed by
GitHub
parent
7de0616229
commit
23264ced9a
@@ -505,9 +505,7 @@ export const useSlashCommandProcessor = (
|
||||
const props = result.props as Record<string, unknown>;
|
||||
if (
|
||||
!props ||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof props['name'] !== 'string' ||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof props['displayName'] !== 'string' ||
|
||||
!props['definition']
|
||||
) {
|
||||
|
||||
@@ -38,6 +38,17 @@ vi.mock('./useAtCompletion', () => ({
|
||||
useAtCompletion: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('./usePromptCompletion', () => ({
|
||||
usePromptCompletion: vi.fn(() => ({
|
||||
text: '',
|
||||
isLoading: false,
|
||||
isActive: false,
|
||||
accept: vi.fn(),
|
||||
clear: vi.fn(),
|
||||
markSelected: vi.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock('./useSlashCompletion', () => ({
|
||||
useSlashCompletion: vi.fn(() => ({
|
||||
completionStart: 0,
|
||||
@@ -183,13 +194,13 @@ describe('useCommandCompletion', () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const renderCommandCompletionHook = (
|
||||
const renderCommandCompletionHook = async (
|
||||
initialText: string,
|
||||
cursorOffset?: number,
|
||||
shellModeActive = false,
|
||||
active = true,
|
||||
) => {
|
||||
const renderResult = renderWithProviders(
|
||||
const renderResult = await renderWithProviders(
|
||||
<TestComponent
|
||||
initialText={initialText}
|
||||
cursorOffset={cursorOffset}
|
||||
@@ -219,8 +230,8 @@ describe('useCommandCompletion', () => {
|
||||
|
||||
describe('Core Hook Behavior', () => {
|
||||
describe('State Management', () => {
|
||||
it('should initialize with default state', () => {
|
||||
const { result } = renderCommandCompletionHook('');
|
||||
it('should initialize with default state', async () => {
|
||||
const { result } = await renderCommandCompletionHook('');
|
||||
|
||||
expect(result.current.suggestions).toEqual([]);
|
||||
expect(result.current.activeSuggestionIndex).toBe(-1);
|
||||
@@ -235,7 +246,7 @@ describe('useCommandCompletion', () => {
|
||||
atSuggestions: [{ label: 'src/file.txt', value: 'src/file.txt' }],
|
||||
});
|
||||
|
||||
const { result } = renderCommandCompletionHook('@file');
|
||||
const { result } = await renderCommandCompletionHook('@file');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toHaveLength(1);
|
||||
@@ -256,8 +267,8 @@ describe('useCommandCompletion', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should reset all state to default values', () => {
|
||||
const { result } = renderCommandCompletionHook('@files');
|
||||
it('should reset all state to default values', async () => {
|
||||
const { result } = await renderCommandCompletionHook('@files');
|
||||
|
||||
act(() => {
|
||||
result.current.setActiveSuggestionIndex(5);
|
||||
@@ -274,7 +285,7 @@ describe('useCommandCompletion', () => {
|
||||
|
||||
it('should call useAtCompletion with the correct query for an escaped space', async () => {
|
||||
const text = '@src/a\\ file.txt';
|
||||
const { result } = renderCommandCompletionHook(text);
|
||||
const { result } = await renderCommandCompletionHook(text);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(useAtCompletion).toHaveBeenLastCalledWith(
|
||||
@@ -291,7 +302,7 @@ describe('useCommandCompletion', () => {
|
||||
const text = '@file1 @file2';
|
||||
const cursorOffset = 3; // @fi|le1 @file2
|
||||
|
||||
renderCommandCompletionHook(text, cursorOffset);
|
||||
await renderCommandCompletionHook(text, cursorOffset);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(useAtCompletion).toHaveBeenLastCalledWith(
|
||||
@@ -329,7 +340,7 @@ describe('useCommandCompletion', () => {
|
||||
slashSuggestions: [{ label: 'clear', value: 'clear' }],
|
||||
});
|
||||
|
||||
const { result } = renderCommandCompletionHook(
|
||||
const { result } = await renderCommandCompletionHook(
|
||||
'/',
|
||||
undefined,
|
||||
shellModeActive,
|
||||
@@ -361,10 +372,10 @@ describe('useCommandCompletion', () => {
|
||||
setupMocks({ slashSuggestions: mockSuggestions });
|
||||
});
|
||||
|
||||
it('should handle navigateUp with no suggestions', () => {
|
||||
it('should handle navigateUp with no suggestions', async () => {
|
||||
setupMocks({ slashSuggestions: [] });
|
||||
|
||||
const { result } = renderCommandCompletionHook('/');
|
||||
const { result } = await renderCommandCompletionHook('/');
|
||||
|
||||
act(() => {
|
||||
result.current.navigateUp();
|
||||
@@ -373,9 +384,9 @@ describe('useCommandCompletion', () => {
|
||||
expect(result.current.activeSuggestionIndex).toBe(-1);
|
||||
});
|
||||
|
||||
it('should handle navigateDown with no suggestions', () => {
|
||||
it('should handle navigateDown with no suggestions', async () => {
|
||||
setupMocks({ slashSuggestions: [] });
|
||||
const { result } = renderCommandCompletionHook('/');
|
||||
const { result } = await renderCommandCompletionHook('/');
|
||||
|
||||
act(() => {
|
||||
result.current.navigateDown();
|
||||
@@ -385,7 +396,7 @@ describe('useCommandCompletion', () => {
|
||||
});
|
||||
|
||||
it('should navigate up through suggestions with wrap-around', async () => {
|
||||
const { result } = renderCommandCompletionHook('/');
|
||||
const { result } = await renderCommandCompletionHook('/');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBe(5);
|
||||
@@ -401,7 +412,7 @@ describe('useCommandCompletion', () => {
|
||||
});
|
||||
|
||||
it('should navigate down through suggestions with wrap-around', async () => {
|
||||
const { result } = renderCommandCompletionHook('/');
|
||||
const { result } = await renderCommandCompletionHook('/');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBe(5);
|
||||
@@ -420,7 +431,7 @@ describe('useCommandCompletion', () => {
|
||||
});
|
||||
|
||||
it('should handle navigation with multiple suggestions', async () => {
|
||||
const { result } = renderCommandCompletionHook('/');
|
||||
const { result } = await renderCommandCompletionHook('/');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBe(5);
|
||||
@@ -447,7 +458,7 @@ describe('useCommandCompletion', () => {
|
||||
it('should automatically select the first item when suggestions are available', async () => {
|
||||
setupMocks({ slashSuggestions: mockSuggestions });
|
||||
|
||||
const { result } = renderCommandCompletionHook('/');
|
||||
const { result } = await renderCommandCompletionHook('/');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBe(
|
||||
@@ -466,7 +477,7 @@ describe('useCommandCompletion', () => {
|
||||
slashCompletionRange: { completionStart: 1, completionEnd: 4 },
|
||||
});
|
||||
|
||||
const { result } = renderCommandCompletionHook('/mem');
|
||||
const { result } = await renderCommandCompletionHook('/mem');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBe(1);
|
||||
@@ -484,7 +495,7 @@ describe('useCommandCompletion', () => {
|
||||
atSuggestions: [{ label: 'src/file1.txt', value: 'src/file1.txt' }],
|
||||
});
|
||||
|
||||
const { result } = renderCommandCompletionHook('@src/fi');
|
||||
const { result } = await renderCommandCompletionHook('@src/fi');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBe(1);
|
||||
@@ -509,7 +520,7 @@ describe('useCommandCompletion', () => {
|
||||
slashCompletionRange: { completionStart: 1, completionEnd: 5 },
|
||||
});
|
||||
|
||||
const { result } = renderCommandCompletionHook('/resu');
|
||||
const { result } = await renderCommandCompletionHook('/resu');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBe(1);
|
||||
@@ -530,7 +541,7 @@ describe('useCommandCompletion', () => {
|
||||
atSuggestions: [{ label: 'src/file1.txt', value: 'src/file1.txt' }],
|
||||
});
|
||||
|
||||
const { result } = renderCommandCompletionHook(text, cursorOffset);
|
||||
const { result } = await renderCommandCompletionHook(text, cursorOffset);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBe(1);
|
||||
@@ -550,7 +561,7 @@ describe('useCommandCompletion', () => {
|
||||
atSuggestions: [{ label: 'src/components/', value: 'src/components/' }],
|
||||
});
|
||||
|
||||
const { result } = renderCommandCompletionHook('@src/comp');
|
||||
const { result } = await renderCommandCompletionHook('@src/comp');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBe(1);
|
||||
@@ -570,7 +581,7 @@ describe('useCommandCompletion', () => {
|
||||
],
|
||||
});
|
||||
|
||||
const { result } = renderCommandCompletionHook('@src\\comp');
|
||||
const { result } = await renderCommandCompletionHook('@src\\comp');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBe(1);
|
||||
@@ -595,7 +606,7 @@ describe('useCommandCompletion', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const { result } = renderCommandCompletionHook(
|
||||
const { result } = await renderCommandCompletionHook(
|
||||
text,
|
||||
text.length,
|
||||
true, // shellModeActive
|
||||
@@ -624,7 +635,7 @@ describe('useCommandCompletion', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const { result } = renderCommandCompletionHook(
|
||||
const { result } = await renderCommandCompletionHook(
|
||||
text,
|
||||
text.length,
|
||||
true, // shellModeActive
|
||||
@@ -642,7 +653,7 @@ describe('useCommandCompletion', () => {
|
||||
const text = 'ls ';
|
||||
const cursorOffset = text.length;
|
||||
|
||||
const { result } = renderCommandCompletionHook(
|
||||
const { result } = await renderCommandCompletionHook(
|
||||
text,
|
||||
cursorOffset,
|
||||
true, // shellModeActive
|
||||
@@ -668,7 +679,7 @@ describe('useCommandCompletion', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const { result } = renderCommandCompletionHook(
|
||||
const { result } = await renderCommandCompletionHook(
|
||||
textWithoutSpace,
|
||||
textWithoutSpace.length,
|
||||
true, // shellModeActive
|
||||
@@ -733,7 +744,7 @@ describe('useCommandCompletion', () => {
|
||||
hookResult = { ...completion, textBuffer };
|
||||
return null;
|
||||
}
|
||||
renderWithProviders(<TestComponent />);
|
||||
await renderWithProviders(<TestComponent />);
|
||||
|
||||
// Should not trigger prompt completion for comments
|
||||
await waitFor(() => {
|
||||
@@ -768,7 +779,7 @@ describe('useCommandCompletion', () => {
|
||||
hookResult = { ...completion, textBuffer };
|
||||
return null;
|
||||
}
|
||||
renderWithProviders(<TestComponent />);
|
||||
await renderWithProviders(<TestComponent />);
|
||||
|
||||
// Should not trigger prompt completion for comments
|
||||
await waitFor(() => {
|
||||
@@ -803,7 +814,7 @@ describe('useCommandCompletion', () => {
|
||||
hookResult = { ...completion, textBuffer };
|
||||
return null;
|
||||
}
|
||||
renderWithProviders(<TestComponent />);
|
||||
await renderWithProviders(<TestComponent />);
|
||||
|
||||
// This test verifies that comments are filtered out while regular text is not
|
||||
await waitFor(() => {
|
||||
@@ -823,7 +834,7 @@ describe('useCommandCompletion', () => {
|
||||
const text = '/mycommand @src/fi';
|
||||
const cursorOffset = text.length;
|
||||
|
||||
renderCommandCompletionHook(text, cursorOffset);
|
||||
await renderCommandCompletionHook(text, cursorOffset);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(useAtCompletion).toHaveBeenLastCalledWith(
|
||||
@@ -843,7 +854,7 @@ describe('useCommandCompletion', () => {
|
||||
const text = '/mycom';
|
||||
const cursorOffset = text.length;
|
||||
|
||||
const { result } = renderCommandCompletionHook(text, cursorOffset);
|
||||
const { result } = await renderCommandCompletionHook(text, cursorOffset);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions).toHaveLength(1);
|
||||
@@ -859,7 +870,7 @@ describe('useCommandCompletion', () => {
|
||||
const text = '/command @';
|
||||
const cursorOffset = text.length;
|
||||
|
||||
renderCommandCompletionHook(text, cursorOffset);
|
||||
await renderCommandCompletionHook(text, cursorOffset);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(useAtCompletion).toHaveBeenLastCalledWith(
|
||||
@@ -879,7 +890,7 @@ describe('useCommandCompletion', () => {
|
||||
const text = '/diff @src/foo.ts @src/ba';
|
||||
const cursorOffset = text.length;
|
||||
|
||||
renderCommandCompletionHook(text, cursorOffset);
|
||||
await renderCommandCompletionHook(text, cursorOffset);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(useAtCompletion).toHaveBeenLastCalledWith(
|
||||
@@ -896,7 +907,7 @@ describe('useCommandCompletion', () => {
|
||||
atSuggestions: [{ label: 'src/file.txt', value: 'src/file.txt' }],
|
||||
});
|
||||
|
||||
const { result } = renderCommandCompletionHook('/cmd @src/fi');
|
||||
const { result } = await renderCommandCompletionHook('/cmd @src/fi');
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.suggestions.length).toBe(1);
|
||||
@@ -915,7 +926,7 @@ describe('useCommandCompletion', () => {
|
||||
});
|
||||
|
||||
const text = '/help ';
|
||||
renderCommandCompletionHook(text);
|
||||
await renderCommandCompletionHook(text);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(useSlashCompletion).toHaveBeenLastCalledWith(
|
||||
|
||||
@@ -47,13 +47,13 @@ describe('useFocus', () => {
|
||||
stdin.removeAllListeners();
|
||||
});
|
||||
|
||||
const renderFocusHook = () => {
|
||||
const renderFocusHook = async () => {
|
||||
let hookResult: ReturnType<typeof useFocus>;
|
||||
function TestComponent() {
|
||||
hookResult = useFocus();
|
||||
return null;
|
||||
}
|
||||
const { unmount } = renderWithProviders(<TestComponent />);
|
||||
const { unmount } = await renderWithProviders(<TestComponent />);
|
||||
return {
|
||||
result: {
|
||||
get current() {
|
||||
@@ -64,15 +64,15 @@ describe('useFocus', () => {
|
||||
};
|
||||
};
|
||||
|
||||
it('should initialize with focus and enable focus reporting', () => {
|
||||
const { result } = renderFocusHook();
|
||||
it('should initialize with focus and enable focus reporting', async () => {
|
||||
const { result } = await renderFocusHook();
|
||||
|
||||
expect(result.current.isFocused).toBe(true);
|
||||
expect(stdout.write).toHaveBeenCalledWith('\x1b[?1004h');
|
||||
});
|
||||
|
||||
it('should set isFocused to false when a focus-out event is received', () => {
|
||||
const { result } = renderFocusHook();
|
||||
it('should set isFocused to false when a focus-out event is received', async () => {
|
||||
const { result } = await renderFocusHook();
|
||||
|
||||
// Initial state is focused
|
||||
expect(result.current.isFocused).toBe(true);
|
||||
@@ -86,8 +86,8 @@ describe('useFocus', () => {
|
||||
expect(result.current.isFocused).toBe(false);
|
||||
});
|
||||
|
||||
it('should set isFocused to true when a focus-in event is received', () => {
|
||||
const { result } = renderFocusHook();
|
||||
it('should set isFocused to true when a focus-in event is received', async () => {
|
||||
const { result } = await renderFocusHook();
|
||||
|
||||
// Simulate focus-out to set initial state to false
|
||||
act(() => {
|
||||
@@ -104,8 +104,8 @@ describe('useFocus', () => {
|
||||
expect(result.current.isFocused).toBe(true);
|
||||
});
|
||||
|
||||
it('should clean up and disable focus reporting on unmount', () => {
|
||||
const { unmount } = renderFocusHook();
|
||||
it('should clean up and disable focus reporting on unmount', async () => {
|
||||
const { unmount } = await renderFocusHook();
|
||||
|
||||
// At this point we should have listeners from both KeypressProvider and useFocus
|
||||
const listenerCountAfterMount = stdin.listenerCount('data');
|
||||
@@ -119,8 +119,8 @@ describe('useFocus', () => {
|
||||
expect(stdin.listenerCount('data')).toBeLessThan(listenerCountAfterMount);
|
||||
});
|
||||
|
||||
it('should handle multiple focus events correctly', () => {
|
||||
const { result } = renderFocusHook();
|
||||
it('should handle multiple focus events correctly', async () => {
|
||||
const { result } = await renderFocusHook();
|
||||
|
||||
act(() => {
|
||||
stdin.emit('data', '\x1b[O');
|
||||
@@ -143,8 +143,8 @@ describe('useFocus', () => {
|
||||
expect(result.current.isFocused).toBe(true);
|
||||
});
|
||||
|
||||
it('restores focus on keypress after focus is lost', () => {
|
||||
const { result } = renderFocusHook();
|
||||
it('restores focus on keypress after focus is lost', async () => {
|
||||
const { result } = await renderFocusHook();
|
||||
|
||||
// Simulate focus-out event
|
||||
act(() => {
|
||||
@@ -159,8 +159,8 @@ describe('useFocus', () => {
|
||||
expect(result.current.isFocused).toBe(true);
|
||||
});
|
||||
|
||||
it('tracks whether any focus event has been received', () => {
|
||||
const { result } = renderFocusHook();
|
||||
it('tracks whether any focus event has been received', async () => {
|
||||
const { result } = await renderFocusHook();
|
||||
|
||||
expect(result.current.hasReceivedFocusEvent).toBe(false);
|
||||
|
||||
|
||||
@@ -375,7 +375,7 @@ describe('useGeminiStream', () => {
|
||||
setValue: vi.fn(),
|
||||
} as unknown as LoadedSettings;
|
||||
|
||||
const renderTestHook = (
|
||||
const renderTestHook = async (
|
||||
initialToolCalls: TrackedToolCall[] = [],
|
||||
geminiClient?: any,
|
||||
loadedSettings: LoadedSettings = mockLoadedSettings,
|
||||
@@ -436,7 +436,7 @@ describe('useGeminiStream', () => {
|
||||
];
|
||||
});
|
||||
|
||||
const { result, rerender } = renderHookWithProviders(
|
||||
const { result, rerender } = await renderHookWithProviders(
|
||||
(props: typeof initialProps) =>
|
||||
useGeminiStream(
|
||||
props.client,
|
||||
@@ -518,7 +518,7 @@ describe('useGeminiStream', () => {
|
||||
});
|
||||
|
||||
// Helper to render hook with default parameters - reduces boilerplate
|
||||
const renderHookWithDefaults = (
|
||||
const renderHookWithDefaults = async (
|
||||
options: {
|
||||
shellModeActive?: boolean;
|
||||
onCancelSubmit?: () => void;
|
||||
@@ -562,7 +562,7 @@ describe('useGeminiStream', () => {
|
||||
);
|
||||
};
|
||||
|
||||
it('should not submit tool responses if not all tool calls are completed', () => {
|
||||
it('should not submit tool responses if not all tool calls are completed', async () => {
|
||||
const toolCalls: TrackedToolCall[] = [
|
||||
{
|
||||
request: {
|
||||
@@ -617,7 +617,7 @@ describe('useGeminiStream', () => {
|
||||
];
|
||||
|
||||
const { mockMarkToolsAsSubmitted, mockSendMessageStream } =
|
||||
renderTestHook(toolCalls);
|
||||
await renderTestHook(toolCalls);
|
||||
|
||||
// Effect for submitting tool responses depends on toolCalls and isResponding
|
||||
// isResponding is initially false, so the effect should run.
|
||||
@@ -626,7 +626,7 @@ describe('useGeminiStream', () => {
|
||||
expect(mockSendMessageStream).not.toHaveBeenCalled(); // submitQuery uses this
|
||||
});
|
||||
|
||||
it('should expose activePtyId for non-shell executing tools that report an execution ID', () => {
|
||||
it('should expose activePtyId for non-shell executing tools that report an execution ID', async () => {
|
||||
const remoteExecutingTool: TrackedExecutingToolCall = {
|
||||
request: {
|
||||
callId: 'remote-call-1',
|
||||
@@ -651,7 +651,7 @@ describe('useGeminiStream', () => {
|
||||
pid: 4242,
|
||||
};
|
||||
|
||||
const { result } = renderTestHook([remoteExecutingTool]);
|
||||
const { result } = await renderTestHook([remoteExecutingTool]);
|
||||
expect(result.current.activePtyId).toBe(4242);
|
||||
});
|
||||
|
||||
@@ -716,7 +716,7 @@ describe('useGeminiStream', () => {
|
||||
];
|
||||
});
|
||||
|
||||
renderHookWithProviders(() =>
|
||||
await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
new MockedGeminiClientClass(mockConfig),
|
||||
[],
|
||||
@@ -817,7 +817,7 @@ describe('useGeminiStream', () => {
|
||||
];
|
||||
});
|
||||
|
||||
renderHookWithProviders(() =>
|
||||
await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
new MockedGeminiClientClass(mockConfig),
|
||||
[],
|
||||
@@ -927,7 +927,7 @@ describe('useGeminiStream', () => {
|
||||
];
|
||||
});
|
||||
|
||||
renderHookWithProviders(() =>
|
||||
await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
client,
|
||||
[],
|
||||
@@ -998,7 +998,7 @@ describe('useGeminiStream', () => {
|
||||
];
|
||||
const client = new MockedGeminiClientClass(mockConfig);
|
||||
|
||||
const { result } = renderTestHook([], client);
|
||||
const { result } = await renderTestHook([], client);
|
||||
|
||||
// Trigger the onComplete callback with STOP_EXECUTION tool
|
||||
await act(async () => {
|
||||
@@ -1077,7 +1077,7 @@ describe('useGeminiStream', () => {
|
||||
} as LoadedSettings;
|
||||
const client = new MockedGeminiClientClass(mockConfig);
|
||||
|
||||
const { result } = renderTestHook([], client, lowVerbositySettings);
|
||||
const { result } = await renderTestHook([], client, lowVerbositySettings);
|
||||
|
||||
await act(async () => {
|
||||
if (capturedOnComplete) {
|
||||
@@ -1190,7 +1190,7 @@ describe('useGeminiStream', () => {
|
||||
];
|
||||
});
|
||||
|
||||
renderHookWithProviders(() =>
|
||||
await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
client,
|
||||
[],
|
||||
@@ -1307,7 +1307,7 @@ describe('useGeminiStream', () => {
|
||||
];
|
||||
});
|
||||
|
||||
const { result, rerender } = renderHookWithProviders(() =>
|
||||
const { result, rerender } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
new MockedGeminiClientClass(mockConfig),
|
||||
[],
|
||||
@@ -1408,7 +1408,7 @@ describe('useGeminiStream', () => {
|
||||
})();
|
||||
mockSendMessageStream.mockReturnValue(mockStream);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
// Start a query
|
||||
await act(async () => {
|
||||
@@ -1445,7 +1445,7 @@ describe('useGeminiStream', () => {
|
||||
})();
|
||||
mockSendMessageStream.mockReturnValue(mockStream);
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
mockConfig.getGeminiClient(),
|
||||
[],
|
||||
@@ -1486,7 +1486,7 @@ describe('useGeminiStream', () => {
|
||||
})();
|
||||
mockSendMessageStream.mockReturnValue(mockStream);
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
mockConfig.getGeminiClient(),
|
||||
[],
|
||||
@@ -1519,8 +1519,8 @@ describe('useGeminiStream', () => {
|
||||
expect(setShellInputFocusedSpy).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('should not do anything if escape is pressed when not responding', () => {
|
||||
const { result } = renderTestHook();
|
||||
it('should not do anything if escape is pressed when not responding', async () => {
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
expect(result.current.streamingState).toBe(StreamingState.Idle);
|
||||
|
||||
@@ -1548,7 +1548,7 @@ describe('useGeminiStream', () => {
|
||||
})();
|
||||
mockSendMessageStream.mockReturnValue(mockStream);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
@@ -1600,7 +1600,7 @@ describe('useGeminiStream', () => {
|
||||
} as TrackedExecutingToolCall,
|
||||
];
|
||||
|
||||
const { result } = renderTestHook(toolCalls);
|
||||
const { result } = await renderTestHook(toolCalls);
|
||||
|
||||
// State is `Responding` because a tool is running
|
||||
expect(result.current.streamingState).toBe(StreamingState.Responding);
|
||||
@@ -1648,7 +1648,7 @@ describe('useGeminiStream', () => {
|
||||
} as TrackedWaitingToolCall,
|
||||
];
|
||||
|
||||
const { result } = renderTestHook(toolCalls);
|
||||
const { result } = await renderTestHook(toolCalls);
|
||||
|
||||
// State is `WaitingForConfirmation` because a tool is awaiting approval
|
||||
expect(result.current.streamingState).toBe(
|
||||
@@ -1677,7 +1677,7 @@ describe('useGeminiStream', () => {
|
||||
|
||||
describe('Retry Handling', () => {
|
||||
it('should update retryStatus when CoreEvent.RetryAttempt is emitted', async () => {
|
||||
const { result } = renderHookWithDefaults();
|
||||
const { result } = await renderHookWithDefaults();
|
||||
|
||||
const retryPayload = {
|
||||
model: 'gemini-2.5-pro',
|
||||
@@ -1694,7 +1694,7 @@ describe('useGeminiStream', () => {
|
||||
});
|
||||
|
||||
it('should reset retryStatus when isResponding becomes false', async () => {
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
const retryPayload = {
|
||||
model: 'gemini-2.5-pro',
|
||||
@@ -1744,7 +1744,7 @@ describe('useGeminiStream', () => {
|
||||
};
|
||||
mockHandleSlashCommand.mockResolvedValue(clientToolRequest);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('/memory add "test fact"');
|
||||
@@ -1771,7 +1771,7 @@ describe('useGeminiStream', () => {
|
||||
};
|
||||
mockHandleSlashCommand.mockResolvedValue(uiOnlyCommandResult);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('/help');
|
||||
@@ -1792,7 +1792,7 @@ describe('useGeminiStream', () => {
|
||||
mockHandleSlashCommand.mockResolvedValue(customCommandResult);
|
||||
|
||||
const { result, mockSendMessageStream: localMockSendMessageStream } =
|
||||
renderTestHook();
|
||||
await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('/my-custom-command');
|
||||
@@ -1830,7 +1830,7 @@ describe('useGeminiStream', () => {
|
||||
mockHandleSlashCommand.mockResolvedValue(emptyPromptResult);
|
||||
|
||||
const { result, mockSendMessageStream: localMockSendMessageStream } =
|
||||
renderTestHook();
|
||||
await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('/emptycmd');
|
||||
@@ -1851,7 +1851,7 @@ describe('useGeminiStream', () => {
|
||||
|
||||
it('should not call handleSlashCommand for line comments', async () => {
|
||||
const { result, mockSendMessageStream: localMockSendMessageStream } =
|
||||
renderTestHook();
|
||||
await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('// This is a line comment');
|
||||
@@ -1872,7 +1872,7 @@ describe('useGeminiStream', () => {
|
||||
|
||||
it('should not call handleSlashCommand for block comments', async () => {
|
||||
const { result, mockSendMessageStream: localMockSendMessageStream } =
|
||||
renderTestHook();
|
||||
await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('/* This is a block comment */');
|
||||
@@ -1892,7 +1892,7 @@ describe('useGeminiStream', () => {
|
||||
});
|
||||
|
||||
it('should not call handleSlashCommand is shell mode is active', async () => {
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
new MockedGeminiClientClass(mockConfig),
|
||||
[],
|
||||
@@ -1972,7 +1972,7 @@ describe('useGeminiStream', () => {
|
||||
];
|
||||
});
|
||||
|
||||
renderHookWithProviders(() =>
|
||||
await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
new MockedGeminiClientClass(mockConfig),
|
||||
[],
|
||||
@@ -2031,7 +2031,7 @@ describe('useGeminiStream', () => {
|
||||
getModel: vi.fn(() => 'gemini-2.5-pro'),
|
||||
} as unknown as Config;
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
new MockedGeminiClientClass(testConfig),
|
||||
[],
|
||||
@@ -2078,7 +2078,7 @@ describe('useGeminiStream', () => {
|
||||
createMockToolCall('read_file', 'call2', 'info'),
|
||||
];
|
||||
|
||||
const { result } = renderTestHook(awaitingApprovalToolCalls);
|
||||
const { result } = await renderTestHook(awaitingApprovalToolCalls);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.handleApprovalModeChange(ApprovalMode.YOLO);
|
||||
@@ -2108,7 +2108,7 @@ describe('useGeminiStream', () => {
|
||||
createMockToolCall('read_file', 'call3', 'info'),
|
||||
];
|
||||
|
||||
const { result } = renderTestHook(awaitingApprovalToolCalls);
|
||||
const { result } = await renderTestHook(awaitingApprovalToolCalls);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.handleApprovalModeChange(ApprovalMode.AUTO_EDIT);
|
||||
@@ -2132,7 +2132,7 @@ describe('useGeminiStream', () => {
|
||||
createMockToolCall('replace', 'call1', 'edit'),
|
||||
];
|
||||
|
||||
const { result } = renderTestHook(awaitingApprovalToolCalls);
|
||||
const { result } = await renderTestHook(awaitingApprovalToolCalls);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.handleApprovalModeChange(ApprovalMode.DEFAULT);
|
||||
@@ -2154,7 +2154,7 @@ describe('useGeminiStream', () => {
|
||||
createMockToolCall('write_file', 'call2', 'edit'),
|
||||
];
|
||||
|
||||
const { result } = renderTestHook(awaitingApprovalToolCalls);
|
||||
const { result } = await renderTestHook(awaitingApprovalToolCalls);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.handleApprovalModeChange(ApprovalMode.YOLO);
|
||||
@@ -2196,7 +2196,7 @@ describe('useGeminiStream', () => {
|
||||
} as unknown as TrackedWaitingToolCall,
|
||||
];
|
||||
|
||||
const { result } = renderTestHook(awaitingApprovalToolCalls);
|
||||
const { result } = await renderTestHook(awaitingApprovalToolCalls);
|
||||
|
||||
// Should not throw an error
|
||||
await act(async () => {
|
||||
@@ -2239,7 +2239,7 @@ describe('useGeminiStream', () => {
|
||||
} as TrackedExecutingToolCall,
|
||||
];
|
||||
|
||||
const { result } = renderTestHook(mixedStatusToolCalls);
|
||||
const { result } = await renderTestHook(mixedStatusToolCalls);
|
||||
|
||||
await act(async () => {
|
||||
await result.current.handleApprovalModeChange(ApprovalMode.YOLO);
|
||||
@@ -2260,7 +2260,7 @@ describe('useGeminiStream', () => {
|
||||
(mockConfig.getApprovalMode as Mock).mockReturnValue(ApprovalMode.PLAN);
|
||||
|
||||
// Render the hook, which will initialize the previousApprovalModeRef with PLAN
|
||||
const { result, client } = renderTestHook([]);
|
||||
const { result, client } = await renderTestHook([]);
|
||||
|
||||
// Update mockConfig to return DEFAULT mode (new mode)
|
||||
(mockConfig.getApprovalMode as Mock).mockReturnValue(
|
||||
@@ -2300,7 +2300,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
new MockedGeminiClientClass(mockConfig),
|
||||
[],
|
||||
@@ -2374,7 +2374,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderHookWithDefaults();
|
||||
const { result } = await renderHookWithDefaults();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('Test overflow');
|
||||
@@ -2405,7 +2405,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
new MockedGeminiClientClass(mockConfig),
|
||||
[],
|
||||
@@ -2454,7 +2454,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderHookWithDefaults();
|
||||
const { result } = await renderHookWithDefaults();
|
||||
|
||||
// Submit a query
|
||||
await act(async () => {
|
||||
@@ -2541,7 +2541,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderHookWithDefaults();
|
||||
const { result } = await renderHookWithDefaults();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery(`Test ${reason}`);
|
||||
@@ -2613,7 +2613,7 @@ describe('useGeminiStream', () => {
|
||||
];
|
||||
});
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
new MockedGeminiClientClass(mockConfig),
|
||||
[],
|
||||
@@ -2684,7 +2684,7 @@ describe('useGeminiStream', () => {
|
||||
shouldProceed: true,
|
||||
});
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
mockConfig.getGeminiClient(),
|
||||
[],
|
||||
@@ -2777,7 +2777,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery(userQuery);
|
||||
@@ -2849,7 +2849,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
new MockedGeminiClientClass(mockConfig),
|
||||
[],
|
||||
@@ -2904,7 +2904,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('Test query');
|
||||
@@ -2949,7 +2949,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
new MockedGeminiClientClass(mockConfig),
|
||||
[],
|
||||
@@ -3021,7 +3021,7 @@ describe('useGeminiStream', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should memoize pendingHistoryItems', () => {
|
||||
it('should memoize pendingHistoryItems', async () => {
|
||||
mockUseToolScheduler.mockReturnValue([
|
||||
[],
|
||||
mockScheduleToolCalls,
|
||||
@@ -3031,7 +3031,7 @@ describe('useGeminiStream', () => {
|
||||
0,
|
||||
]);
|
||||
|
||||
const { result, rerender } = renderHookWithProviders(() =>
|
||||
const { result, rerender } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
mockConfig.getGeminiClient(),
|
||||
[],
|
||||
@@ -3102,7 +3102,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
new MockedGeminiClientClass(mockConfig),
|
||||
[],
|
||||
@@ -3159,7 +3159,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
new MockedGeminiClientClass(mockConfig),
|
||||
[],
|
||||
@@ -3227,7 +3227,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useGeminiStream(
|
||||
new MockedGeminiClientClass(mockConfig),
|
||||
[],
|
||||
@@ -3287,7 +3287,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('test query');
|
||||
@@ -3334,7 +3334,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('test query');
|
||||
@@ -3399,7 +3399,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('test query');
|
||||
@@ -3434,7 +3434,7 @@ describe('useGeminiStream', () => {
|
||||
});
|
||||
|
||||
it('should handle multiple loop detection events properly', async () => {
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
// First loop detection - set up fresh mock for first call
|
||||
mockSendMessageStream.mockReturnValueOnce(
|
||||
@@ -3544,7 +3544,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('test query');
|
||||
@@ -3581,7 +3581,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
// Start first query without awaiting (fire-and-forget, like existing tests)
|
||||
await act(async () => {
|
||||
@@ -3637,7 +3637,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('test query');
|
||||
@@ -3692,7 +3692,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('test stop');
|
||||
@@ -3720,7 +3720,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('test stop');
|
||||
@@ -3751,7 +3751,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('test block');
|
||||
@@ -3778,7 +3778,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('test block');
|
||||
@@ -3807,7 +3807,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('user query');
|
||||
@@ -3855,7 +3855,7 @@ describe('useGeminiStream', () => {
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('user query');
|
||||
@@ -3881,7 +3881,7 @@ describe('useGeminiStream', () => {
|
||||
});
|
||||
|
||||
it('should trace UserPrompt telemetry on submitQuery', async () => {
|
||||
const { result } = renderTestHook();
|
||||
const { result } = await renderTestHook();
|
||||
|
||||
mockSendMessageStream.mockReturnValue(
|
||||
(async function* () {
|
||||
|
||||
@@ -43,7 +43,7 @@ describe(`useKeypress`, () => {
|
||||
const onKeypress = vi.fn();
|
||||
let originalNodeVersion: string;
|
||||
|
||||
const renderKeypressHook = (isActive = true) =>
|
||||
const renderKeypressHook = async (isActive = true) =>
|
||||
renderHookWithProviders(() => useKeypress(onKeypress, { isActive }));
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -66,8 +66,8 @@ describe(`useKeypress`, () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should not listen if isActive is false', () => {
|
||||
renderKeypressHook(false);
|
||||
it('should not listen if isActive is false', async () => {
|
||||
await renderKeypressHook(false);
|
||||
act(() => stdin.write('a'));
|
||||
expect(onKeypress).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -79,28 +79,31 @@ describe(`useKeypress`, () => {
|
||||
{ key: { name: 'up', sequence: '\x1b[A' } },
|
||||
{ key: { name: 'down', sequence: '\x1b[B' } },
|
||||
{ key: { name: 'tab', sequence: '\x1b[Z', shift: true } },
|
||||
])('should listen for keypress when active for key $key.name', ({ key }) => {
|
||||
renderKeypressHook(true);
|
||||
act(() => stdin.write(key.sequence));
|
||||
expect(onKeypress).toHaveBeenCalledWith(expect.objectContaining(key));
|
||||
});
|
||||
])(
|
||||
'should listen for keypress when active for key $key.name',
|
||||
async ({ key }) => {
|
||||
await renderKeypressHook(true);
|
||||
act(() => stdin.write(key.sequence));
|
||||
expect(onKeypress).toHaveBeenCalledWith(expect.objectContaining(key));
|
||||
},
|
||||
);
|
||||
|
||||
it('should set and release raw mode', () => {
|
||||
const { unmount } = renderKeypressHook(true);
|
||||
it('should set and release raw mode', async () => {
|
||||
const { unmount } = await renderKeypressHook(true);
|
||||
expect(mockSetRawMode).toHaveBeenCalledWith(true);
|
||||
unmount();
|
||||
expect(mockSetRawMode).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('should stop listening after being unmounted', () => {
|
||||
const { unmount } = renderKeypressHook(true);
|
||||
it('should stop listening after being unmounted', async () => {
|
||||
const { unmount } = await renderKeypressHook(true);
|
||||
unmount();
|
||||
act(() => stdin.write('a'));
|
||||
expect(onKeypress).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should correctly identify alt+enter (meta key)', () => {
|
||||
renderKeypressHook(true);
|
||||
it('should correctly identify alt+enter (meta key)', async () => {
|
||||
await renderKeypressHook(true);
|
||||
const key = { name: 'enter', sequence: '\x1B\r' };
|
||||
act(() => stdin.write(key.sequence));
|
||||
expect(onKeypress).toHaveBeenCalledWith(
|
||||
@@ -128,8 +131,8 @@ describe(`useKeypress`, () => {
|
||||
setup();
|
||||
});
|
||||
|
||||
it('should process a paste as a single event', () => {
|
||||
renderKeypressHook(true);
|
||||
it('should process a paste as a single event', async () => {
|
||||
await renderKeypressHook(true);
|
||||
const pasteText = 'hello world';
|
||||
act(() => stdin.write(PASTE_START + pasteText + PASTE_END));
|
||||
|
||||
@@ -145,8 +148,8 @@ describe(`useKeypress`, () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle keypress interspersed with pastes', () => {
|
||||
renderKeypressHook(true);
|
||||
it('should handle keypress interspersed with pastes', async () => {
|
||||
await renderKeypressHook(true);
|
||||
|
||||
const keyA = { name: 'a', sequence: 'a' };
|
||||
act(() => stdin.write('a'));
|
||||
@@ -169,8 +172,8 @@ describe(`useKeypress`, () => {
|
||||
expect(onKeypress).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
it('should handle lone pastes', () => {
|
||||
renderKeypressHook(true);
|
||||
it('should handle lone pastes', async () => {
|
||||
await renderKeypressHook(true);
|
||||
|
||||
const pasteText = 'pasted';
|
||||
act(() => {
|
||||
@@ -184,7 +187,7 @@ describe(`useKeypress`, () => {
|
||||
});
|
||||
|
||||
it('should handle paste false alarm', async () => {
|
||||
renderKeypressHook(true);
|
||||
await renderKeypressHook(true);
|
||||
|
||||
act(() => {
|
||||
stdin.write(PASTE_START.slice(0, 5));
|
||||
@@ -200,8 +203,8 @@ describe(`useKeypress`, () => {
|
||||
expect(onKeypress).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should handle back to back pastes', () => {
|
||||
renderKeypressHook(true);
|
||||
it('should handle back to back pastes', async () => {
|
||||
await renderKeypressHook(true);
|
||||
|
||||
const pasteText1 = 'herp';
|
||||
const pasteText2 = 'derp';
|
||||
@@ -226,7 +229,7 @@ describe(`useKeypress`, () => {
|
||||
});
|
||||
|
||||
it('should handle pastes split across writes', async () => {
|
||||
renderKeypressHook(true);
|
||||
await renderKeypressHook(true);
|
||||
|
||||
const keyA = { name: 'a', sequence: 'a' };
|
||||
act(() => stdin.write('a'));
|
||||
|
||||
@@ -30,10 +30,10 @@ describe('useReverseSearchCompletion', () => {
|
||||
|
||||
describe('Core Hook Behavior', () => {
|
||||
describe('State Management', () => {
|
||||
it('should initialize with default state', () => {
|
||||
it('should initialize with default state', async () => {
|
||||
const mockShellHistory = ['echo hello'];
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useReverseSearchCompletion(
|
||||
useTextBufferForTest(''),
|
||||
mockShellHistory,
|
||||
@@ -48,9 +48,9 @@ describe('useReverseSearchCompletion', () => {
|
||||
expect(result.current.isLoadingSuggestions).toBe(false);
|
||||
});
|
||||
|
||||
it('should reset state when reverseSearchActive becomes false', () => {
|
||||
it('should reset state when reverseSearchActive becomes false', async () => {
|
||||
const mockShellHistory = ['echo hello'];
|
||||
const { result, rerender } = renderHookWithProviders(
|
||||
const { result, rerender } = await renderHookWithProviders(
|
||||
({ text, active }) => {
|
||||
const textBuffer = useTextBufferForTest(text);
|
||||
return useReverseSearchCompletion(
|
||||
@@ -72,10 +72,10 @@ describe('useReverseSearchCompletion', () => {
|
||||
});
|
||||
|
||||
describe('Navigation', () => {
|
||||
it('should handle navigateUp with no suggestions', () => {
|
||||
it('should handle navigateUp with no suggestions', async () => {
|
||||
const mockShellHistory = ['echo hello'];
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useReverseSearchCompletion(
|
||||
useTextBufferForTest('grep'),
|
||||
mockShellHistory,
|
||||
@@ -90,9 +90,9 @@ describe('useReverseSearchCompletion', () => {
|
||||
expect(result.current.activeSuggestionIndex).toBe(-1);
|
||||
});
|
||||
|
||||
it('should handle navigateDown with no suggestions', () => {
|
||||
it('should handle navigateDown with no suggestions', async () => {
|
||||
const mockShellHistory = ['echo hello'];
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useReverseSearchCompletion(
|
||||
useTextBufferForTest('grep'),
|
||||
mockShellHistory,
|
||||
@@ -107,7 +107,7 @@ describe('useReverseSearchCompletion', () => {
|
||||
expect(result.current.activeSuggestionIndex).toBe(-1);
|
||||
});
|
||||
|
||||
it('should navigate up through suggestions with wrap-around', () => {
|
||||
it('should navigate up through suggestions with wrap-around', async () => {
|
||||
const mockShellHistory = [
|
||||
'ls -l',
|
||||
'ls -la',
|
||||
@@ -117,7 +117,7 @@ describe('useReverseSearchCompletion', () => {
|
||||
'echo Hi',
|
||||
];
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useReverseSearchCompletion(
|
||||
useTextBufferForTest('echo'),
|
||||
mockShellHistory,
|
||||
@@ -135,7 +135,7 @@ describe('useReverseSearchCompletion', () => {
|
||||
expect(result.current.activeSuggestionIndex).toBe(1);
|
||||
});
|
||||
|
||||
it('should navigate down through suggestions with wrap-around', () => {
|
||||
it('should navigate down through suggestions with wrap-around', async () => {
|
||||
const mockShellHistory = [
|
||||
'ls -l',
|
||||
'ls -la',
|
||||
@@ -144,7 +144,7 @@ describe('useReverseSearchCompletion', () => {
|
||||
'echo "Hello, World!"',
|
||||
'echo Hi',
|
||||
];
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useReverseSearchCompletion(
|
||||
useTextBufferForTest('ls'),
|
||||
mockShellHistory,
|
||||
@@ -162,7 +162,7 @@ describe('useReverseSearchCompletion', () => {
|
||||
expect(result.current.activeSuggestionIndex).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle navigation with multiple suggestions', () => {
|
||||
it('should handle navigation with multiple suggestions', async () => {
|
||||
const mockShellHistory = [
|
||||
'ls -l',
|
||||
'ls -la',
|
||||
@@ -172,7 +172,7 @@ describe('useReverseSearchCompletion', () => {
|
||||
'echo "Hi all"',
|
||||
];
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useReverseSearchCompletion(
|
||||
useTextBufferForTest('l'),
|
||||
mockShellHistory,
|
||||
@@ -209,13 +209,13 @@ describe('useReverseSearchCompletion', () => {
|
||||
expect(result.current.activeSuggestionIndex).toBe(4);
|
||||
});
|
||||
|
||||
it('should handle navigation with large suggestion lists and scrolling', () => {
|
||||
it('should handle navigation with large suggestion lists and scrolling', async () => {
|
||||
const largeMockCommands = Array.from(
|
||||
{ length: 15 },
|
||||
(_, i) => `echo ${i}`,
|
||||
);
|
||||
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useReverseSearchCompletion(
|
||||
useTextBufferForTest('echo'),
|
||||
largeMockCommands,
|
||||
@@ -239,9 +239,9 @@ describe('useReverseSearchCompletion', () => {
|
||||
});
|
||||
|
||||
describe('Filtering', () => {
|
||||
it('filters history by buffer.text and sets showSuggestions', () => {
|
||||
it('filters history by buffer.text and sets showSuggestions', async () => {
|
||||
const history = ['foo', 'barfoo', 'baz'];
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useReverseSearchCompletion(useTextBufferForTest('foo'), history, true),
|
||||
);
|
||||
|
||||
@@ -253,9 +253,9 @@ describe('useReverseSearchCompletion', () => {
|
||||
expect(result.current.showSuggestions).toBe(true);
|
||||
});
|
||||
|
||||
it('hides suggestions when there are no matches', () => {
|
||||
it('hides suggestions when there are no matches', async () => {
|
||||
const history = ['alpha', 'beta'];
|
||||
const { result } = renderHookWithProviders(() =>
|
||||
const { result } = await renderHookWithProviders(() =>
|
||||
useReverseSearchCompletion(useTextBufferForTest('γ'), history, true),
|
||||
);
|
||||
|
||||
|
||||
@@ -48,10 +48,13 @@ describe('useSnowfall', () => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('initially enables animation during holiday season with Holiday theme', () => {
|
||||
const { result } = renderHookWithProviders(() => useSnowfall(mockArt), {
|
||||
uiState: { history: [], historyRemountKey: 0 } as Partial<UIState>,
|
||||
});
|
||||
it('initially enables animation during holiday season with Holiday theme', async () => {
|
||||
const { result } = await renderHookWithProviders(
|
||||
() => useSnowfall(mockArt),
|
||||
{
|
||||
uiState: { history: [], historyRemountKey: 0 } as Partial<UIState>,
|
||||
},
|
||||
);
|
||||
|
||||
// Should contain holiday trees
|
||||
expect(result.current).toContain('|_|');
|
||||
@@ -59,10 +62,13 @@ describe('useSnowfall', () => {
|
||||
expect(debugState.debugNumAnimatedComponents).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('stops animation after 15 seconds', () => {
|
||||
const { result } = renderHookWithProviders(() => useSnowfall(mockArt), {
|
||||
uiState: { history: [], historyRemountKey: 0 } as Partial<UIState>,
|
||||
});
|
||||
it('stops animation after 15 seconds', async () => {
|
||||
const { result } = await renderHookWithProviders(
|
||||
() => useSnowfall(mockArt),
|
||||
{
|
||||
uiState: { history: [], historyRemountKey: 0 } as Partial<UIState>,
|
||||
},
|
||||
);
|
||||
|
||||
expect(debugState.debugNumAnimatedComponents).toBeGreaterThan(0);
|
||||
|
||||
@@ -76,35 +82,44 @@ describe('useSnowfall', () => {
|
||||
expect(result.current).toBe(mockArt);
|
||||
});
|
||||
|
||||
it('does not enable animation if not holiday season', () => {
|
||||
it('does not enable animation if not holiday season', async () => {
|
||||
vi.setSystemTime(new Date('2025-06-15'));
|
||||
const { result } = renderHookWithProviders(() => useSnowfall(mockArt), {
|
||||
uiState: { history: [], historyRemountKey: 0 } as Partial<UIState>,
|
||||
});
|
||||
const { result } = await renderHookWithProviders(
|
||||
() => useSnowfall(mockArt),
|
||||
{
|
||||
uiState: { history: [], historyRemountKey: 0 } as Partial<UIState>,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current).toBe(mockArt);
|
||||
expect(debugState.debugNumAnimatedComponents).toBe(0);
|
||||
});
|
||||
|
||||
it('does not enable animation if theme is not Holiday', () => {
|
||||
it('does not enable animation if theme is not Holiday', async () => {
|
||||
vi.mocked(themeManager.getActiveTheme).mockReturnValue({
|
||||
name: 'Default',
|
||||
} as Theme);
|
||||
const { result } = renderHookWithProviders(() => useSnowfall(mockArt), {
|
||||
uiState: { history: [], historyRemountKey: 0 } as Partial<UIState>,
|
||||
});
|
||||
const { result } = await renderHookWithProviders(
|
||||
() => useSnowfall(mockArt),
|
||||
{
|
||||
uiState: { history: [], historyRemountKey: 0 } as Partial<UIState>,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current).toBe(mockArt);
|
||||
expect(debugState.debugNumAnimatedComponents).toBe(0);
|
||||
});
|
||||
|
||||
it('does not enable animation if chat has started', () => {
|
||||
const { result } = renderHookWithProviders(() => useSnowfall(mockArt), {
|
||||
uiState: {
|
||||
history: [{ type: 'user', text: 'hello' }],
|
||||
historyRemountKey: 0,
|
||||
} as Partial<UIState>,
|
||||
});
|
||||
it('does not enable animation if chat has started', async () => {
|
||||
const { result } = await renderHookWithProviders(
|
||||
() => useSnowfall(mockArt),
|
||||
{
|
||||
uiState: {
|
||||
history: [{ type: 'user', text: 'hello' }],
|
||||
historyRemountKey: 0,
|
||||
} as Partial<UIState>,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current).toBe(mockArt);
|
||||
expect(debugState.debugNumAnimatedComponents).toBe(0);
|
||||
|
||||
@@ -16,8 +16,8 @@ describe('useTips()', () => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return false and call set(1) if state is undefined', () => {
|
||||
const { result } = renderHookWithProviders(() => useTips());
|
||||
it('should return false and call set(1) if state is undefined', async () => {
|
||||
const { result } = await renderHookWithProviders(() => useTips());
|
||||
|
||||
expect(result.current.showTips).toBe(true);
|
||||
|
||||
@@ -25,20 +25,20 @@ describe('useTips()', () => {
|
||||
expect(persistentStateMock.get('tipsShown')).toBe(1);
|
||||
});
|
||||
|
||||
it('should return false and call set(6) if state is 5', () => {
|
||||
it('should return false and call set(6) if state is 5', async () => {
|
||||
persistentStateMock.setData({ tipsShown: 5 });
|
||||
|
||||
const { result } = renderHookWithProviders(() => useTips());
|
||||
const { result } = await renderHookWithProviders(() => useTips());
|
||||
|
||||
expect(result.current.showTips).toBe(true);
|
||||
|
||||
expect(persistentStateMock.get('tipsShown')).toBe(6);
|
||||
});
|
||||
|
||||
it('should return true if state is 10', () => {
|
||||
it('should return true if state is 10', async () => {
|
||||
persistentStateMock.setData({ tipsShown: 10 });
|
||||
|
||||
const { result } = renderHookWithProviders(() => useTips());
|
||||
const { result } = await renderHookWithProviders(() => useTips());
|
||||
|
||||
expect(result.current.showTips).toBe(false);
|
||||
expect(persistentStateMock.set).not.toHaveBeenCalled();
|
||||
|
||||
Reference in New Issue
Block a user