9d457f73d9
- Replaced relative `../src` imports with `@oh-my-pi/pi-ai` and `@oh-my-pi/pi-agent-core` subpaths.
150 lines
5.3 KiB
TypeScript
150 lines
5.3 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
|
import { KeybindingsManager as AppKeybindingsManager } from "@oh-my-pi/pi-coding-agent/config/keybindings";
|
|
import { createPromptActionAutocompleteProvider } from "@oh-my-pi/pi-coding-agent/modes/prompt-action-autocomplete";
|
|
import { KeybindingsManager, setKeybindings, TUI_KEYBINDINGS } from "@oh-my-pi/pi-tui";
|
|
|
|
describe("prompt action autocomplete", () => {
|
|
beforeEach(() => {
|
|
setKeybindings(
|
|
new KeybindingsManager({
|
|
"tui.editor.cursorLineStart": { defaultKeys: ["home", "f6"], description: "Move cursor to line start" },
|
|
"tui.editor.cursorLineEnd": { defaultKeys: "f7", description: "Move cursor to line end" },
|
|
"tui.editor.undo": { defaultKeys: "f8", description: "Undo" },
|
|
}),
|
|
);
|
|
});
|
|
|
|
afterEach(() => {
|
|
setKeybindings(new KeybindingsManager(TUI_KEYBINDINGS));
|
|
});
|
|
|
|
it("shows prompt actions with configured shortcut hints", async () => {
|
|
const provider = createPromptActionAutocompleteProvider({
|
|
commands: [],
|
|
basePath: "/tmp",
|
|
keybindings: AppKeybindingsManager.inMemory({
|
|
"app.clipboard.copyLine": "ctrl+shift+l",
|
|
"app.clipboard.copyPrompt": ["alt+shift+c", "ctrl+shift+c"],
|
|
}),
|
|
copyCurrentLine: () => {},
|
|
copyPrompt: () => {},
|
|
undo: () => {},
|
|
moveCursorToMessageEnd: () => {},
|
|
moveCursorToMessageStart: () => {},
|
|
moveCursorToLineStart: () => {},
|
|
moveCursorToLineEnd: () => {},
|
|
});
|
|
|
|
const suggestions = await provider.getSuggestions(["#"], 0, 1);
|
|
expect(suggestions).not.toBeNull();
|
|
expect(suggestions?.prefix).toBe("#");
|
|
expect(suggestions?.items.map(item => item.label)).toEqual([
|
|
"Copy current line",
|
|
"Copy whole prompt",
|
|
"Undo",
|
|
"Move cursor to end of message",
|
|
"Move cursor to beginning of message",
|
|
"Move cursor to beginning of line",
|
|
"Move cursor to end of line",
|
|
]);
|
|
expect(suggestions?.items.find(item => item.label === "Copy current line")?.description).toBe("Ctrl+Shift+L");
|
|
expect(suggestions?.items.find(item => item.label === "Copy whole prompt")?.description).toBe(
|
|
"Alt+Shift+C/Ctrl+Shift+C",
|
|
);
|
|
expect(suggestions?.items.find(item => item.label === "Move cursor to beginning of line")?.description).toBe(
|
|
"Home/F6",
|
|
);
|
|
expect(suggestions?.items.find(item => item.label === "Move cursor to end of line")?.description).toBe("F7");
|
|
expect(suggestions?.items.find(item => item.label === "Undo")?.description).toBe("F8");
|
|
});
|
|
|
|
it("passes the typed trigger to undo and leaves text removal to the editor", async () => {
|
|
let undoCalls = 0;
|
|
let undoPrefix = "";
|
|
const provider = createPromptActionAutocompleteProvider({
|
|
commands: [],
|
|
basePath: "/tmp",
|
|
keybindings: AppKeybindingsManager.inMemory(),
|
|
copyCurrentLine: () => {},
|
|
copyPrompt: () => {},
|
|
undo: prefix => {
|
|
undoCalls += 1;
|
|
undoPrefix = prefix;
|
|
},
|
|
moveCursorToMessageEnd: () => {},
|
|
moveCursorToMessageStart: () => {},
|
|
moveCursorToLineStart: () => {},
|
|
moveCursorToLineEnd: () => {},
|
|
});
|
|
|
|
const suggestions = await provider.getSuggestions(["hello #undo"], 0, 11);
|
|
const item = suggestions?.items.find(entry => entry.label === "Undo");
|
|
expect(item).toBeDefined();
|
|
if (!item || !suggestions) {
|
|
throw new Error("expected undo suggestion");
|
|
}
|
|
|
|
const result = provider.applyCompletion(["hello #undo"], 0, 11, item, suggestions.prefix);
|
|
expect(result.lines).toEqual(["hello #undo"]);
|
|
expect(result.cursorLine).toBe(0);
|
|
expect(result.cursorCol).toBe(11);
|
|
result.onApplied?.();
|
|
expect(undoCalls).toBe(1);
|
|
expect(undoPrefix).toBe("#undo");
|
|
});
|
|
|
|
it("falls back to normal typing for literal hashtags with no matching action", async () => {
|
|
const provider = createPromptActionAutocompleteProvider({
|
|
commands: [],
|
|
basePath: "/tmp",
|
|
keybindings: AppKeybindingsManager.inMemory(),
|
|
copyCurrentLine: () => {},
|
|
copyPrompt: () => {},
|
|
undo: () => {},
|
|
moveCursorToMessageEnd: () => {},
|
|
moveCursorToMessageStart: () => {},
|
|
moveCursorToLineStart: () => {},
|
|
moveCursorToLineEnd: () => {},
|
|
});
|
|
|
|
const suggestions = await provider.getSuggestions(["release #v1"], 0, 11);
|
|
expect(suggestions).toBeNull();
|
|
});
|
|
|
|
it("delegates trySyncSlashCompletion to CombinedAutocompleteProvider", () => {
|
|
const provider = createPromptActionAutocompleteProvider({
|
|
commands: [{ name: "model", description: "Switch AI model" }],
|
|
basePath: "/tmp",
|
|
keybindings: AppKeybindingsManager.inMemory(),
|
|
copyCurrentLine: () => {},
|
|
copyPrompt: () => {},
|
|
undo: () => {},
|
|
moveCursorToMessageEnd: () => {},
|
|
moveCursorToMessageStart: () => {},
|
|
moveCursorToLineStart: () => {},
|
|
moveCursorToLineEnd: () => {},
|
|
});
|
|
|
|
const result = provider.trySyncSlashCompletion("/mo");
|
|
expect(result).not.toBeNull();
|
|
expect(result!.items.map(i => i.value)).toContain("model");
|
|
});
|
|
|
|
it("returns null from trySyncSlashCompletion for non-slash text", () => {
|
|
const provider = createPromptActionAutocompleteProvider({
|
|
commands: [{ name: "model", description: "Switch AI model" }],
|
|
basePath: "/tmp",
|
|
keybindings: AppKeybindingsManager.inMemory(),
|
|
copyCurrentLine: () => {},
|
|
copyPrompt: () => {},
|
|
undo: () => {},
|
|
moveCursorToMessageEnd: () => {},
|
|
moveCursorToMessageStart: () => {},
|
|
moveCursorToLineStart: () => {},
|
|
moveCursorToLineEnd: () => {},
|
|
});
|
|
|
|
expect(provider.trySyncSlashCompletion("hello")).toBeNull();
|
|
});
|
|
});
|