Files
gemini-cli/packages/cli/src/ui/hooks/useInlineEditBuffer.test.ts

159 lines
5.2 KiB
TypeScript

/**
* @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
});
});