Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: d981de1229ef899957bbe968bc8dcda02a21f477
127 lines
4.4 KiB
TypeScript
127 lines
4.4 KiB
TypeScript
import { setKeybindings } from "@earendil-works/pi-tui";
|
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
import { KeybindingsManager } from "../src/core/keybindings.ts";
|
|
import { ScopedModelsSelectorComponent } from "../src/modes/interactive/components/scoped-models-selector.ts";
|
|
import { initTheme } from "../src/modes/interactive/theme/theme.ts";
|
|
import { stripAnsi } from "../src/utils/ansi.ts";
|
|
import { createHarness, type Harness } from "./suite/harness.ts";
|
|
|
|
interface ModelState {
|
|
id: string;
|
|
name: string;
|
|
enabled: boolean;
|
|
}
|
|
|
|
function getMarkerStates(selector: ScopedModelsSelectorComponent, models: ModelState[]): boolean[] {
|
|
const lines = stripAnsi(selector.render(120).join("\n")).split("\n");
|
|
return models.map((model) => {
|
|
const line = lines.find((candidate) => candidate.includes(`${model.id} [`));
|
|
if (!line) throw new Error(`Expected rendered row for ${model.id}`);
|
|
return line.slice(2).startsWith("✓ ");
|
|
});
|
|
}
|
|
|
|
describe("scoped models selector", () => {
|
|
let harness: Harness | undefined;
|
|
|
|
beforeAll(() => {
|
|
initTheme("dark");
|
|
});
|
|
|
|
beforeEach(() => {
|
|
setKeybindings(new KeybindingsManager());
|
|
});
|
|
|
|
afterEach(() => {
|
|
harness?.cleanup();
|
|
harness = undefined;
|
|
});
|
|
|
|
async function createSelector(models: ModelState[]) {
|
|
harness = await createHarness({ models });
|
|
const provider = harness.models[0].provider;
|
|
const selector = new ScopedModelsSelectorComponent(
|
|
{
|
|
allModels: [...harness.models],
|
|
enabledModelIds: models.filter((model) => model.enabled).map((model) => `${provider}/${model.id}`),
|
|
},
|
|
{
|
|
onChange: (enabledModelIds) => {
|
|
for (const model of models) {
|
|
model.enabled = enabledModelIds === null || enabledModelIds.includes(`${provider}/${model.id}`);
|
|
}
|
|
},
|
|
onPersist: () => {},
|
|
onCancel: () => {},
|
|
},
|
|
);
|
|
return selector;
|
|
}
|
|
|
|
it("marks every model after enabling all", async () => {
|
|
const models = [
|
|
{ id: "model-a", name: "Model A", enabled: true },
|
|
{ id: "model-b", name: "Model B", enabled: false },
|
|
{ id: "model-c", name: "Model C", enabled: false },
|
|
];
|
|
const selector = await createSelector(models);
|
|
|
|
selector.handleInput("\x01");
|
|
|
|
expect(models.map((model) => model.enabled)).toEqual([true, true, true]);
|
|
expect(getMarkerStates(selector, models)).toEqual([true, true, true]);
|
|
expect(stripAnsi(selector.render(120).join("\n"))).toContain("all enabled");
|
|
});
|
|
|
|
it("disables only the selected model after enabling all", async () => {
|
|
const models = [
|
|
{ id: "model-a", name: "Model A", enabled: true },
|
|
{ id: "model-b", name: "Model B", enabled: false },
|
|
{ id: "model-c", name: "Model C", enabled: false },
|
|
];
|
|
const selector = await createSelector(models);
|
|
|
|
selector.handleInput("\x01");
|
|
selector.handleInput("\r");
|
|
|
|
expect(models.map((model) => model.enabled)).toEqual([false, true, true]);
|
|
expect(getMarkerStates(selector, models)).toEqual([false, true, true]);
|
|
});
|
|
|
|
it("enables only the selected model after clearing all", async () => {
|
|
const models = [
|
|
{ id: "model-a", name: "Model A", enabled: true },
|
|
{ id: "model-b", name: "Model B", enabled: true },
|
|
{ id: "model-c", name: "Model C", enabled: true },
|
|
];
|
|
const selector = await createSelector(models);
|
|
|
|
selector.handleInput("\x18");
|
|
expect(models.map((model) => model.enabled)).toEqual([false, false, false]);
|
|
expect(getMarkerStates(selector, models)).toEqual([false, false, false]);
|
|
|
|
selector.handleInput("\r");
|
|
expect(models.map((model) => model.enabled)).toEqual([true, false, false]);
|
|
expect(getMarkerStates(selector, models)).toEqual([true, false, false]);
|
|
});
|
|
|
|
it("restores the all-enabled state after re-enabling the last disabled model", async () => {
|
|
const models = [
|
|
{ id: "model-a", name: "Model A", enabled: true },
|
|
{ id: "model-b", name: "Model B", enabled: false },
|
|
{ id: "model-c", name: "Model C", enabled: false },
|
|
];
|
|
const selector = await createSelector(models);
|
|
|
|
selector.handleInput("\x01"); // enable all -> null
|
|
selector.handleInput("\r"); // disable model-a; enabled models re-sort first: [b, c, a]
|
|
selector.handleInput("\x1b[B");
|
|
selector.handleInput("\x1b[B"); // move selection back to model-a
|
|
selector.handleInput("\r"); // re-enable model-a
|
|
|
|
expect(models.map((model) => model.enabled)).toEqual([true, true, true]);
|
|
expect(getMarkerStates(selector, models)).toEqual([true, true, true]);
|
|
expect(stripAnsi(selector.render(120).join("\n"))).toContain("all enabled");
|
|
});
|
|
});
|