mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-20 10:10:56 -07:00
refactor(cli): better react patterns for BaseSettingsDialog (#21206)
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { renderHook } from '../../test-utils/render.js';
|
||||
import { act } from 'react';
|
||||
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
|
||||
import { useInlineEditBuffer } from './useInlineEditBuffer.js';
|
||||
|
||||
describe('useEditBuffer', () => {
|
||||
let mockOnCommit: Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockOnCommit = vi.fn();
|
||||
});
|
||||
|
||||
it('should initialize with empty state', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInlineEditBuffer({ onCommit: mockOnCommit }),
|
||||
);
|
||||
expect(result.current.editState.editingKey).toBeNull();
|
||||
expect(result.current.editState.buffer).toBe('');
|
||||
expect(result.current.editState.cursorPos).toBe(0);
|
||||
});
|
||||
|
||||
it('should start editing correctly', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInlineEditBuffer({ onCommit: mockOnCommit }),
|
||||
);
|
||||
act(() => result.current.startEditing('my-key', 'initial'));
|
||||
|
||||
expect(result.current.editState.editingKey).toBe('my-key');
|
||||
expect(result.current.editState.buffer).toBe('initial');
|
||||
expect(result.current.editState.cursorPos).toBe(7); // End of string
|
||||
});
|
||||
|
||||
it('should commit edit and reset state', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInlineEditBuffer({ onCommit: mockOnCommit }),
|
||||
);
|
||||
|
||||
act(() => result.current.startEditing('my-key', 'text'));
|
||||
act(() => result.current.commitEdit());
|
||||
|
||||
expect(mockOnCommit).toHaveBeenCalledWith('my-key', 'text');
|
||||
expect(result.current.editState.editingKey).toBeNull();
|
||||
expect(result.current.editState.buffer).toBe('');
|
||||
});
|
||||
|
||||
it('should move cursor left and right', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInlineEditBuffer({ onCommit: mockOnCommit }),
|
||||
);
|
||||
act(() => result.current.startEditing('key', 'ab')); // cursor at 2
|
||||
|
||||
act(() => result.current.editDispatch({ type: 'MOVE_LEFT' }));
|
||||
expect(result.current.editState.cursorPos).toBe(1);
|
||||
|
||||
act(() => result.current.editDispatch({ type: 'MOVE_LEFT' }));
|
||||
expect(result.current.editState.cursorPos).toBe(0);
|
||||
|
||||
// Shouldn't go below 0
|
||||
act(() => result.current.editDispatch({ type: 'MOVE_LEFT' }));
|
||||
expect(result.current.editState.cursorPos).toBe(0);
|
||||
|
||||
act(() => result.current.editDispatch({ type: 'MOVE_RIGHT' }));
|
||||
expect(result.current.editState.cursorPos).toBe(1);
|
||||
});
|
||||
|
||||
it('should handle home and end', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInlineEditBuffer({ onCommit: mockOnCommit }),
|
||||
);
|
||||
act(() => result.current.startEditing('key', 'testing')); // cursor at 7
|
||||
|
||||
act(() => result.current.editDispatch({ type: 'HOME' }));
|
||||
expect(result.current.editState.cursorPos).toBe(0);
|
||||
|
||||
act(() => result.current.editDispatch({ type: 'END' }));
|
||||
expect(result.current.editState.cursorPos).toBe(7);
|
||||
});
|
||||
|
||||
it('should delete characters to the left (backspace)', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInlineEditBuffer({ onCommit: mockOnCommit }),
|
||||
);
|
||||
act(() => result.current.startEditing('key', 'abc')); // cursor at 3
|
||||
|
||||
act(() => result.current.editDispatch({ type: 'DELETE_LEFT' }));
|
||||
expect(result.current.editState.buffer).toBe('ab');
|
||||
expect(result.current.editState.cursorPos).toBe(2);
|
||||
|
||||
// Move to start, shouldn't delete
|
||||
act(() => result.current.editDispatch({ type: 'HOME' }));
|
||||
act(() => result.current.editDispatch({ type: 'DELETE_LEFT' }));
|
||||
expect(result.current.editState.buffer).toBe('ab');
|
||||
});
|
||||
|
||||
it('should delete characters to the right (delete tab)', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInlineEditBuffer({ onCommit: mockOnCommit }),
|
||||
);
|
||||
act(() => result.current.startEditing('key', 'abc'));
|
||||
act(() => result.current.editDispatch({ type: 'HOME' })); // cursor at 0
|
||||
|
||||
act(() => result.current.editDispatch({ type: 'DELETE_RIGHT' }));
|
||||
expect(result.current.editState.buffer).toBe('bc');
|
||||
expect(result.current.editState.cursorPos).toBe(0);
|
||||
});
|
||||
|
||||
it('should insert valid characters into string', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInlineEditBuffer({ onCommit: mockOnCommit }),
|
||||
);
|
||||
act(() => result.current.startEditing('key', 'ab'));
|
||||
act(() => result.current.editDispatch({ type: 'MOVE_LEFT' })); // cursor at 1
|
||||
|
||||
act(() =>
|
||||
result.current.editDispatch({
|
||||
type: 'INSERT_CHAR',
|
||||
char: 'x',
|
||||
isNumberType: false,
|
||||
}),
|
||||
);
|
||||
expect(result.current.editState.buffer).toBe('axb');
|
||||
expect(result.current.editState.cursorPos).toBe(2);
|
||||
});
|
||||
|
||||
it('should validate number character insertions', () => {
|
||||
const { result } = renderHook(() =>
|
||||
useInlineEditBuffer({ onCommit: mockOnCommit }),
|
||||
);
|
||||
act(() => result.current.startEditing('key', '12'));
|
||||
|
||||
// Valid number char
|
||||
act(() =>
|
||||
result.current.editDispatch({
|
||||
type: 'INSERT_CHAR',
|
||||
char: '.',
|
||||
isNumberType: true,
|
||||
}),
|
||||
);
|
||||
expect(result.current.editState.buffer).toBe('12.');
|
||||
|
||||
// Invalid number char
|
||||
act(() =>
|
||||
result.current.editDispatch({
|
||||
type: 'INSERT_CHAR',
|
||||
char: 'a',
|
||||
isNumberType: true,
|
||||
}),
|
||||
);
|
||||
expect(result.current.editState.buffer).toBe('12.'); // Unchanged
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user