Files
oh-my-pi/packages/coding-agent/test/input-controller-keybindings.test.ts
T
2026-04-08 18:52:42 +02:00

123 lines
3.6 KiB
TypeScript

import { describe, expect, it, vi } from "bun:test";
import { InputController } from "../src/modes/controllers/input-controller";
import type { InteractiveModeContext } from "../src/modes/types";
type FakeEditor = {
onEscape?: () => void;
shouldBypassAutocompleteOnEscape?: () => boolean;
onClear?: () => void;
onExit?: () => void;
onSuspend?: () => void;
onCycleThinkingLevel?: () => void;
onCycleModelForward?: () => void;
onCycleModelBackward?: () => void;
onSelectModelTemporary?: () => void;
onSelectModel?: () => void;
onHistorySearch?: () => void;
onShowHotkeys?: () => void;
onPasteImage?: () => Promise<boolean>;
onCopyPrompt?: () => void;
onExpandTools?: () => void;
onToggleThinking?: () => void;
onExternalEditor?: () => void;
onDequeue?: () => void;
onChange?: (text: string) => void;
setText(text: string): void;
getText(): string;
addToHistory(text: string): void;
setActionKeys(action: string, keys: string[]): void;
setCustomKeyHandler(key: string, handler: () => void): void;
clearCustomKeyHandlers(): void;
};
async function createContext() {
let editorText = "";
const keyMap: Record<string, string[]> = {
"app.model.selectTemporary": ["ctrl+y"],
"app.model.select": ["ctrl+l"],
};
const setActionKeys = vi.fn();
const showModelSelector = vi.fn();
const editor: FakeEditor = {
setText(text: string) {
editorText = text;
},
getText() {
return editorText;
},
addToHistory: vi.fn(),
setActionKeys,
setCustomKeyHandler: vi.fn(),
clearCustomKeyHandlers: vi.fn(),
};
const ctx = {
editor: editor as unknown as InteractiveModeContext["editor"],
ui: {} as InteractiveModeContext["ui"],
loadingAnimation: undefined,
autoCompactionLoader: undefined,
retryLoader: undefined,
autoCompactionEscapeHandler: undefined,
retryEscapeHandler: undefined,
session: {
isStreaming: false,
isCompacting: false,
isGeneratingHandoff: false,
isBashRunning: false,
isPythonRunning: false,
extensionRunner: undefined,
} as InteractiveModeContext["session"],
keybindings: {
getKeys(action: string) {
return keyMap[action] ? [...keyMap[action]] : [];
},
} as InteractiveModeContext["keybindings"],
pendingImages: [],
isBashMode: false,
isPythonMode: false,
handleHotkeysCommand: vi.fn(),
handlePlanModeCommand: vi.fn(),
handleClearCommand: vi.fn(),
showTreeSelector: vi.fn(),
showUserMessageSelector: vi.fn(),
showSessionSelector: vi.fn(),
handleSTTToggle: vi.fn(),
showDebugSelector: vi.fn(),
showHistorySearch: vi.fn(),
toggleThinkingBlockVisibility: vi.fn(),
showModelSelector,
updateEditorBorderColor: vi.fn(),
hasActiveBtw: vi.fn(() => false),
} as unknown as InteractiveModeContext;
return {
InputController,
ctx,
editor,
spies: {
setActionKeys,
showModelSelector,
},
};
}
describe("InputController keybinding setup", () => {
it("registers temporary and persisted model selector actions separately", async () => {
const { InputController, ctx, editor, spies } = await createContext();
const controller = new InputController(ctx);
controller.setupKeyHandlers();
expect(spies.setActionKeys).toHaveBeenCalledWith("app.model.selectTemporary", ["ctrl+y"]);
expect(spies.setActionKeys).toHaveBeenCalledWith("app.model.select", ["ctrl+l"]);
expect(editor.onSelectModelTemporary).toBeDefined();
expect(editor.onSelectModel).toBeDefined();
expect(editor.onSelectModelTemporary).not.toBe(editor.onSelectModel);
editor.onSelectModelTemporary?.();
editor.onSelectModel?.();
expect(spies.showModelSelector).toHaveBeenNthCalledWith(1, { temporaryOnly: true });
expect(spies.showModelSelector).toHaveBeenNthCalledWith(2);
});
});