repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/footer/__tests__/harness.test.ts
Rawimport path from "node:path";
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
import { visibleWidth } from "@earendil-works/pi-tui";
import { afterEach, describe, expect, it, vi } from "vitest";
import { createTestSession, type TestSession } from "../../../test/harness";
import footerExtension, { __test } from "../index.ts";
describe("footer pi-test-harness", () => {
let t: TestSession | undefined;
afterEach(() => {
vi.restoreAllMocks();
t?.dispose();
t = undefined;
});
async function mountTui(session: TestSession): Promise<void> {
const runner = session.session.extensionRunner;
runner.setUIContext(runner.getUIContext(), "tui");
await runner.emit({ type: "session_start", reason: "startup" });
}
it("loads in real Pi extension runtime", async () => {
const extensionPath = path.resolve(import.meta.dirname, "../index.ts");
t = await createTestSession({ extensions: [extensionPath] });
expect(t.session.extensionRunner.extensions).toHaveLength(1);
expect(t.session.extensionRunner.extensions[0].path).toBe(extensionPath);
});
it("acknowledges and retains pre-mount updates without polling", async () => {
let accepted = 0;
let offer: ((id: string) => void) | undefined;
const producer = (pi: ExtensionAPI) => {
offer = (id) =>
pi.events.emit("footer:segment", {
id,
text: "ready",
zone: "llm",
accept: () => accepted++,
});
offer("early");
};
const setTimeoutSpy = vi.spyOn(globalThis, "setTimeout");
t = await createTestSession({
extensionFactories: [footerExtension, producer],
});
expect(accepted).toBe(1);
expect(t.events.uiCallsFor("setFooter")).toHaveLength(0);
const runner = t.session.extensionRunner;
runner.setUIContext(runner.getUIContext(), "rpc");
await runner.emit({ type: "session_start", reason: "startup" });
expect(t.events.uiCallsFor("setFooter")).toHaveLength(0);
offer?.("rpc");
expect(accepted).toBe(1);
await mountTui(t);
expect(accepted).toBe(1);
expect(setTimeoutSpy.mock.calls.some(([, delay]) => delay === 50)).toBe(
false,
);
setTimeoutSpy.mockRestore();
const factory = t.events.uiCallsFor("setFooter").at(-1)?.args[0] as
| ((
tui: { requestRender(): void },
theme: { fg(style: string, text: string): string },
data: {
onBranchChange(callback: () => void): () => void;
getExtensionStatuses(): ReadonlyMap<string, string>;
},
) => { render(width: number): string[] })
| undefined;
const component = factory?.(
{ requestRender() {} },
{ fg: (_style, text) => text },
{
onBranchChange: () => () => {},
getExtensionStatuses: () => new Map(),
},
);
expect(component?.render(80).join("\n")).toContain("ready");
});
it("isolates acknowledgements from a nested non-TUI runtime", async () => {
let parentAccepted = 0;
const producer = (pi: ExtensionAPI) => {
pi.events.emit("footer:segment", {
id: "parent",
text: "ready",
zone: "llm",
accept: () => parentAccepted++,
});
};
t = await createTestSession({
extensionFactories: [footerExtension, producer],
});
await mountTui(t);
expect(parentAccepted).toBe(1);
const nested = await createTestSession({
extensionFactories: [footerExtension],
});
try {
const runner = nested.session.extensionRunner;
runner.setUIContext(runner.getUIContext(), "rpc");
await runner.emit({ type: "session_start", reason: "startup" });
await runner.emit({ type: "session_shutdown", reason: "quit" });
expect(parentAccepted).toBe(1);
} finally {
nested.dispose();
}
});
it("keeps valid segments when a producer emits malformed data", async () => {
let emitSegment: ((segment: unknown) => void) | undefined;
const producer = (pi: ExtensionAPI) => {
emitSegment = (segment) => pi.events.emit("footer:segment", segment);
};
t = await createTestSession({
extensionFactories: [footerExtension, producer],
});
await mountTui(t);
const factory = t.events.uiCallsFor("setFooter").at(-1)?.args[0] as
| ((
tui: { requestRender(): void },
theme: { fg(style: string, text: string): string },
data: {
onBranchChange(callback: () => void): () => void;
getExtensionStatuses(): ReadonlyMap<string, string>;
},
) => { render(width: number): string[] })
| undefined;
const component = factory?.(
{ requestRender() {} },
{
fg: (style, text) => {
if (style === "not-a-theme-color") throw new Error("bad color");
return text;
},
},
{
onBranchChange: () => () => {},
getExtensionStatuses: () => new Map(),
},
);
let accepted = 0;
emitSegment?.({
id: "good",
text: "healthy",
zone: "llm",
accept: () => accepted++,
});
emitSegment?.({
id: "good",
text: 42,
zone: "llm",
accept: () => accepted++,
});
expect(accepted).toBe(1);
emitSegment?.({
id: "good",
text: "poison",
zone: "llm",
color: "not-a-theme-color",
accept: () => accepted++,
});
expect(accepted).toBe(1);
expect(component?.render(80).join("\n")).toContain("healthy");
expect(component?.render(80).join("\n")).not.toContain("poison");
});
it("keeps later LLM components visible as earlier values grow", async () => {
let emitSegment: ((segment: unknown) => void) | undefined;
const producer = (pi: ExtensionAPI) => {
emitSegment = (segment) => pi.events.emit("footer:segment", segment);
};
t = await createTestSession({
extensionFactories: [footerExtension, producer],
});
await mountTui(t);
const factory = t.events.uiCallsFor("setFooter").at(-1)?.args[0] as
| ((
tui: { requestRender(): void },
theme: { fg(style: string, text: string): string },
data: {
onBranchChange(callback: () => void): () => void;
getExtensionStatuses(): ReadonlyMap<string, string>;
},
) => {
render(width: number): string[];
dispose(): void;
})
| undefined;
expect(factory).toBeTypeOf("function");
const component = factory?.(
{ requestRender() {} },
{ fg: (_style, text) => text },
{
onBranchChange: () => () => {},
getExtensionStatuses: () => new Map(),
},
);
expect(component).toBeDefined();
emitSegment?.({ id: "model", text: "sol:medium", zone: "llm", order: 0 });
emitSegment?.({ id: "tokens", text: "↑0", zone: "llm", order: 2 });
emitSegment?.({ id: "tps", text: "50.0/s", zone: "llm", order: 4 });
const initial = component?.render(32) ?? [];
expect(initial).toHaveLength(2);
expect(initial.join("\n")).toContain("50.0/s");
emitSegment?.({
id: "tokens",
text: "↑12k ↓3.4k",
zone: "llm",
order: 2,
});
const grown = component?.render(32) ?? [];
expect(grown).toHaveLength(3);
expect(grown.join("\n")).toContain("50.0/s");
expect(grown.every((line) => visibleWidth(line) <= 32)).toBe(true);
emitSegment?.({ id: "tokens", text: "↑0", zone: "llm", order: 2 });
const shrunk = component?.render(32) ?? [];
expect(shrunk).toHaveLength(2);
expect(shrunk.join("\n")).toContain("50.0/s");
emitSegment?.({
id: "provider",
text: "provider",
zone: "llm",
group: "model-route",
order: -0.5,
});
emitSegment?.({
id: "model",
text: "sol:medium",
zone: "llm",
group: "model-route",
gap: 2,
order: 0,
});
expect(component?.render(80).join("\n")).toContain("provider sol:medium");
component?.dispose();
});
it.each([
[true, "tui", true],
[true, "rpc", false],
[false, "json", false],
[false, "print", false],
[false, "tui", false],
] as const)(
"requires UI and TUI mode when hasUI=%s and mode=%s",
(hasUI, mode, expected) => {
expect(__test.canRenderFooter({ hasUI, mode })).toBe(expected);
},
);
it("keeps equal-priority order stable after removal and reinsertion", () => {
const quota = {
id: "quota:anthropic",
text: "quota",
zone: "llm",
order: 4,
} as const;
const tps = {
id: "tps",
text: "tps",
zone: "llm",
order: 4,
} as const;
const segments = new Map([
[tps.id, tps],
[quota.id, quota],
]);
segments.delete(tps.id);
segments.set(tps.id, tps);
expect([...segments.values()].sort(__test.compareSegments)).toEqual([
quota,
tps,
]);
});
it("renders nothing and schedules no retries when hidden", async () => {
let emitSegment: ((segment: unknown) => void) | undefined;
const producer = (pi: ExtensionAPI) => {
emitSegment = (segment) => pi.events.emit("footer:segment", segment);
};
t = await createTestSession({
extensionFactories: [footerExtension, producer],
});
await mountTui(t);
const runner = t.session.extensionRunner;
await runner.extensions[0].commands
.get("footer")
.handler("", runner.createContext());
const factory = t.events.uiCallsFor("setFooter").at(-1)?.args[0] as
| ((tui: { requestRender(force?: boolean): void }) => {
render(width: number): string[];
})
| undefined;
expect(factory?.({ requestRender() {} }).render(80)).toEqual([]);
const setTimeoutSpy = vi.spyOn(globalThis, "setTimeout");
emitSegment?.({ id: "tps", text: "50/s", zone: "llm" });
expect(setTimeoutSpy).not.toHaveBeenCalled();
setTimeoutSpy.mockRestore();
});
it("right-aligns statuses beside the workspace row", () => {
expect(__test.alignLeftAndRight("workspace", "busy", 16)).toBe(
"workspace busy",
);
});
it("preserves the workspace row when statuses exceed the remaining space", () => {
const line = __test.alignLeftAndRight("workspace", "very long status", 12);
expect(visibleWidth(line)).toBe(12);
expect(line.startsWith("workspace ")).toBe(true);
});
it("uses the first row when its first group is oversized", () => {
const [first, second] = __test.flowLines(
["oversized-model-route", "tokens"],
" | ",
8,
);
expect(first).not.toBe("");
expect(second).toBe("tokens");
});
it("keeps adjacent members of a named segment group atomic", () => {
const model = { id: "model", text: "sol:medium", zone: "llm" } as const;
const fast = {
id: "fast",
text: "",
zone: "llm",
group: "model-route",
} as const;
const angel = {
id: "angel",
text: "astra:max",
zone: "llm",
group: "model-route",
} as const;
const tokens = { id: "tokens", text: "1k", zone: "llm" } as const;
expect(
__test.groupSegments([
{ ...model, group: "model-route" },
fast,
angel,
tokens,
]),
).toEqual([[{ ...model, group: "model-route" }, fast, angel], [tokens]]);
});
});