edcc8ab30f
- Added unit tests for renderSegmentTrack verifying per-segment coloring, active chip fill formatting, and active-index movement. - Added tests for theme.getContrastFgAnsi to ensure selected foreground contrast remains clearly readable against RGB fills. - Removed the OAuth selector keybinding test and its local auth-storage scaffolding, and documented the new model-tier/role-cycle chip-track behavior in the changelog.
174 lines
5.1 KiB
TypeScript
174 lines
5.1 KiB
TypeScript
import { afterEach, beforeAll, describe, expect, it } from "bun:test";
|
|
import * as fs from "node:fs/promises";
|
|
import * as os from "node:os";
|
|
import * as path from "node:path";
|
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
import { KeybindingsManager } from "@oh-my-pi/pi-coding-agent/config/keybindings";
|
|
import { ExtensionList } from "@oh-my-pi/pi-coding-agent/modes/components/extensions/extension-list";
|
|
import type { Extension } from "@oh-my-pi/pi-coding-agent/modes/components/extensions/types";
|
|
import { HistorySearchComponent } from "@oh-my-pi/pi-coding-agent/modes/components/history-search";
|
|
import { SessionSelectorComponent } from "@oh-my-pi/pi-coding-agent/modes/components/session-selector";
|
|
import { TreeSelectorComponent } from "@oh-my-pi/pi-coding-agent/modes/components/tree-selector";
|
|
import { UserMessageSelectorComponent } from "@oh-my-pi/pi-coding-agent/modes/components/user-message-selector";
|
|
import { initTheme } from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
|
|
import { HistoryStorage } from "@oh-my-pi/pi-coding-agent/session/history-storage";
|
|
import type { SessionInfo, SessionTreeNode } from "@oh-my-pi/pi-coding-agent/session/session-manager";
|
|
import { setKeybindings } from "@oh-my-pi/pi-tui";
|
|
|
|
const CTRL_N = "\x0e";
|
|
const CTRL_P = "\x10";
|
|
const TEST_KEYBINDINGS = KeybindingsManager.inMemory({
|
|
"tui.select.up": "ctrl+p",
|
|
"tui.select.down": "ctrl+n",
|
|
});
|
|
|
|
const tempDirs: string[] = [];
|
|
|
|
beforeAll(() => {
|
|
initTheme();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
setKeybindings(KeybindingsManager.inMemory());
|
|
HistoryStorage.resetInstance();
|
|
await Promise.all(tempDirs.splice(0).map(dir => fs.rm(dir, { recursive: true, force: true })));
|
|
});
|
|
|
|
function createSession(id: string, title: string): SessionInfo {
|
|
return {
|
|
path: `/tmp/${id}.jsonl`,
|
|
id,
|
|
cwd: "/tmp",
|
|
title,
|
|
created: new Date("2024-01-01T00:00:00Z"),
|
|
modified: new Date("2024-01-02T00:00:00Z"),
|
|
messageCount: 1,
|
|
size: 0,
|
|
firstMessage: `${title} first message`,
|
|
allMessagesText: `${title} first message`,
|
|
};
|
|
}
|
|
|
|
function createMessageNode(id: string, parentId: string | null, content: string): SessionTreeNode {
|
|
const message: AgentMessage = { role: "user", content, timestamp: 1 };
|
|
return {
|
|
entry: {
|
|
type: "message",
|
|
id,
|
|
parentId,
|
|
timestamp: "2024-01-01T00:00:00Z",
|
|
message,
|
|
},
|
|
children: [],
|
|
};
|
|
}
|
|
|
|
function createExtension(id: string, displayName: string): Extension {
|
|
return {
|
|
id,
|
|
kind: "tool",
|
|
name: id,
|
|
displayName,
|
|
description: displayName,
|
|
path: `/tmp/${id}.md`,
|
|
source: {
|
|
provider: "test-provider",
|
|
providerName: "Test Provider",
|
|
level: "project",
|
|
},
|
|
state: "active",
|
|
raw: {},
|
|
};
|
|
}
|
|
|
|
async function createHistoryStorage(prompts: string[]): Promise<HistoryStorage> {
|
|
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "omp-history-nav-"));
|
|
tempDirs.push(dir);
|
|
HistoryStorage.resetInstance();
|
|
const storage = HistoryStorage.open(path.join(dir, "history.db"));
|
|
for (const prompt of prompts) {
|
|
await storage.add(prompt);
|
|
}
|
|
return storage;
|
|
}
|
|
|
|
describe("selector navigation keybindings", () => {
|
|
it("uses tui.select.down in the session selector", () => {
|
|
setKeybindings(TEST_KEYBINDINGS);
|
|
const selected: string[] = [];
|
|
const selector = new SessionSelectorComponent(
|
|
[createSession("session-a", "Alpha"), createSession("session-b", "Beta")],
|
|
path => selected.push(path),
|
|
() => {},
|
|
() => {},
|
|
);
|
|
|
|
selector.handleInput(CTRL_N);
|
|
selector.handleInput("\n");
|
|
|
|
expect(selected).toEqual(["/tmp/session-b.jsonl"]);
|
|
});
|
|
|
|
it("uses tui.select.down in the session tree", () => {
|
|
setKeybindings(TEST_KEYBINDINGS);
|
|
const root = createMessageNode("root", null, "Root");
|
|
const child = createMessageNode("child", "root", "Child");
|
|
root.children.push(child);
|
|
const selected: string[] = [];
|
|
const selector = new TreeSelectorComponent(
|
|
[root],
|
|
"root",
|
|
40,
|
|
id => selected.push(id),
|
|
() => {},
|
|
);
|
|
selector.handleInput(CTRL_N);
|
|
selector.handleInput("\n");
|
|
|
|
expect(selected).toEqual(["child"]);
|
|
});
|
|
|
|
it("uses tui.select.up in the user message selector", () => {
|
|
setKeybindings(TEST_KEYBINDINGS);
|
|
const selected: string[] = [];
|
|
const selector = new UserMessageSelectorComponent(
|
|
[
|
|
{ id: "first", text: "First" },
|
|
{ id: "second", text: "Second" },
|
|
{ id: "third", text: "Third" },
|
|
],
|
|
id => selected.push(id),
|
|
() => {},
|
|
);
|
|
|
|
selector.getMessageList().handleInput(CTRL_P);
|
|
selector.getMessageList().handleInput("\n");
|
|
|
|
expect(selected).toEqual(["second"]);
|
|
});
|
|
|
|
it("uses tui.select.down in the extension list", () => {
|
|
setKeybindings(TEST_KEYBINDINGS);
|
|
const list = new ExtensionList([createExtension("tool-a", "Tool A"), createExtension("tool-b", "Tool B")]);
|
|
|
|
list.handleInput(CTRL_N);
|
|
|
|
expect(list.getSelectedExtension()?.id).toBe("tool-a");
|
|
});
|
|
|
|
it("uses tui.select.down in history search", async () => {
|
|
setKeybindings(TEST_KEYBINDINGS);
|
|
const selected: string[] = [];
|
|
const storage = await createHistoryStorage(["old prompt", "middle prompt", "new prompt"]);
|
|
const selector = new HistorySearchComponent(
|
|
storage,
|
|
prompt => selected.push(prompt),
|
|
() => {},
|
|
);
|
|
selector.handleInput(CTRL_N);
|
|
selector.handleInput("\n");
|
|
|
|
expect(selected).toEqual(["middle prompt"]);
|
|
});
|
|
});
|