mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-10 19:37:17 -07:00
test(cli): add unit tests for /btw command functionality and components
This commit is contained in:
committed by
Mahima Shanware
parent
dcde43b031
commit
86487af7ff
@@ -0,0 +1,38 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { btwCommand } from './btwCommand.js';
|
||||
import { CommandKind } from './types.js';
|
||||
import type { CommandContext } from './types.js';
|
||||
|
||||
describe('btwCommand', () => {
|
||||
it('has the correct metadata', () => {
|
||||
expect(btwCommand.name).toBe('btw');
|
||||
expect(btwCommand.kind).toBe(CommandKind.BUILT_IN);
|
||||
expect(btwCommand.autoExecute).toBe(true);
|
||||
expect(btwCommand.isSafeConcurrent).toBe(true);
|
||||
});
|
||||
|
||||
it('returns an error message when args are empty', () => {
|
||||
const context = {} as CommandContext;
|
||||
const result = btwCommand.action!(context, ' ');
|
||||
expect(result).toEqual({
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Please provide a question, e.g. /btw what is this regex doing?',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a btw action when query is provided', () => {
|
||||
const context = {} as CommandContext;
|
||||
const result = btwCommand.action!(context, ' what is this regex doing? ');
|
||||
expect(result).toEqual({
|
||||
type: 'btw',
|
||||
query: 'what is this regex doing?',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,88 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { renderWithProviders } from '../../test-utils/render.js';
|
||||
import { BtwDisplay } from './BtwDisplay.js';
|
||||
import { StreamingState } from '../types.js';
|
||||
import type { UIState } from '../contexts/UIStateContext.js';
|
||||
|
||||
describe('BtwDisplay', () => {
|
||||
const defaultMockUiState = {
|
||||
renderMarkdown: true,
|
||||
} as unknown as Partial<UIState>;
|
||||
|
||||
it('renders nothing when query is empty', async () => {
|
||||
const { lastFrame, unmount } = await renderWithProviders(
|
||||
<BtwDisplay
|
||||
query=""
|
||||
response=""
|
||||
isStreaming={false}
|
||||
error={null}
|
||||
terminalWidth={100}
|
||||
/>,
|
||||
{ uiState: defaultMockUiState },
|
||||
);
|
||||
expect(lastFrame({ allowEmpty: true })).toBe('');
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('renders query and response', async () => {
|
||||
const { lastFrame, unmount } = await renderWithProviders(
|
||||
<BtwDisplay
|
||||
query="What is life?"
|
||||
response="Life is 42."
|
||||
isStreaming={false}
|
||||
error={null}
|
||||
terminalWidth={100}
|
||||
/>,
|
||||
{ uiState: defaultMockUiState },
|
||||
);
|
||||
const frame = lastFrame();
|
||||
expect(frame).toContain('What is life?');
|
||||
expect(frame).toContain('Life is 42.');
|
||||
expect(frame).toContain('BY THE WAY');
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('renders error message when error is provided', async () => {
|
||||
const { lastFrame, unmount } = await renderWithProviders(
|
||||
<BtwDisplay
|
||||
query="What is life?"
|
||||
response="Life is 42."
|
||||
isStreaming={false}
|
||||
error="An API error occurred."
|
||||
terminalWidth={100}
|
||||
/>,
|
||||
{ uiState: defaultMockUiState },
|
||||
);
|
||||
const frame = lastFrame();
|
||||
expect(frame).toContain('An API error occurred.');
|
||||
expect(frame).not.toContain('Life is 42.');
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('renders a spinner when streaming', async () => {
|
||||
const { lastFrame, unmount } = await renderWithProviders(
|
||||
<BtwDisplay
|
||||
query="What is life?"
|
||||
response="Life is 42."
|
||||
isStreaming={true}
|
||||
error={null}
|
||||
terminalWidth={100}
|
||||
/>,
|
||||
{
|
||||
uiState: {
|
||||
...defaultMockUiState,
|
||||
streamingState: StreamingState.Responding,
|
||||
},
|
||||
},
|
||||
);
|
||||
const frame = lastFrame();
|
||||
expect(frame).toContain('⠋'); // Assuming standard spinner frame
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
@@ -5097,6 +5097,92 @@ describe('InputPrompt', () => {
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('Btw dismiss behavior', () => {
|
||||
it('dismisses Btw on ESC key press', async () => {
|
||||
const dismissBtw = vi.fn();
|
||||
const { stdin, unmount } = await renderWithProviders(
|
||||
<InputPrompt {...props} />,
|
||||
{
|
||||
uiState: {
|
||||
btwState: {
|
||||
isActive: true,
|
||||
query: '',
|
||||
response: '',
|
||||
isStreaming: false,
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
uiActions: { dismissBtw },
|
||||
},
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
stdin.write('\x1B'); // ESC
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(dismissBtw).toHaveBeenCalled();
|
||||
});
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('dismisses Btw on Enter key press', async () => {
|
||||
const dismissBtw = vi.fn();
|
||||
const { stdin, unmount } = await renderWithProviders(
|
||||
<InputPrompt {...props} />,
|
||||
{
|
||||
uiState: {
|
||||
btwState: {
|
||||
isActive: true,
|
||||
query: '',
|
||||
response: '',
|
||||
isStreaming: false,
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
uiActions: { dismissBtw },
|
||||
},
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
stdin.write('\r'); // Enter
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(dismissBtw).toHaveBeenCalled();
|
||||
});
|
||||
unmount();
|
||||
});
|
||||
|
||||
it('dismisses Btw on Space key press when buffer is empty', async () => {
|
||||
const dismissBtw = vi.fn();
|
||||
const { stdin, unmount } = await renderWithProviders(
|
||||
<InputPrompt {...props} />,
|
||||
{
|
||||
uiState: {
|
||||
btwState: {
|
||||
isActive: true,
|
||||
query: '',
|
||||
response: '',
|
||||
isStreaming: false,
|
||||
error: null,
|
||||
},
|
||||
},
|
||||
uiActions: { dismissBtw },
|
||||
},
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
stdin.write(' '); // Space
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(dismissBtw).toHaveBeenCalled();
|
||||
});
|
||||
unmount();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function clean(str: string | undefined): string {
|
||||
|
||||
@@ -644,6 +644,31 @@ describe('useSlashCommandProcessor', () => {
|
||||
|
||||
expect(mockSetQuittingMessages).toHaveBeenCalledWith(['bye']);
|
||||
});
|
||||
|
||||
it('should handle a "btw" action', async () => {
|
||||
const btwAction = vi
|
||||
.fn()
|
||||
.mockResolvedValue({ type: 'btw', query: 'some query' });
|
||||
const command = createTestCommand({
|
||||
name: 'side_question',
|
||||
action: btwAction,
|
||||
});
|
||||
const result = await setupProcessorHook({
|
||||
builtinCommands: [command],
|
||||
});
|
||||
|
||||
await waitFor(() => expect(result.current.slashCommands).toHaveLength(1));
|
||||
|
||||
let processedResult;
|
||||
await act(async () => {
|
||||
processedResult = await result.current.handleSlashCommand(
|
||||
'/side_question some args',
|
||||
);
|
||||
});
|
||||
|
||||
expect(processedResult).toEqual({ type: 'btw', query: 'some query' });
|
||||
});
|
||||
|
||||
it('should handle "submit_prompt" action returned from a file-based command', async () => {
|
||||
const fileCommand = createTestCommand(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user