From 23264ced9a3d6ccd26a216dfb4d925b00a7e7ac1 Mon Sep 17 00:00:00 2001 From: Tommaso Sciortino Date: Thu, 19 Mar 2026 17:05:33 +0000 Subject: [PATCH] refactor(cli): integrate real config loading into async test utils (#23040) --- .../integration-tests/modelSteering.test.tsx | 2 +- packages/cli/src/test-utils/AppRig.test.tsx | 10 +- packages/cli/src/test-utils/AppRig.tsx | 10 +- packages/cli/src/test-utils/render.tsx | 101 ++-- packages/cli/src/ui/App.test.tsx | 32 +- .../cli/src/ui/IdeIntegrationNudge.test.tsx | 17 +- packages/cli/src/ui/auth/AuthDialog.test.tsx | 34 +- .../src/ui/auth/BannedAccountDialog.test.tsx | 20 +- .../cli/src/ui/components/AboutBox.test.tsx | 8 +- .../AdminSettingsChangedDialog.test.tsx | 6 +- .../ui/components/AgentConfigDialog.test.tsx | 4 +- .../AlternateBufferQuittingDisplay.test.tsx | 12 +- .../cli/src/ui/components/AppHeader.test.tsx | 44 +- .../src/ui/components/AppHeaderIcon.test.tsx | 4 +- .../src/ui/components/AskUserDialog.test.tsx | 70 +-- .../cli/src/ui/components/Banner.test.tsx | 4 +- .../ui/components/BubblingRegression.test.tsx | 2 +- .../cli/src/ui/components/CliSpinner.test.tsx | 6 +- .../src/ui/components/ColorsDisplay.test.tsx | 2 +- .../ui/components/ConfigInitDisplay.test.tsx | 8 +- .../components/ContextUsageDisplay.test.tsx | 10 +- .../DetailedMessagesDisplay.test.tsx | 10 +- .../src/ui/components/DialogManager.test.tsx | 4 +- .../components/EditorSettingsDialog.test.tsx | 12 +- .../ui/components/EmptyWalletDialog.test.tsx | 22 +- .../ui/components/ExitPlanModeDialog.test.tsx | 56 +- .../ui/components/FolderTrustDialog.test.tsx | 43 +- .../cli/src/ui/components/Footer.test.tsx | 138 +++-- .../ui/components/FooterConfigDialog.test.tsx | 16 +- .../ui/components/GradientRegression.test.tsx | 10 +- .../ui/components/HistoryItemDisplay.test.tsx | 134 ++--- .../src/ui/components/HooksDialog.test.tsx | 44 +- .../components/IdeTrustChangeDialog.test.tsx | 12 +- .../src/ui/components/InputPrompt.test.tsx | 532 +++++++++++------- .../ui/components/LoadingIndicator.test.tsx | 38 +- .../LogoutConfirmationDialog.test.tsx | 10 +- .../LoopDetectionConfirmation.test.tsx | 4 +- .../src/ui/components/MainContent.test.tsx | 28 +- .../src/ui/components/ModelDialog.test.tsx | 11 +- .../components/NewAgentsNotification.test.tsx | 6 +- .../src/ui/components/Notifications.test.tsx | 72 +-- .../ui/components/OverageMenuDialog.test.tsx | 22 +- .../PermissionsModifyTrustDialog.test.tsx | 29 +- .../ui/components/PolicyUpdateDialog.test.tsx | 8 +- .../ui/components/RewindConfirmation.test.tsx | 8 +- .../src/ui/components/RewindViewer.test.tsx | 85 +-- .../components/SessionSummaryDisplay.test.tsx | 2 +- .../src/ui/components/SettingsDialog.test.tsx | 113 ++-- .../src/ui/components/ShortcutsHelp.test.tsx | 4 +- .../src/ui/components/StatsDisplay.test.tsx | 49 +- .../src/ui/components/StickyHeader.test.tsx | 2 +- .../src/ui/components/ThemeDialog.test.tsx | 25 +- .../src/ui/components/ToastDisplay.test.tsx | 22 +- .../components/ToolConfirmationQueue.test.tsx | 16 +- .../src/ui/components/UserIdentity.test.tsx | 18 +- .../ConfigInitDisplay.test.tsx.snap | 12 - .../messages/CompressionMessage.test.tsx | 32 +- .../components/messages/DiffRenderer.test.tsx | 34 +- .../messages/GeminiMessage.test.tsx | 6 +- .../messages/RedirectionConfirmation.test.tsx | 2 +- .../messages/ShellToolMessage.test.tsx | 53 +- .../messages/SubagentGroupDisplay.test.tsx | 11 +- .../messages/ThinkingMessage.test.tsx | 16 +- .../messages/ToolConfirmationMessage.test.tsx | 76 +-- .../messages/ToolGroupMessage.test.tsx | 78 +-- .../components/messages/ToolMessage.test.tsx | 54 +- .../messages/ToolMessageFocusHint.test.tsx | 6 +- .../messages/ToolMessageRawMarkdown.test.tsx | 2 +- .../ToolOverflowConsistencyChecks.test.tsx | 4 +- .../messages/ToolResultDisplay.test.tsx | 26 +- .../ToolResultDisplayOverflow.test.tsx | 6 +- .../ToolStickyHeaderRegression.test.tsx | 4 +- .../components/messages/UserMessage.test.tsx | 8 +- .../shared/BaseSelectionList.test.tsx | 6 +- .../shared/BaseSettingsDialog.test.tsx | 2 +- .../DescriptiveRadioButtonSelect.test.tsx | 2 +- .../components/shared/EnumSelector.test.tsx | 18 +- .../shared/HalfLinePaddedBox.test.tsx | 8 +- .../ui/components/shared/MaxSizedBox.test.tsx | 2 +- .../shared/RadioButtonSelect.test.tsx | 14 +- .../ui/components/shared/Scrollable.test.tsx | 12 +- .../components/shared/ScrollableList.test.tsx | 28 +- .../components/shared/SearchableList.test.tsx | 18 +- .../components/shared/SectionHeader.test.tsx | 2 +- .../ui/components/shared/TabHeader.test.tsx | 26 +- .../ui/components/shared/text-buffer.test.ts | 16 +- .../views/ExtensionDetails.test.tsx | 14 +- .../views/ExtensionRegistryView.test.tsx | 14 +- .../ui/components/views/ToolsList.test.tsx | 6 +- .../src/ui/contexts/KeypressContext.test.tsx | 185 ++++-- .../cli/src/ui/contexts/MouseContext.test.tsx | 44 +- .../cli/src/ui/hooks/slashCommandProcessor.ts | 2 - .../ui/hooks/useCommandCompletion.test.tsx | 85 +-- packages/cli/src/ui/hooks/useFocus.test.tsx | 32 +- .../cli/src/ui/hooks/useGeminiStream.test.tsx | 140 ++--- .../cli/src/ui/hooks/useKeypress.test.tsx | 51 +- .../hooks/useReverseSearchCompletion.test.tsx | 40 +- .../cli/src/ui/hooks/useSnowfall.test.tsx | 61 +- packages/cli/src/ui/hooks/useTips.test.ts | 12 +- .../cli/src/ui/utils/CodeColorizer.test.tsx | 4 +- .../cli/src/ui/utils/MarkdownDisplay.test.tsx | 32 +- .../cli/src/ui/utils/TableRenderer.test.tsx | 24 +- .../cli/src/ui/utils/borderStyles.test.tsx | 6 +- 103 files changed, 1806 insertions(+), 1541 deletions(-) diff --git a/packages/cli/src/integration-tests/modelSteering.test.tsx b/packages/cli/src/integration-tests/modelSteering.test.tsx index 27bcde0dc2..bada268329 100644 --- a/packages/cli/src/integration-tests/modelSteering.test.tsx +++ b/packages/cli/src/integration-tests/modelSteering.test.tsx @@ -29,7 +29,7 @@ describe('Model Steering Integration', () => { configOverrides: { modelSteering: true }, }); await rig.initialize(); - rig.render(); + await rig.render(); await rig.waitForIdle(); rig.setToolPolicy('list_directory', PolicyDecision.ASK_USER); diff --git a/packages/cli/src/test-utils/AppRig.test.tsx b/packages/cli/src/test-utils/AppRig.test.tsx index 76c0ddc522..6d94342937 100644 --- a/packages/cli/src/test-utils/AppRig.test.tsx +++ b/packages/cli/src/test-utils/AppRig.test.tsx @@ -5,7 +5,6 @@ */ import { describe, it, afterEach, expect } from 'vitest'; -import { act } from 'react'; import { AppRig } from './AppRig.js'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -31,7 +30,7 @@ describe('AppRig', () => { configOverrides: { modelSteering: true }, }); await rig.initialize(); - rig.render(); + await rig.render(); await rig.waitForIdle(); // Set breakpoints on the canonical tool names @@ -69,12 +68,7 @@ describe('AppRig', () => { ); rig = new AppRig({ fakeResponsesPath }); await rig.initialize(); - await act(async () => { - rig!.render(); - // Allow async initializations (like banners) to settle within the act boundary - await new Promise((resolve) => setTimeout(resolve, 0)); - }); - + await rig.render(); // Wait for initial render await rig.waitForIdle(); diff --git a/packages/cli/src/test-utils/AppRig.tsx b/packages/cli/src/test-utils/AppRig.tsx index 39a896a3f8..5ead5d615a 100644 --- a/packages/cli/src/test-utils/AppRig.tsx +++ b/packages/cli/src/test-utils/AppRig.tsx @@ -11,7 +11,7 @@ import os from 'node:os'; import path from 'node:path'; import fs from 'node:fs'; import { AppContainer } from '../ui/AppContainer.js'; -import { renderWithProviders } from './render.js'; +import { renderWithProviders, type RenderInstance } from './render.js'; import { makeFakeConfig, type Config, @@ -155,7 +155,7 @@ export interface PendingConfirmation { } export class AppRig { - private renderResult: ReturnType | undefined; + private renderResult: RenderInstance | undefined; private config: Config | undefined; private settings: LoadedSettings | undefined; private testDir: string; @@ -393,12 +393,12 @@ export class AppRig { return isAnyToolActive || isAwaitingConfirmation; } - render() { + async render() { if (!this.config || !this.settings) throw new Error('AppRig not initialized'); - act(() => { - this.renderResult = renderWithProviders( + await act(async () => { + this.renderResult = await renderWithProviders( ({ - persistentState: persistentStateMock, + get persistentState() { + return persistentStateMock; + }, })); vi.mock('../ui/utils/terminalUtils.js', () => ({ @@ -486,50 +487,6 @@ export const simulateClick = async ( }); }; -let mockConfigInternal: Config | undefined; - -const getMockConfigInternal = (): Config => { - if (!mockConfigInternal) { - mockConfigInternal = makeFakeConfig({ - targetDir: os.tmpdir(), - enableEventDrivenScheduler: true, - }); - } - return mockConfigInternal; -}; - -const configProxy = new Proxy({} as Config, { - get(_target, prop) { - if (prop === 'getTargetDir') { - return () => - path.join( - path.parse(process.cwd()).root, - 'Users', - 'test', - 'project', - 'foo', - 'bar', - 'and', - 'some', - 'more', - 'directories', - 'to', - 'make', - 'it', - 'long', - ); - } - if (prop === 'getUseBackgroundColor') { - return () => true; - } - const internal = getMockConfigInternal(); - if (prop in internal) { - return internal[prop as keyof typeof internal]; - } - throw new Error(`mockConfig does not have property ${String(prop)}`); - }, -}); - export const mockSettings = createMockSettings(); // A minimal mock UIState to satisfy the context provider. @@ -639,7 +596,7 @@ const ContextCapture: React.FC<{ children: React.ReactNode }> = ({ return <>{children}; }; -export const renderWithProviders = ( +export const renderWithProviders = async ( component: React.ReactElement, { shellFocus = true, @@ -647,8 +604,7 @@ export const renderWithProviders = ( uiState: providedUiState, width, mouseEventsEnabled = false, - - config = configProxy as unknown as Config, + config, uiActions, persistentState, appState = mockAppState, @@ -666,13 +622,15 @@ export const renderWithProviders = ( }; appState?: AppState; } = {}, -): RenderInstance & { - simulateClick: ( - col: number, - row: number, - button?: 0 | 1 | 2, - ) => Promise; -} => { +): Promise< + RenderInstance & { + simulateClick: ( + col: number, + row: number, + button?: 0 | 1 | 2, + ) => Promise; + } +> => { const baseState: UIState = new Proxy( { ...baseMockUiState, ...providedUiState }, { @@ -701,8 +659,15 @@ export const renderWithProviders = ( persistentStateMock.mockClear(); const terminalWidth = width ?? baseState.terminalWidth; - const finalSettings = settings; - const finalConfig = config; + + if (!config) { + config = await loadCliConfig( + settings.merged, + 'random-session-id', + {} as unknown as CliArgs, + { cwd: '/' }, + ); + } const mainAreaWidth = terminalWidth; @@ -732,8 +697,8 @@ export const renderWithProviders = ( const wrapWithProviders = (comp: React.ReactElement) => ( - - + + @@ -744,7 +709,7 @@ export const renderWithProviders = ( ( return { result, rerender, unmount, waitUntilReady, generateSvg }; } -export function renderHookWithProviders( +export async function renderHookWithProviders( renderCallback: (props: Props) => Result, options: { initialProps?: Props; @@ -876,13 +841,13 @@ export function renderHookWithProviders( mouseEventsEnabled?: boolean; config?: Config; } = {}, -): { +): Promise<{ result: { current: Result }; rerender: (props?: Props) => void; unmount: () => void; waitUntilReady: () => Promise; generateSvg: () => string; -} { +}> { const result = { current: undefined as unknown as Result }; let setPropsFn: ((props: Props) => void) | undefined; @@ -901,8 +866,8 @@ export function renderHookWithProviders( let renderResult: ReturnType; - act(() => { - renderResult = renderWithProviders( + await act(async () => { + renderResult = await renderWithProviders( {} diff --git a/packages/cli/src/ui/App.test.tsx b/packages/cli/src/ui/App.test.tsx index 4e59ab854e..7f5e55c022 100644 --- a/packages/cli/src/ui/App.test.tsx +++ b/packages/cli/src/ui/App.test.tsx @@ -94,11 +94,10 @@ describe('App', () => { }; it('should render main content and composer when not quitting', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: mockUIState, - config: makeFakeConfig({ useAlternateBuffer: false }), settings: createMockSettings({ ui: { useAlternateBuffer: false } }), }, ); @@ -116,11 +115,10 @@ describe('App', () => { quittingMessages: [{ id: 1, type: 'user', text: 'test' }], } as UIState; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: quittingUIState, - config: makeFakeConfig({ useAlternateBuffer: false }), settings: createMockSettings({ ui: { useAlternateBuffer: false } }), }, ); @@ -138,11 +136,10 @@ describe('App', () => { pendingHistoryItems: [{ type: 'user', text: 'pending item' }], } as UIState; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: quittingUIState, - config: makeFakeConfig({ useAlternateBuffer: true }), settings: createMockSettings({ ui: { useAlternateBuffer: true } }), }, ); @@ -159,11 +156,10 @@ describe('App', () => { dialogsVisible: true, } as UIState; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: dialogUIState, - config: makeFakeConfig({ useAlternateBuffer: true }), settings: createMockSettings({ ui: { useAlternateBuffer: true } }), }, ); @@ -187,11 +183,10 @@ describe('App', () => { [stateKey]: true, } as UIState; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState, - config: makeFakeConfig({ useAlternateBuffer: true }), settings: createMockSettings({ ui: { useAlternateBuffer: true } }), }, ); @@ -205,11 +200,10 @@ describe('App', () => { it('should render ScreenReaderAppLayout when screen reader is enabled', async () => { (useIsScreenReaderEnabled as Mock).mockReturnValue(true); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: mockUIState, - config: makeFakeConfig({ useAlternateBuffer: true }), settings: createMockSettings({ ui: { useAlternateBuffer: true } }), }, ); @@ -225,11 +219,10 @@ describe('App', () => { it('should render DefaultAppLayout when screen reader is not enabled', async () => { (useIsScreenReaderEnabled as Mock).mockReturnValue(false); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: mockUIState, - config: makeFakeConfig({ useAlternateBuffer: true }), settings: createMockSettings({ ui: { useAlternateBuffer: true } }), }, ); @@ -281,7 +274,7 @@ describe('App', () => { vi.spyOn(configWithExperiment, 'isTrustedFolder').mockReturnValue(true); vi.spyOn(configWithExperiment, 'getIdeMode').mockReturnValue(false); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: stateWithConfirmingTool, @@ -302,11 +295,10 @@ describe('App', () => { describe('Snapshots', () => { it('renders default layout correctly', async () => { (useIsScreenReaderEnabled as Mock).mockReturnValue(false); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: mockUIState, - config: makeFakeConfig({ useAlternateBuffer: true }), settings: createMockSettings({ ui: { useAlternateBuffer: true } }), }, ); @@ -317,11 +309,10 @@ describe('App', () => { it('renders screen reader layout correctly', async () => { (useIsScreenReaderEnabled as Mock).mockReturnValue(true); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: mockUIState, - config: makeFakeConfig({ useAlternateBuffer: true }), settings: createMockSettings({ ui: { useAlternateBuffer: true } }), }, ); @@ -335,11 +326,10 @@ describe('App', () => { ...mockUIState, dialogsVisible: true, } as UIState; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: dialogUIState, - config: makeFakeConfig({ useAlternateBuffer: true }), settings: createMockSettings({ ui: { useAlternateBuffer: true } }), }, ); diff --git a/packages/cli/src/ui/IdeIntegrationNudge.test.tsx b/packages/cli/src/ui/IdeIntegrationNudge.test.tsx index 1b30e0e0b2..5df3534f12 100644 --- a/packages/cli/src/ui/IdeIntegrationNudge.test.tsx +++ b/packages/cli/src/ui/IdeIntegrationNudge.test.tsx @@ -53,7 +53,7 @@ describe('IdeIntegrationNudge', () => { }); it('renders correctly with default options', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -68,7 +68,7 @@ describe('IdeIntegrationNudge', () => { it('handles "Yes" selection', async () => { const onComplete = vi.fn(); - const { stdin, waitUntilReady, unmount } = renderWithProviders( + const { stdin, waitUntilReady, unmount } = await renderWithProviders( , ); @@ -89,7 +89,7 @@ describe('IdeIntegrationNudge', () => { it('handles "No" selection', async () => { const onComplete = vi.fn(); - const { stdin, waitUntilReady, unmount } = renderWithProviders( + const { stdin, waitUntilReady, unmount } = await renderWithProviders( , ); @@ -115,7 +115,7 @@ describe('IdeIntegrationNudge', () => { it('handles "Dismiss" selection', async () => { const onComplete = vi.fn(); - const { stdin, waitUntilReady, unmount } = renderWithProviders( + const { stdin, waitUntilReady, unmount } = await renderWithProviders( , ); @@ -146,7 +146,7 @@ describe('IdeIntegrationNudge', () => { it('handles Escape key press', async () => { const onComplete = vi.fn(); - const { stdin, waitUntilReady, unmount } = renderWithProviders( + const { stdin, waitUntilReady, unmount } = await renderWithProviders( , ); @@ -173,9 +173,10 @@ describe('IdeIntegrationNudge', () => { vi.stubEnv('GEMINI_CLI_IDE_WORKSPACE_PATH', '/tmp'); const onComplete = vi.fn(); - const { lastFrame, stdin, waitUntilReady, unmount } = renderWithProviders( - , - ); + const { lastFrame, stdin, waitUntilReady, unmount } = + await renderWithProviders( + , + ); await waitUntilReady(); diff --git a/packages/cli/src/ui/auth/AuthDialog.test.tsx b/packages/cli/src/ui/auth/AuthDialog.test.tsx index 7ab5fc0be2..878b2a8ee0 100644 --- a/packages/cli/src/ui/auth/AuthDialog.test.tsx +++ b/packages/cli/src/ui/auth/AuthDialog.test.tsx @@ -143,7 +143,7 @@ describe('AuthDialog', () => { for (const [key, value] of Object.entries(env)) { vi.stubEnv(key, value as string); } - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -161,7 +161,7 @@ describe('AuthDialog', () => { it('filters auth types when enforcedType is set', async () => { props.settings.merged.security.auth.enforcedType = AuthType.USE_GEMINI; - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -173,7 +173,7 @@ describe('AuthDialog', () => { it('sets initial index to 0 when enforcedType is set', async () => { props.settings.merged.security.auth.enforcedType = AuthType.USE_GEMINI; - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -213,7 +213,7 @@ describe('AuthDialog', () => { }, ])('selects initial auth type $desc', async ({ setup, expected }) => { setup(); - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -226,7 +226,7 @@ describe('AuthDialog', () => { describe('handleAuthSelect', () => { it('calls onAuthError if validation fails', async () => { mockedValidateAuthMethod.mockReturnValue('Invalid method'); - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -245,7 +245,7 @@ describe('AuthDialog', () => { it('sets auth context with requiresRestart: true for LOGIN_WITH_GOOGLE', async () => { mockedValidateAuthMethod.mockReturnValue(null); - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -261,7 +261,7 @@ describe('AuthDialog', () => { it('sets auth context with empty object for other auth types', async () => { mockedValidateAuthMethod.mockReturnValue(null); - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -278,7 +278,7 @@ describe('AuthDialog', () => { vi.stubEnv('GEMINI_API_KEY', 'test-key-from-env'); // props.settings.merged.security.auth.selectedType is undefined here, simulating initial setup - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -297,7 +297,7 @@ describe('AuthDialog', () => { vi.stubEnv('GEMINI_API_KEY', ''); // Empty string // props.settings.merged.security.auth.selectedType is undefined here - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -316,7 +316,7 @@ describe('AuthDialog', () => { // process.env['GEMINI_API_KEY'] is not set // props.settings.merged.security.auth.selectedType is undefined here, simulating initial setup - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -337,7 +337,7 @@ describe('AuthDialog', () => { props.settings.merged.security.auth.selectedType = AuthType.LOGIN_WITH_GOOGLE; - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -360,7 +360,7 @@ describe('AuthDialog', () => { vi.mocked(props.config.isBrowserLaunchSuppressed).mockReturnValue(true); mockedValidateAuthMethod.mockReturnValue(null); - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -383,7 +383,7 @@ describe('AuthDialog', () => { it('displays authError when provided', async () => { props.authError = 'Something went wrong'; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -429,7 +429,7 @@ describe('AuthDialog', () => { }, ])('$desc', async ({ setup, expectations }) => { setup(); - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -442,7 +442,7 @@ describe('AuthDialog', () => { describe('Snapshots', () => { it('renders correctly with default props', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -452,7 +452,7 @@ describe('AuthDialog', () => { it('renders correctly with auth error', async () => { props.authError = 'Something went wrong'; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -462,7 +462,7 @@ describe('AuthDialog', () => { it('renders correctly with enforced auth type', async () => { props.settings.merged.security.auth.enforcedType = AuthType.USE_GEMINI; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); diff --git a/packages/cli/src/ui/auth/BannedAccountDialog.test.tsx b/packages/cli/src/ui/auth/BannedAccountDialog.test.tsx index 692b249415..0670c81bc9 100644 --- a/packages/cli/src/ui/auth/BannedAccountDialog.test.tsx +++ b/packages/cli/src/ui/auth/BannedAccountDialog.test.tsx @@ -73,7 +73,7 @@ describe('BannedAccountDialog', () => { }); it('renders the suspension message from accountSuspensionInfo', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( { }); it('renders menu options with appeal link text from response', async () => { - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( { const infoWithoutUrl: AccountSuspensionInfo = { message: 'Account suspended.', }; - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( { message: 'Account suspended.', appealUrl: 'https://example.com/appeal', }; - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( { }); it('opens browser when appeal option is selected', async () => { - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( { it('shows URL when browser cannot be launched', async () => { mockedShouldLaunchBrowser.mockReturnValue(false); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( { }); it('calls onExit when "Exit" is selected', async () => { - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( { }); it('calls onChangeAuth when "Change authentication" is selected', async () => { - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( { }); it('exits on escape key', async () => { - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( { }); it('renders snapshot correctly', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( { }; it('renders with required props', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -46,7 +46,7 @@ describe('AboutBox', () => { ['tier', 'Enterprise', 'Tier'], ])('renders optional prop %s', async (prop, value, label) => { const props = { ...defaultProps, [prop]: value }; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -58,7 +58,7 @@ describe('AboutBox', () => { it('renders Auth Method with email when userEmail is provided', async () => { const props = { ...defaultProps, userEmail: 'test@example.com' }; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -69,7 +69,7 @@ describe('AboutBox', () => { it('renders Auth Method correctly when not oauth', async () => { const props = { ...defaultProps, selectedAuthType: 'api-key' }; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); diff --git a/packages/cli/src/ui/components/AdminSettingsChangedDialog.test.tsx b/packages/cli/src/ui/components/AdminSettingsChangedDialog.test.tsx index 0cfe00c764..19db058b87 100644 --- a/packages/cli/src/ui/components/AdminSettingsChangedDialog.test.tsx +++ b/packages/cli/src/ui/components/AdminSettingsChangedDialog.test.tsx @@ -17,7 +17,7 @@ describe('AdminSettingsChangedDialog', () => { }); it('renders correctly', async () => { - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( , ); await waitUntilReady(); @@ -25,7 +25,7 @@ describe('AdminSettingsChangedDialog', () => { }); it('restarts on "r" key press', async () => { - const { stdin, waitUntilReady } = renderWithProviders( + const { stdin, waitUntilReady } = await renderWithProviders( , { uiActions: { @@ -43,7 +43,7 @@ describe('AdminSettingsChangedDialog', () => { }); it.each(['r', 'R'])('restarts on "%s" key press', async (key) => { - const { stdin, waitUntilReady } = renderWithProviders( + const { stdin, waitUntilReady } = await renderWithProviders( , { uiActions: { diff --git a/packages/cli/src/ui/components/AgentConfigDialog.test.tsx b/packages/cli/src/ui/components/AgentConfigDialog.test.tsx index 2e5b6ecdb2..a2bfe052bb 100644 --- a/packages/cli/src/ui/components/AgentConfigDialog.test.tsx +++ b/packages/cli/src/ui/components/AgentConfigDialog.test.tsx @@ -115,7 +115,7 @@ describe('AgentConfigDialog', () => { settings: LoadedSettings, definition: AgentDefinition = createMockAgentDefinition(), ) => { - const result = renderWithProviders( + const result = await renderWithProviders( { const settings = createMockSettings(); // Agent config has about 6 base items + 2 per tool // Render with very small height (20) - const { lastFrame, unmount } = renderWithProviders( + const { lastFrame, unmount } = await renderWithProviders( { it('renders with active and pending tool messages', async () => { persistentStateMock.setData({ tipsShown: 0 }); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: { @@ -125,7 +125,7 @@ describe('AlternateBufferQuittingDisplay', () => { it('renders with empty history and no pending items', async () => { persistentStateMock.setData({ tipsShown: 0 }); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: { @@ -142,7 +142,7 @@ describe('AlternateBufferQuittingDisplay', () => { it('renders with history but no pending items', async () => { persistentStateMock.setData({ tipsShown: 0 }); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: { @@ -159,7 +159,7 @@ describe('AlternateBufferQuittingDisplay', () => { it('renders with pending items but no history', async () => { persistentStateMock.setData({ tipsShown: 0 }); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: { @@ -195,7 +195,7 @@ describe('AlternateBufferQuittingDisplay', () => { ], }, ]; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: { @@ -220,7 +220,7 @@ describe('AlternateBufferQuittingDisplay', () => { { id: 1, type: 'user', text: 'Hello Gemini' }, { id: 2, type: 'gemini', text: 'Hello User!' }, ]; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: { diff --git a/packages/cli/src/ui/components/AppHeader.test.tsx b/packages/cli/src/ui/components/AppHeader.test.tsx index ebcd4de973..0d7e2b3a7b 100644 --- a/packages/cli/src/ui/components/AppHeader.test.tsx +++ b/packages/cli/src/ui/components/AppHeader.test.tsx @@ -10,7 +10,6 @@ import { } from '../../test-utils/render.js'; import { AppHeader } from './AppHeader.js'; import { describe, it, expect, vi } from 'vitest'; -import { makeFakeConfig } from '@google/gemini-cli-core'; import crypto from 'node:crypto'; vi.mock('../utils/terminalSetup.js', () => ({ @@ -19,7 +18,6 @@ vi.mock('../utils/terminalSetup.js', () => ({ describe('', () => { it('should render the banner with default text', async () => { - const mockConfig = makeFakeConfig(); const uiState = { history: [], bannerData: { @@ -29,10 +27,9 @@ describe('', () => { bannerVisible: true, }; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { - config: mockConfig, uiState, }, ); @@ -44,7 +41,6 @@ describe('', () => { }); it('should render the banner with warning text', async () => { - const mockConfig = makeFakeConfig(); const uiState = { history: [], bannerData: { @@ -54,10 +50,9 @@ describe('', () => { bannerVisible: true, }; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { - config: mockConfig, uiState, }, ); @@ -69,7 +64,6 @@ describe('', () => { }); it('should not render the banner when no flags are set', async () => { - const mockConfig = makeFakeConfig(); const uiState = { history: [], bannerData: { @@ -78,10 +72,9 @@ describe('', () => { }, }; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { - config: mockConfig, uiState, }, ); @@ -93,7 +86,6 @@ describe('', () => { }); it('should not render the default banner if shown count is 5 or more', async () => { - const mockConfig = makeFakeConfig(); const uiState = { history: [], bannerData: { @@ -111,10 +103,9 @@ describe('', () => { }, }); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { - config: mockConfig, uiState, }, ); @@ -126,7 +117,6 @@ describe('', () => { }); it('should increment the version count when default banner is displayed', async () => { - const mockConfig = makeFakeConfig(); const uiState = { history: [], bannerData: { @@ -139,10 +129,9 @@ describe('', () => { // and interfering with the expected persistentState.set call. persistentStateMock.setData({ tipsShown: 10 }); - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , { - config: mockConfig, uiState, }, ); @@ -161,7 +150,6 @@ describe('', () => { }); it('should render banner text with unescaped newlines', async () => { - const mockConfig = makeFakeConfig(); const uiState = { history: [], bannerData: { @@ -171,10 +159,9 @@ describe('', () => { bannerVisible: true, }; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { - config: mockConfig, uiState, }, ); @@ -185,7 +172,6 @@ describe('', () => { }); it('should render Tips when tipsShown is less than 10', async () => { - const mockConfig = makeFakeConfig(); const uiState = { history: [], bannerData: { @@ -197,10 +183,9 @@ describe('', () => { persistentStateMock.setData({ tipsShown: 5 }); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { - config: mockConfig, uiState, }, ); @@ -212,7 +197,6 @@ describe('', () => { }); it('should NOT render Tips when tipsShown is 10 or more', async () => { - const mockConfig = makeFakeConfig(); const uiState = { bannerData: { defaultText: '', @@ -222,10 +206,9 @@ describe('', () => { persistentStateMock.setData({ tipsShown: 10 }); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { - config: mockConfig, uiState, }, ); @@ -238,7 +221,6 @@ describe('', () => { it('should show tips until they have been shown 10 times (persistence flow)', async () => { persistentStateMock.setData({ tipsShown: 9 }); - const mockConfig = makeFakeConfig(); const uiState = { history: [], bannerData: { @@ -249,8 +231,7 @@ describe('', () => { }; // First session - const session1 = renderWithProviders(, { - config: mockConfig, + const session1 = await renderWithProviders(, { uiState, }); await session1.waitUntilReady(); @@ -260,9 +241,10 @@ describe('', () => { session1.unmount(); // Second session - state is persisted in the fake - const session2 = renderWithProviders(, { - config: mockConfig, - }); + const session2 = await renderWithProviders( + , + {}, + ); await session2.waitUntilReady(); expect(session2.lastFrame()).not.toContain('Tips'); diff --git a/packages/cli/src/ui/components/AppHeaderIcon.test.tsx b/packages/cli/src/ui/components/AppHeaderIcon.test.tsx index c16febea66..6b6f0e6210 100644 --- a/packages/cli/src/ui/components/AppHeaderIcon.test.tsx +++ b/packages/cli/src/ui/components/AppHeaderIcon.test.tsx @@ -32,7 +32,7 @@ describe('AppHeader Icon Rendering', () => { it('renders the default icon in standard terminals', async () => { vi.mocked(isAppleTerminal).mockReturnValue(false); - const result = renderWithProviders(); + const result = await renderWithProviders(); await result.waitUntilReady(); await expect(result).toMatchSvgSnapshot(); @@ -41,7 +41,7 @@ describe('AppHeader Icon Rendering', () => { it('renders the symmetric icon in Apple Terminal', async () => { vi.mocked(isAppleTerminal).mockReturnValue(true); - const result = renderWithProviders(); + const result = await renderWithProviders(); await result.waitUntilReady(); await expect(result).toMatchSvgSnapshot(); diff --git a/packages/cli/src/ui/components/AskUserDialog.test.tsx b/packages/cli/src/ui/components/AskUserDialog.test.tsx index 67289769be..8ed240389c 100644 --- a/packages/cli/src/ui/components/AskUserDialog.test.tsx +++ b/packages/cli/src/ui/components/AskUserDialog.test.tsx @@ -48,7 +48,7 @@ describe('AskUserDialog', () => { ]; it('renders question and options', async () => { - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { ])('Submission: $name', ({ name, questions, actions, expectedSubmit }) => { it(`submits correct values for ${name}`, async () => { const onSubmit = vi.fn(); - const { stdin } = renderWithProviders( + const { stdin } = await renderWithProviders( { }, ] as Question[]; - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { it('handles custom option in single select with inline typing', async () => { const onSubmit = vi.fn(); - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { ]; const onSubmit = vi.fn(); - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { ); it('navigates to custom option when typing unbound characters (Type-to-Jump)', async () => { - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { }); it('hides progress header for single question', async () => { - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { }); it('shows keyboard hints', async () => { - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { ]; const onSubmit = vi.fn(); - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { ]; const onSubmit = vi.fn(); - const { stdin } = renderWithProviders( + const { stdin } = await renderWithProviders( { }, ]; - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { ]; const onSubmit = vi.fn(); - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { ]; const onSubmit = vi.fn(); - const { stdin } = renderWithProviders( + const { stdin } = await renderWithProviders( { ]; const onCancel = vi.fn(); - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { ]; const onSubmit = vi.fn(); - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { availableTerminalHeight: 5, // Small height to force scroll arrows } as UIState; - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { availableTerminalHeight: 5, } as UIState; - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { }, ]; - const { stdin, lastFrame, waitUntilReady } = renderWithProviders( + const { stdin, lastFrame, waitUntilReady } = await renderWithProviders( { ]; const onSubmit = vi.fn(); - const { stdin } = renderWithProviders( + const { stdin } = await renderWithProviders( { ['info mode', false, 'Info Message'], ['multi-line warning', true, 'Title Line\\nBody Line 1\\nBody Line 2'], ])('renders in %s', async (_, isWarning, text) => { - const renderResult = renderWithProviders( + const renderResult = await renderWithProviders( , ); await renderResult.waitUntilReady(); @@ -24,7 +24,7 @@ describe('Banner', () => { it('handles newlines in text', async () => { const text = 'Line 1\\nLine 2'; - const renderResult = renderWithProviders( + const renderResult = await renderWithProviders( , ); await renderResult.waitUntilReady(); diff --git a/packages/cli/src/ui/components/BubblingRegression.test.tsx b/packages/cli/src/ui/components/BubblingRegression.test.tsx index b91943b019..5e83a6b9eb 100644 --- a/packages/cli/src/ui/components/BubblingRegression.test.tsx +++ b/packages/cli/src/ui/components/BubblingRegression.test.tsx @@ -30,7 +30,7 @@ describe('Key Bubbling Regression', () => { ]; it('does not navigate when pressing "j" or "k" in a focused text input', async () => { - const { stdin, lastFrame } = renderWithProviders( + const { stdin, lastFrame } = await renderWithProviders( ', () => { it('should increment debugNumAnimatedComponents on mount and decrement on unmount', async () => { expect(debugState.debugNumAnimatedComponents).toBe(0); - const { waitUntilReady, unmount } = renderWithProviders(); + const { waitUntilReady, unmount } = await renderWithProviders( + , + ); await waitUntilReady(); expect(debugState.debugNumAnimatedComponents).toBe(1); unmount(); @@ -26,7 +28,7 @@ describe('', () => { it('should not render when showSpinner is false', async () => { const settings = createMockSettings({ ui: { showSpinner: false } }); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { settings }, ); diff --git a/packages/cli/src/ui/components/ColorsDisplay.test.tsx b/packages/cli/src/ui/components/ColorsDisplay.test.tsx index ec44bd6406..fdd08fd653 100644 --- a/packages/cli/src/ui/components/ColorsDisplay.test.tsx +++ b/packages/cli/src/ui/components/ColorsDisplay.test.tsx @@ -96,7 +96,7 @@ describe('ColorsDisplay', () => { it('renders correctly', async () => { const mockTheme = themeManager.getActiveTheme(); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); diff --git a/packages/cli/src/ui/components/ConfigInitDisplay.test.tsx b/packages/cli/src/ui/components/ConfigInitDisplay.test.tsx index 36ecbcbe5f..45ead4862e 100644 --- a/packages/cli/src/ui/components/ConfigInitDisplay.test.tsx +++ b/packages/cli/src/ui/components/ConfigInitDisplay.test.tsx @@ -43,7 +43,7 @@ describe('ConfigInitDisplay', () => { }); it('renders initial state', async () => { - const { lastFrame, waitUntilReady } = renderWithProviders( + const { lastFrame, waitUntilReady } = await renderWithProviders( , ); await waitUntilReady(); @@ -59,7 +59,7 @@ describe('ConfigInitDisplay', () => { return coreEvents; }); - const { lastFrame } = renderWithProviders(); + const { lastFrame } = await renderWithProviders(); // Wait for listener to be registered await waitFor(() => { @@ -97,7 +97,7 @@ describe('ConfigInitDisplay', () => { return coreEvents; }); - const { lastFrame } = renderWithProviders(); + const { lastFrame } = await renderWithProviders(); await waitFor(() => { if (!listener) throw new Error('Listener not registered yet'); @@ -133,7 +133,7 @@ describe('ConfigInitDisplay', () => { return coreEvents; }); - const { lastFrame } = renderWithProviders(); + const { lastFrame } = await renderWithProviders(); await waitFor(() => { if (!listener) throw new Error('Listener not registered yet'); diff --git a/packages/cli/src/ui/components/ContextUsageDisplay.test.tsx b/packages/cli/src/ui/components/ContextUsageDisplay.test.tsx index dcb2a3eae7..904e06635c 100644 --- a/packages/cli/src/ui/components/ContextUsageDisplay.test.tsx +++ b/packages/cli/src/ui/components/ContextUsageDisplay.test.tsx @@ -19,7 +19,7 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => { describe('ContextUsageDisplay', () => { it('renders correct percentage used', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( { }); it('renders correctly when usage is 0%', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( { }); it('renders abbreviated label when terminal width is small', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( { }); it('renders 80% correctly', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( { }); it('renders 100% when full', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( ({ describe('DetailedMessagesDisplay', () => { it('renders nothing when messages are empty', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( { { type: 'debug', content: 'Debug message', count: 1 }, ]; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( { { type: 'error', content: 'Error message', count: 1 }, ]; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( { { type: 'error', content: 'Error message', count: 1 }, ]; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( { { type: 'log', content: 'Repeated message', count: 5 }, ]; - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( { }; it('renders nothing by default', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: baseUiState as Partial as UIState }, ); @@ -197,7 +197,7 @@ describe('DialogManager', () => { it.each(testCases)( 'renders %s when state is %o', async (uiStateOverride, expectedComponent) => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , { uiState: { diff --git a/packages/cli/src/ui/components/EditorSettingsDialog.test.tsx b/packages/cli/src/ui/components/EditorSettingsDialog.test.tsx index d3b285c3a4..bd995652b1 100644 --- a/packages/cli/src/ui/components/EditorSettingsDialog.test.tsx +++ b/packages/cli/src/ui/components/EditorSettingsDialog.test.tsx @@ -51,11 +51,11 @@ describe('EditorSettingsDialog', () => { vi.clearAllMocks(); }); - const renderWithProvider = (ui: React.ReactElement) => + const renderWithProvider = async (ui: React.ReactElement) => renderWithProviders(ui); it('renders correctly', async () => { - const { lastFrame, waitUntilReady } = renderWithProvider( + const { lastFrame, waitUntilReady } = await renderWithProvider( { it('calls onSelect when an editor is selected', async () => { const onSelect = vi.fn(); - const { lastFrame, waitUntilReady } = renderWithProvider( + const { lastFrame, waitUntilReady } = await renderWithProvider( { }); it('switches focus between editor and scope sections on Tab', async () => { - const { lastFrame, stdin, waitUntilReady } = renderWithProvider( + const { lastFrame, stdin, waitUntilReady } = await renderWithProvider( { it('calls onExit when Escape is pressed', async () => { const onExit = vi.fn(); - const { stdin, waitUntilReady } = renderWithProvider( + const { stdin, waitUntilReady } = await renderWithProvider( { }, } as unknown as LoadedSettings; - const { lastFrame, waitUntilReady } = renderWithProvider( + const { lastFrame, waitUntilReady } = await renderWithProvider( { describe('rendering', () => { it('should match snapshot with fallback available', async () => { - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { }); it('should match snapshot without fallback', async () => { - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { }); it('should display the model name and usage limit message', async () => { - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { }); it('should display purchase prompt and credits update notice', async () => { - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { }); it('should display reset time when provided', async () => { - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { }); it('should not display reset time when not provided', async () => { - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { }); it('should display slash command hints', async () => { - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { describe('onChoice handling', () => { it('should call onGetCredits and onChoice when get_credits is selected', async () => { // get_credits is the first item, so just press Enter - const { unmount, stdin, waitUntilReady } = renderWithProviders( + const { unmount, stdin, waitUntilReady } = await renderWithProviders( { }); it('should call onChoice without onGetCredits when onGetCredits is not provided', async () => { - const { unmount, stdin, waitUntilReady } = renderWithProviders( + const { unmount, stdin, waitUntilReady } = await renderWithProviders( { it('should call onChoice with use_fallback when selected', async () => { // With fallback: items are [get_credits, use_fallback, stop] // use_fallback is the second item: Down + Enter - const { unmount, stdin, waitUntilReady } = renderWithProviders( + const { unmount, stdin, waitUntilReady } = await renderWithProviders( { it('should call onChoice with stop when selected', async () => { // Without fallback: items are [get_credits, stop] // stop is the second item: Down + Enter - const { unmount, stdin, waitUntilReady } = renderWithProviders( + const { unmount, stdin, waitUntilReady } = await renderWithProviders( { + const renderDialog = async (options?: { useAlternateBuffer?: boolean }) => { const useAlternateBuffer = options?.useAlternateBuffer ?? true; return renderWithProviders( { it('renders correctly with plan content', async () => { - const { lastFrame } = renderDialog({ useAlternateBuffer }); + const { lastFrame } = await act(async () => + renderDialog({ useAlternateBuffer }), + ); // Advance timers to pass the debounce period await act(async () => { @@ -199,7 +201,9 @@ Implement a comprehensive authentication system with multiple providers. }); it('calls onApprove with AUTO_EDIT when first option is selected', async () => { - const { stdin, lastFrame } = renderDialog({ useAlternateBuffer }); + const { stdin, lastFrame } = await act(async () => + renderDialog({ useAlternateBuffer }), + ); await act(async () => { vi.runAllTimers(); @@ -217,7 +221,9 @@ Implement a comprehensive authentication system with multiple providers. }); it('calls onApprove with DEFAULT when second option is selected', async () => { - const { stdin, lastFrame } = renderDialog({ useAlternateBuffer }); + const { stdin, lastFrame } = await act(async () => + renderDialog({ useAlternateBuffer }), + ); await act(async () => { vi.runAllTimers(); @@ -236,7 +242,9 @@ Implement a comprehensive authentication system with multiple providers. }); it('calls onFeedback when feedback is typed and submitted', async () => { - const { stdin, lastFrame } = renderDialog({ useAlternateBuffer }); + const { stdin, lastFrame } = await act(async () => + renderDialog({ useAlternateBuffer }), + ); await act(async () => { vi.runAllTimers(); @@ -267,7 +275,9 @@ Implement a comprehensive authentication system with multiple providers. }); it('calls onCancel when Esc is pressed', async () => { - const { stdin, lastFrame } = renderDialog({ useAlternateBuffer }); + const { stdin, lastFrame } = await act(async () => + renderDialog({ useAlternateBuffer }), + ); await act(async () => { vi.runAllTimers(); @@ -293,7 +303,9 @@ Implement a comprehensive authentication system with multiple providers. error: 'File not found', }); - const { lastFrame } = renderDialog({ useAlternateBuffer }); + const { lastFrame } = await act(async () => + renderDialog({ useAlternateBuffer }), + ); await act(async () => { vi.runAllTimers(); @@ -309,7 +321,9 @@ Implement a comprehensive authentication system with multiple providers. it('displays error state when plan file is empty', async () => { vi.mocked(validatePlanContent).mockResolvedValue('Plan file is empty.'); - const { lastFrame } = renderDialog({ useAlternateBuffer }); + const { lastFrame } = await act(async () => + renderDialog({ useAlternateBuffer }), + ); await act(async () => { vi.runAllTimers(); @@ -328,7 +342,9 @@ Implement a comprehensive authentication system with multiple providers. returnDisplay: 'Read file', }); - const { lastFrame } = renderDialog({ useAlternateBuffer }); + const { lastFrame } = await act(async () => + renderDialog({ useAlternateBuffer }), + ); await act(async () => { vi.runAllTimers(); @@ -344,7 +360,9 @@ Implement a comprehensive authentication system with multiple providers. }); it('allows number key quick selection', async () => { - const { stdin, lastFrame } = renderDialog({ useAlternateBuffer }); + const { stdin, lastFrame } = await act(async () => + renderDialog({ useAlternateBuffer }), + ); await act(async () => { vi.runAllTimers(); @@ -363,7 +381,9 @@ Implement a comprehensive authentication system with multiple providers. }); it('clears feedback text when Ctrl+C is pressed while editing', async () => { - const { stdin, lastFrame } = renderDialog({ useAlternateBuffer }); + const { stdin, lastFrame } = await act(async () => + renderDialog({ useAlternateBuffer }), + ); await act(async () => { vi.runAllTimers(); @@ -420,7 +440,7 @@ Implement a comprehensive authentication system with multiple providers. return <>{children}; }; - const { stdin, lastFrame } = renderWithProviders( + const { stdin, lastFrame } = await renderWithProviders( { - const { stdin, lastFrame } = renderDialog({ useAlternateBuffer }); + const { stdin, lastFrame } = await act(async () => + renderDialog({ useAlternateBuffer }), + ); await act(async () => { vi.runAllTimers(); @@ -518,7 +540,9 @@ Implement a comprehensive authentication system with multiple providers. }); it('allows arrow navigation while typing feedback to change selection', async () => { - const { stdin, lastFrame } = renderDialog({ useAlternateBuffer }); + const { stdin, lastFrame } = await act(async () => + renderDialog({ useAlternateBuffer }), + ); await act(async () => { vi.runAllTimers(); @@ -550,7 +574,9 @@ Implement a comprehensive authentication system with multiple providers. }); it('automatically submits feedback when Ctrl+X is used to edit the plan', async () => { - const { stdin, lastFrame } = renderDialog({ useAlternateBuffer }); + const { stdin, lastFrame } = await act(async () => + renderDialog({ useAlternateBuffer }), + ); await act(async () => { vi.runAllTimers(); diff --git a/packages/cli/src/ui/components/FolderTrustDialog.test.tsx b/packages/cli/src/ui/components/FolderTrustDialog.test.tsx index 9ad4fac02d..c1d04b3ff9 100644 --- a/packages/cli/src/ui/components/FolderTrustDialog.test.tsx +++ b/packages/cli/src/ui/components/FolderTrustDialog.test.tsx @@ -48,7 +48,7 @@ describe('FolderTrustDialog', () => { }); it('should render the dialog with title and description', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -72,7 +72,7 @@ describe('FolderTrustDialog', () => { discoveryErrors: [], securityWarnings: [], }; - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { discoveryErrors: [], securityWarnings: [], }; - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { discoveryErrors: [], securityWarnings: [], }; - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { securityWarnings: [], }; - const { lastFrame, unmount } = renderWithProviders( + const { lastFrame, unmount } = await renderWithProviders( { // because it's handled in AppContainer. // But we can re-render with constrainHeight: false. const { lastFrame: lastFrameExpanded, unmount: unmountExpanded } = - renderWithProviders( + await renderWithProviders( { it('should display exit message and call process.exit and not call onSelect when escape is pressed', async () => { const onSelect = vi.fn(); - const { lastFrame, stdin, waitUntilReady, unmount } = renderWithProviders( - , - ); + const { lastFrame, stdin, waitUntilReady, unmount } = + await renderWithProviders( + , + ); await waitUntilReady(); await act(async () => { @@ -245,7 +246,7 @@ describe('FolderTrustDialog', () => { }); it('should display restart message when isRestarting is true', async () => { - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -259,7 +260,7 @@ describe('FolderTrustDialog', () => { const relaunchApp = vi .spyOn(processUtils, 'relaunchApp') .mockResolvedValue(undefined); - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -274,7 +275,7 @@ describe('FolderTrustDialog', () => { const relaunchApp = vi .spyOn(processUtils, 'relaunchApp') .mockResolvedValue(undefined); - const { waitUntilReady, unmount } = renderWithProviders( + const { waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -288,7 +289,7 @@ describe('FolderTrustDialog', () => { }); it('should not call process.exit when "r" is pressed and isRestarting is false', async () => { - const { stdin, waitUntilReady, unmount } = renderWithProviders( + const { stdin, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -307,7 +308,7 @@ describe('FolderTrustDialog', () => { describe('directory display', () => { it('should correctly display the folder name for a nested directory', async () => { mockedCwd.mockReturnValue('/home/user/project'); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -317,7 +318,7 @@ describe('FolderTrustDialog', () => { it('should correctly display the parent folder name for a nested directory', async () => { mockedCwd.mockReturnValue('/home/user/project'); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -327,7 +328,7 @@ describe('FolderTrustDialog', () => { it('should correctly display an empty parent folder name for a directory directly under root', async () => { mockedCwd.mockReturnValue('/project'); - const { lastFrame, waitUntilReady, unmount } = renderWithProviders( + const { lastFrame, waitUntilReady, unmount } = await renderWithProviders( , ); await waitUntilReady(); @@ -347,7 +348,7 @@ describe('FolderTrustDialog', () => { discoveryErrors: [], securityWarnings: [], }; - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { discoveryErrors: [], securityWarnings: ['Dangerous setting detected!'], }; - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { discoveryErrors: ['Failed to load custom commands'], securityWarnings: [], }; - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { discoveryErrors: [], securityWarnings: [], }; - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { securityWarnings: [`${ansiRed}warning-with-ansi${ansiReset}`], }; - const { lastFrame, unmount, waitUntilReady } = renderWithProviders( + const { lastFrame, unmount, waitUntilReady } = await renderWithProviders( { return frame.replace(/\\/g, '/'); }; -let mockIsDevelopment = false; +const { mocks } = vi.hoisted(() => ({ + mocks: { + isDevelopment: false, + }, +})); vi.mock('../../utils/installationInfo.js', async (importOriginal) => { const original = @@ -24,7 +29,7 @@ vi.mock('../../utils/installationInfo.js', async (importOriginal) => { return { ...original, get isDevelopment() { - return mockIsDevelopment; + return mocks.isDevelopment; }, }; }); @@ -45,11 +50,34 @@ vi.mock('@google/gemini-cli-core', async (importOriginal) => { const defaultProps = { model: 'gemini-pro', - targetDir: - '/Users/test/project/foo/bar/and/some/more/directories/to/make/it/long', + targetDir: path.join( + path.parse(process.cwd()).root, + 'Users', + 'test', + 'project', + 'foo', + 'bar', + 'and', + 'some', + 'more', + 'directories', + 'to', + 'make', + 'it', + 'long', + ), branchName: 'main', }; +const mockConfig = { + getTargetDir: () => defaultProps.targetDir, + getDebugMode: () => false, + getModel: () => defaultProps.model, + getIdeMode: () => false, + isTrustedFolder: () => true, + getExtensionRegistryURI: () => undefined, +} as unknown as Config; + const mockSessionStats = { sessionId: 'test-session-id', sessionStartTime: new Date(), @@ -110,9 +138,10 @@ describe('