sync gemini-cli 0.1.17

Co-Authored-By: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
Yiheng Xu
2025-08-05 16:44:06 +08:00
235 changed files with 16997 additions and 3736 deletions

View File

@@ -30,7 +30,6 @@ import {
SlashCommandProcessorResult,
StreamingState,
} from '../types.js';
import { Dispatch, SetStateAction } from 'react';
import { LoadedSettings } from '../../config/settings.js';
// --- MOCKS ---
@@ -257,7 +256,6 @@ describe('mergePartListUnions', () => {
// --- Tests for useGeminiStream Hook ---
describe('useGeminiStream', () => {
let mockAddItem: Mock;
let mockSetShowHelp: Mock;
let mockConfig: Config;
let mockOnDebugMessage: Mock;
let mockHandleSlashCommand: Mock;
@@ -269,7 +267,6 @@ describe('useGeminiStream', () => {
vi.clearAllMocks(); // Clear mocks before each test
mockAddItem = vi.fn();
mockSetShowHelp = vi.fn();
// Define the mock for getGeminiClient
const mockGetGeminiClient = vi.fn().mockImplementation(() => {
// MockedGeminiClientClass is defined in the module scope by the previous change.
@@ -319,6 +316,7 @@ describe('useGeminiStream', () => {
},
setQuotaErrorOccurred: vi.fn(),
getQuotaErrorOccurred: vi.fn(() => false),
getModel: vi.fn(() => 'gemini-2.5-pro'),
getContentGeneratorConfig: vi
.fn()
.mockReturnValue(contentGeneratorConfig),
@@ -381,7 +379,6 @@ describe('useGeminiStream', () => {
client: any;
history: HistoryItem[];
addItem: UseHistoryManagerReturn['addItem'];
setShowHelp: Dispatch<SetStateAction<boolean>>;
config: Config;
onDebugMessage: (message: string) => void;
handleSlashCommand: (
@@ -399,7 +396,6 @@ describe('useGeminiStream', () => {
props.client,
props.history,
props.addItem,
props.setShowHelp,
props.config,
props.onDebugMessage,
props.handleSlashCommand,
@@ -416,7 +412,6 @@ describe('useGeminiStream', () => {
client,
history: [],
addItem: mockAddItem as unknown as UseHistoryManagerReturn['addItem'],
setShowHelp: mockSetShowHelp,
config: mockConfig,
onDebugMessage: mockOnDebugMessage,
handleSlashCommand: mockHandleSlashCommand as unknown as (
@@ -541,7 +536,6 @@ describe('useGeminiStream', () => {
new MockedGeminiClientClass(mockConfig),
[],
mockAddItem,
mockSetShowHelp,
mockConfig,
mockOnDebugMessage,
mockHandleSlashCommand,
@@ -609,7 +603,6 @@ describe('useGeminiStream', () => {
client,
[],
mockAddItem,
mockSetShowHelp,
mockConfig,
mockOnDebugMessage,
mockHandleSlashCommand,
@@ -706,7 +699,6 @@ describe('useGeminiStream', () => {
client,
[],
mockAddItem,
mockSetShowHelp,
mockConfig,
mockOnDebugMessage,
mockHandleSlashCommand,
@@ -809,7 +801,6 @@ describe('useGeminiStream', () => {
new MockedGeminiClientClass(mockConfig),
[],
mockAddItem,
mockSetShowHelp,
mockConfig,
mockOnDebugMessage,
mockHandleSlashCommand,
@@ -1160,7 +1151,6 @@ describe('useGeminiStream', () => {
new MockedGeminiClientClass(mockConfig),
[],
mockAddItem,
mockSetShowHelp,
mockConfig,
mockOnDebugMessage,
mockHandleSlashCommand,
@@ -1212,7 +1202,6 @@ describe('useGeminiStream', () => {
new MockedGeminiClientClass(testConfig),
[],
mockAddItem,
mockSetShowHelp,
testConfig,
mockOnDebugMessage,
mockHandleSlashCommand,
@@ -1261,7 +1250,6 @@ describe('useGeminiStream', () => {
new MockedGeminiClientClass(mockConfig),
[],
mockAddItem,
mockSetShowHelp,
mockConfig,
mockOnDebugMessage,
mockHandleSlashCommand,
@@ -1308,7 +1296,6 @@ describe('useGeminiStream', () => {
new MockedGeminiClientClass(mockConfig),
[],
mockAddItem,
mockSetShowHelp,
mockConfig,
mockOnDebugMessage,
mockHandleSlashCommand,
@@ -1356,7 +1343,6 @@ describe('useGeminiStream', () => {
new MockedGeminiClientClass(mockConfig),
[],
mockAddItem,
mockSetShowHelp,
mockConfig,
mockOnDebugMessage,
mockHandleSlashCommand,
@@ -1444,7 +1430,6 @@ describe('useGeminiStream', () => {
new MockedGeminiClientClass(mockConfig),
[],
mockAddItem,
mockSetShowHelp,
mockConfig,
mockOnDebugMessage,
mockHandleSlashCommand,
@@ -1473,4 +1458,195 @@ describe('useGeminiStream', () => {
}
});
});
describe('Thought Reset', () => {
it('should reset thought to null when starting a new prompt', async () => {
// First, simulate a response with a thought
mockSendMessageStream.mockReturnValue(
(async function* () {
yield {
type: ServerGeminiEventType.Thought,
value: {
subject: 'Previous thought',
description: 'Old description',
},
};
yield {
type: ServerGeminiEventType.Content,
value: 'Some response content',
};
yield { type: ServerGeminiEventType.Finished, value: 'STOP' };
})(),
);
const { result } = renderHook(() =>
useGeminiStream(
new MockedGeminiClientClass(mockConfig),
[],
mockAddItem,
mockConfig,
mockOnDebugMessage,
mockHandleSlashCommand,
false,
() => 'vscode' as EditorType,
() => {},
() => Promise.resolve(),
false,
() => {},
),
);
// Submit first query to set a thought
await act(async () => {
await result.current.submitQuery('First query');
});
// Wait for the first response to complete
await waitFor(() => {
expect(mockAddItem).toHaveBeenCalledWith(
expect.objectContaining({
type: 'gemini',
text: 'Some response content',
}),
expect.any(Number),
);
});
// Now simulate a new response without a thought
mockSendMessageStream.mockReturnValue(
(async function* () {
yield {
type: ServerGeminiEventType.Content,
value: 'New response content',
};
yield { type: ServerGeminiEventType.Finished, value: 'STOP' };
})(),
);
// Submit second query - thought should be reset
await act(async () => {
await result.current.submitQuery('Second query');
});
// The thought should be reset to null when starting the new prompt
// We can verify this by checking that the LoadingIndicator would not show the previous thought
// The actual thought state is internal to the hook, but we can verify the behavior
// by ensuring the second response doesn't show the previous thought
await waitFor(() => {
expect(mockAddItem).toHaveBeenCalledWith(
expect.objectContaining({
type: 'gemini',
text: 'New response content',
}),
expect.any(Number),
);
});
});
it('should reset thought to null when user cancels', async () => {
// Mock a stream that yields a thought then gets cancelled
mockSendMessageStream.mockReturnValue(
(async function* () {
yield {
type: ServerGeminiEventType.Thought,
value: { subject: 'Some thought', description: 'Description' },
};
yield { type: ServerGeminiEventType.UserCancelled };
})(),
);
const { result } = renderHook(() =>
useGeminiStream(
new MockedGeminiClientClass(mockConfig),
[],
mockAddItem,
mockConfig,
mockOnDebugMessage,
mockHandleSlashCommand,
false,
() => 'vscode' as EditorType,
() => {},
() => Promise.resolve(),
false,
() => {},
),
);
// Submit query
await act(async () => {
await result.current.submitQuery('Test query');
});
// Verify cancellation message was added
await waitFor(() => {
expect(mockAddItem).toHaveBeenCalledWith(
expect.objectContaining({
type: 'info',
text: 'User cancelled the request.',
}),
expect.any(Number),
);
});
// Verify state is reset to idle
expect(result.current.streamingState).toBe(StreamingState.Idle);
});
it('should reset thought to null when there is an error', async () => {
// Mock a stream that yields a thought then encounters an error
mockSendMessageStream.mockReturnValue(
(async function* () {
yield {
type: ServerGeminiEventType.Thought,
value: { subject: 'Some thought', description: 'Description' },
};
yield {
type: ServerGeminiEventType.Error,
value: { error: { message: 'Test error' } },
};
})(),
);
const { result } = renderHook(() =>
useGeminiStream(
new MockedGeminiClientClass(mockConfig),
[],
mockAddItem,
mockConfig,
mockOnDebugMessage,
mockHandleSlashCommand,
false,
() => 'vscode' as EditorType,
() => {},
() => Promise.resolve(),
false,
() => {},
),
);
// Submit query
await act(async () => {
await result.current.submitQuery('Test query');
});
// Verify error message was added
await waitFor(() => {
expect(mockAddItem).toHaveBeenCalledWith(
expect.objectContaining({
type: 'error',
}),
expect.any(Number),
);
});
// Verify parseAndFormatApiError was called
expect(mockParseAndFormatApiError).toHaveBeenCalledWith(
{ message: 'Test error' },
expect.any(String),
undefined,
'gemini-2.5-pro',
'gemini-2.5-flash',
);
});
});
});