mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-15 07:41:03 -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
@@ -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();
|
||||
|
||||
|
||||
@@ -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<typeof renderWithProviders> | 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(
|
||||
<AppContainer
|
||||
config={this.config!}
|
||||
version="test-version"
|
||||
|
||||
@@ -16,8 +16,6 @@ import { vi } from 'vitest';
|
||||
import stripAnsi from 'strip-ansi';
|
||||
import type React from 'react';
|
||||
import { act, useState } from 'react';
|
||||
import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import type { LoadedSettings } from '../config/settings.js';
|
||||
import { KeypressProvider } from '../ui/contexts/KeypressContext.js';
|
||||
import { SettingsContext } from '../ui/contexts/SettingsContext.js';
|
||||
@@ -44,7 +42,7 @@ import {
|
||||
type OverflowState,
|
||||
} from '../ui/contexts/OverflowContext.js';
|
||||
|
||||
import { makeFakeConfig, type Config } from '@google/gemini-cli-core';
|
||||
import { type Config } from '@google/gemini-cli-core';
|
||||
import { FakePersistentState } from './persistentStateFake.js';
|
||||
import { AppContext, type AppState } from '../ui/contexts/AppContext.js';
|
||||
import { createMockSettings } from './settings.js';
|
||||
@@ -53,6 +51,7 @@ import { themeManager, DEFAULT_THEME } from '../ui/themes/theme-manager.js';
|
||||
import { DefaultLight } from '../ui/themes/builtin/light/default-light.js';
|
||||
import { pickDefaultThemeName } from '../ui/themes/theme.js';
|
||||
import { generateSvgForTerminal } from './svg.js';
|
||||
import { loadCliConfig, type CliArgs } from '../config/config.js';
|
||||
|
||||
export const persistentStateMock = new FakePersistentState();
|
||||
|
||||
@@ -66,7 +65,9 @@ if (process.env['NODE_ENV'] === 'test') {
|
||||
}
|
||||
|
||||
vi.mock('../utils/persistentState.js', () => ({
|
||||
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<void>;
|
||||
} => {
|
||||
): Promise<
|
||||
RenderInstance & {
|
||||
simulateClick: (
|
||||
col: number,
|
||||
row: number,
|
||||
button?: 0 | 1 | 2,
|
||||
) => Promise<void>;
|
||||
}
|
||||
> => {
|
||||
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) => (
|
||||
<AppContext.Provider value={appState}>
|
||||
<ConfigContext.Provider value={finalConfig}>
|
||||
<SettingsContext.Provider value={finalSettings}>
|
||||
<ConfigContext.Provider value={config}>
|
||||
<SettingsContext.Provider value={settings}>
|
||||
<UIStateContext.Provider value={finalUiState}>
|
||||
<VimModeProvider>
|
||||
<ShellFocusContext.Provider value={shellFocus}>
|
||||
@@ -744,7 +709,7 @@ export const renderWithProviders = (
|
||||
<UIActionsContext.Provider value={finalUIActions}>
|
||||
<OverflowProvider>
|
||||
<ToolActionsProvider
|
||||
config={finalConfig}
|
||||
config={config}
|
||||
toolCalls={allToolCalls}
|
||||
>
|
||||
<AskUserActionsProvider
|
||||
@@ -863,7 +828,7 @@ export function renderHook<Result, Props>(
|
||||
return { result, rerender, unmount, waitUntilReady, generateSvg };
|
||||
}
|
||||
|
||||
export function renderHookWithProviders<Result, Props>(
|
||||
export async function renderHookWithProviders<Result, Props>(
|
||||
renderCallback: (props: Props) => Result,
|
||||
options: {
|
||||
initialProps?: Props;
|
||||
@@ -876,13 +841,13 @@ export function renderHookWithProviders<Result, Props>(
|
||||
mouseEventsEnabled?: boolean;
|
||||
config?: Config;
|
||||
} = {},
|
||||
): {
|
||||
): Promise<{
|
||||
result: { current: Result };
|
||||
rerender: (props?: Props) => void;
|
||||
unmount: () => void;
|
||||
waitUntilReady: () => Promise<void>;
|
||||
generateSvg: () => string;
|
||||
} {
|
||||
}> {
|
||||
const result = { current: undefined as unknown as Result };
|
||||
|
||||
let setPropsFn: ((props: Props) => void) | undefined;
|
||||
@@ -901,8 +866,8 @@ export function renderHookWithProviders<Result, Props>(
|
||||
|
||||
let renderResult: ReturnType<typeof render>;
|
||||
|
||||
act(() => {
|
||||
renderResult = renderWithProviders(
|
||||
await act(async () => {
|
||||
renderResult = await renderWithProviders(
|
||||
<Wrapper>
|
||||
{}
|
||||
<TestComponent initialProps={options.initialProps as Props} />
|
||||
|
||||
Reference in New Issue
Block a user