diff --git a/packages/cli/src/ui/components/ModelDialog.test.tsx b/packages/cli/src/ui/components/ModelDialog.test.tsx index e936ad3bae..c9ee077bc8 100644 --- a/packages/cli/src/ui/components/ModelDialog.test.tsx +++ b/packages/cli/src/ui/components/ModelDialog.test.tsx @@ -4,11 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { render } from 'ink-testing-library'; import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { act } from 'react'; import { ModelDialog } from './ModelDialog.js'; -import { ConfigContext } from '../contexts/ConfigContext.js'; -import { KeypressProvider } from '../contexts/KeypressContext.js'; +import { renderWithProviders } from '../../test-utils/render.js'; +import { waitFor } from '../../test-utils/async.js'; import { DEFAULT_GEMINI_MODEL, DEFAULT_GEMINI_MODEL_AUTO, @@ -47,12 +47,14 @@ describe('', () => { setModel: (model: string, isTemporary?: boolean) => void; getModel: () => string; getHasAccessToPreviewModel: () => boolean; + getIdeMode: () => boolean; } const mockConfig: MockConfig = { setModel: mockSetModel, getModel: mockGetModel, getHasAccessToPreviewModel: mockGetHasAccessToPreviewModel, + getIdeMode: () => false, }; beforeEach(() => { @@ -68,17 +70,10 @@ describe('', () => { }); }); - const renderComponent = (contextValue = mockConfig as Config) => - render( - - - - - , - ); - - const waitForUpdate = () => - new Promise((resolve) => setTimeout(resolve, 150)); + const renderComponent = (configValue = mockConfig as Config) => + renderWithProviders(, { + config: configValue, + }); it('renders the initial "main" view correctly', () => { const { lastFrame } = renderComponent(); @@ -93,48 +88,60 @@ describe('', () => { // Select "Manual" (index 1) // Press down arrow to move to "Manual" - stdin.write('\u001B[B'); // Arrow Down - await waitForUpdate(); + await act(async () => { + stdin.write('\u001B[B'); // Arrow Down + }); // Press enter to select - stdin.write('\r'); - await waitForUpdate(); + await act(async () => { + stdin.write('\r'); + }); // Should now show manual options - expect(lastFrame()).toContain(DEFAULT_GEMINI_MODEL); - expect(lastFrame()).toContain(DEFAULT_GEMINI_FLASH_MODEL); - expect(lastFrame()).toContain(DEFAULT_GEMINI_FLASH_LITE_MODEL); + await waitFor(() => { + expect(lastFrame()).toContain(DEFAULT_GEMINI_MODEL); + expect(lastFrame()).toContain(DEFAULT_GEMINI_FLASH_MODEL); + expect(lastFrame()).toContain(DEFAULT_GEMINI_FLASH_LITE_MODEL); + }); }); it('sets model and closes when a model is selected in "main" view', async () => { const { stdin } = renderComponent(); // Select "Auto" (index 0) - stdin.write('\r'); - await waitForUpdate(); + await act(async () => { + stdin.write('\r'); + }); - expect(mockSetModel).toHaveBeenCalledWith( - DEFAULT_GEMINI_MODEL_AUTO, - true, // Session only by default - ); - expect(mockOnClose).toHaveBeenCalled(); + await waitFor(() => { + expect(mockSetModel).toHaveBeenCalledWith( + DEFAULT_GEMINI_MODEL_AUTO, + true, // Session only by default + ); + expect(mockOnClose).toHaveBeenCalled(); + }); }); it('sets model and closes when a model is selected in "manual" view', async () => { const { stdin } = renderComponent(); // Navigate to Manual (index 1) and select - stdin.write('\u001B[B'); - await waitForUpdate(); - stdin.write('\r'); - await waitForUpdate(); + await act(async () => { + stdin.write('\u001B[B'); + }); + await act(async () => { + stdin.write('\r'); + }); // Now in manual view. Default selection is first item (DEFAULT_GEMINI_MODEL) - stdin.write('\r'); - await waitForUpdate(); + await act(async () => { + stdin.write('\r'); + }); - expect(mockSetModel).toHaveBeenCalledWith(DEFAULT_GEMINI_MODEL, true); - expect(mockOnClose).toHaveBeenCalled(); + await waitFor(() => { + expect(mockSetModel).toHaveBeenCalledWith(DEFAULT_GEMINI_MODEL, true); + expect(mockOnClose).toHaveBeenCalled(); + }); }); it('toggles persist mode with Tab key', async () => { @@ -143,48 +150,64 @@ describe('', () => { expect(lastFrame()).toContain('Remember model for future sessions: false'); // Press Tab to toggle persist mode - stdin.write('\t'); - await waitForUpdate(); + await act(async () => { + stdin.write('\t'); + }); - expect(lastFrame()).toContain('Remember model for future sessions: true'); + await waitFor(() => { + expect(lastFrame()).toContain('Remember model for future sessions: true'); + }); // Select "Auto" (index 0) - stdin.write('\r'); - await waitForUpdate(); + await act(async () => { + stdin.write('\r'); + }); - expect(mockSetModel).toHaveBeenCalledWith( - DEFAULT_GEMINI_MODEL_AUTO, - false, // Persist enabled - ); - expect(mockOnClose).toHaveBeenCalled(); + await waitFor(() => { + expect(mockSetModel).toHaveBeenCalledWith( + DEFAULT_GEMINI_MODEL_AUTO, + false, // Persist enabled + ); + expect(mockOnClose).toHaveBeenCalled(); + }); }); it('closes dialog on escape in "main" view', async () => { const { stdin } = renderComponent(); - stdin.write('\u001B'); // Escape - await waitForUpdate(); + await act(async () => { + stdin.write('\u001B'); // Escape + }); - expect(mockOnClose).toHaveBeenCalled(); + await waitFor(() => { + expect(mockOnClose).toHaveBeenCalled(); + }); }); it('goes back to "main" view on escape in "manual" view', async () => { const { lastFrame, stdin } = renderComponent(); // Go to manual view - stdin.write('\u001B[B'); - await waitForUpdate(); - stdin.write('\r'); - await waitForUpdate(); + await act(async () => { + stdin.write('\u001B[B'); + }); + await act(async () => { + stdin.write('\r'); + }); - expect(lastFrame()).toContain(DEFAULT_GEMINI_MODEL); + await waitFor(() => { + expect(lastFrame()).toContain(DEFAULT_GEMINI_MODEL); + }); // Press Escape - stdin.write('\u001B'); - await waitForUpdate(); + await act(async () => { + stdin.write('\u001B'); + }); - expect(mockOnClose).not.toHaveBeenCalled(); - // Should be back to main view (Manual option visible) - expect(lastFrame()).toContain('Manual'); + await waitFor(() => { + expect(mockOnClose).not.toHaveBeenCalled(); + // Should be back to main view (Manual option visible) + expect(lastFrame()).toContain('Manual'); + }); }); }); diff --git a/packages/cli/src/ui/components/UserIdentity.tsx b/packages/cli/src/ui/components/UserIdentity.tsx index ba7473723f..e506bfb052 100644 --- a/packages/cli/src/ui/components/UserIdentity.tsx +++ b/packages/cli/src/ui/components/UserIdentity.tsx @@ -37,7 +37,7 @@ export const UserIdentity: React.FC = ({ config }) => { } return ( - + {authType === AuthType.LOGIN_WITH_GOOGLE ? (