mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-12 15:10:59 -07:00
fix(ui): preventing empty history items from being added (#19014)
This commit is contained in:
@@ -46,6 +46,7 @@ import type { SlashCommandProcessorResult } from '../types.js';
|
||||
import { MessageType, StreamingState } from '../types.js';
|
||||
|
||||
import type { LoadedSettings } from '../../config/settings.js';
|
||||
import { findLastSafeSplitPoint } from '../utils/markdownUtilities.js';
|
||||
|
||||
// --- MOCKS ---
|
||||
const mockSendMessageStream = vi
|
||||
@@ -3320,4 +3321,88 @@ describe('useGeminiStream', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Stream Splitting', () => {
|
||||
it('should not add empty history item when splitting message results in empty or whitespace-only beforeText', async () => {
|
||||
// Mock split point to always be 0, causing beforeText to be empty
|
||||
vi.mocked(findLastSafeSplitPoint).mockReturnValue(0);
|
||||
|
||||
mockSendMessageStream.mockReturnValue(
|
||||
(async function* () {
|
||||
yield { type: ServerGeminiEventType.Content, value: 'test content' };
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('user query');
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
// We expect the stream to be processed.
|
||||
// Since beforeText is empty (0 split), addItem should NOT be called for it.
|
||||
// addItem IS called for the user query "user query".
|
||||
});
|
||||
|
||||
// Check addItem calls.
|
||||
// It should be called for user query and for the content.
|
||||
expect(mockAddItem).toHaveBeenCalledTimes(2);
|
||||
expect(mockAddItem).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ type: 'user', text: 'user query' }),
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockAddItem).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'gemini_content',
|
||||
text: 'test content',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
|
||||
// Verify that pendingHistoryItem is empty after (afterText).
|
||||
expect(result.current.pendingHistoryItems.length).toEqual(0);
|
||||
|
||||
// Reset mock
|
||||
vi.mocked(findLastSafeSplitPoint).mockReset();
|
||||
vi.mocked(findLastSafeSplitPoint).mockImplementation(
|
||||
(s: string) => s.length,
|
||||
);
|
||||
});
|
||||
|
||||
it('should add whitespace-only history item when splitting message', async () => {
|
||||
// Input: " content"
|
||||
// Split at 3 -> before: " ", after: "content"
|
||||
vi.mocked(findLastSafeSplitPoint).mockReturnValue(3);
|
||||
|
||||
mockSendMessageStream.mockReturnValue(
|
||||
(async function* () {
|
||||
yield { type: ServerGeminiEventType.Content, value: ' content' };
|
||||
})(),
|
||||
);
|
||||
|
||||
const { result } = renderTestHook();
|
||||
|
||||
await act(async () => {
|
||||
await result.current.submitQuery('user query');
|
||||
});
|
||||
|
||||
await waitFor(() => {});
|
||||
|
||||
expect(mockAddItem).toHaveBeenCalledTimes(3);
|
||||
expect(mockAddItem).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ type: 'user', text: 'user query' }),
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(mockAddItem).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'gemini_content',
|
||||
text: 'content',
|
||||
}),
|
||||
expect.any(Number),
|
||||
);
|
||||
|
||||
expect(result.current.pendingHistoryItems.length).toEqual(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -819,16 +819,18 @@ export const useGeminiStream = (
|
||||
// broken up so that there are more "statically" rendered.
|
||||
const beforeText = newGeminiMessageBuffer.substring(0, splitPoint);
|
||||
const afterText = newGeminiMessageBuffer.substring(splitPoint);
|
||||
addItem(
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
type: pendingHistoryItemRef.current?.type as
|
||||
| 'gemini'
|
||||
| 'gemini_content',
|
||||
text: beforeText,
|
||||
},
|
||||
userMessageTimestamp,
|
||||
);
|
||||
if (beforeText.length > 0) {
|
||||
addItem(
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
type: pendingHistoryItemRef.current?.type as
|
||||
| 'gemini'
|
||||
| 'gemini_content',
|
||||
text: beforeText,
|
||||
},
|
||||
userMessageTimestamp,
|
||||
);
|
||||
}
|
||||
setPendingHistoryItem({ type: 'gemini_content', text: afterText });
|
||||
newGeminiMessageBuffer = afterText;
|
||||
}
|
||||
|
||||
@@ -337,7 +337,7 @@ const RenderCodeBlockInternal: React.FC<RenderCodeBlockProps> = ({
|
||||
const RESERVED_LINES = 2; // Lines reserved for the message itself and potential padding
|
||||
|
||||
// When not in alternate buffer mode we need to be careful that we don't
|
||||
// trigger flicker when the pending code is to long to fit in the terminal
|
||||
// trigger flicker when the pending code is too long to fit in the terminal
|
||||
if (
|
||||
!isAlternateBuffer &&
|
||||
isPending &&
|
||||
|
||||
Reference in New Issue
Block a user