mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 22:03:45 +00:00
136 lines
4.5 KiB
TypeScript
136 lines
4.5 KiB
TypeScript
import { Container } from "@mariozechner/pi-tui";
|
|
import { beforeAll, describe, expect, test, vi } from "vitest";
|
|
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.js";
|
|
import { initTheme } from "../src/modes/interactive/theme/theme.js";
|
|
|
|
function renderLastLine(container: Container, width = 120): string {
|
|
const last = container.children[container.children.length - 1];
|
|
if (!last) return "";
|
|
return last.render(width).join("\n");
|
|
}
|
|
|
|
function renderAll(container: Container, width = 120): string {
|
|
return container.children.flatMap((child) => child.render(width)).join("\n");
|
|
}
|
|
|
|
describe("InteractiveMode.showStatus", () => {
|
|
beforeAll(() => {
|
|
// showStatus uses the global theme instance
|
|
initTheme("dark");
|
|
});
|
|
|
|
test("coalesces immediately-sequential status messages", () => {
|
|
const fakeThis: any = {
|
|
chatContainer: new Container(),
|
|
ui: { requestRender: vi.fn() },
|
|
lastStatusSpacer: undefined,
|
|
lastStatusText: undefined,
|
|
};
|
|
|
|
(InteractiveMode as any).prototype.showStatus.call(fakeThis, "STATUS_ONE");
|
|
expect(fakeThis.chatContainer.children).toHaveLength(2);
|
|
expect(renderLastLine(fakeThis.chatContainer)).toContain("STATUS_ONE");
|
|
|
|
(InteractiveMode as any).prototype.showStatus.call(fakeThis, "STATUS_TWO");
|
|
// second status updates the previous line instead of appending
|
|
expect(fakeThis.chatContainer.children).toHaveLength(2);
|
|
expect(renderLastLine(fakeThis.chatContainer)).toContain("STATUS_TWO");
|
|
expect(renderLastLine(fakeThis.chatContainer)).not.toContain("STATUS_ONE");
|
|
});
|
|
|
|
test("appends a new status line if something else was added in between", () => {
|
|
const fakeThis: any = {
|
|
chatContainer: new Container(),
|
|
ui: { requestRender: vi.fn() },
|
|
lastStatusSpacer: undefined,
|
|
lastStatusText: undefined,
|
|
};
|
|
|
|
(InteractiveMode as any).prototype.showStatus.call(fakeThis, "STATUS_ONE");
|
|
expect(fakeThis.chatContainer.children).toHaveLength(2);
|
|
|
|
// Something else gets added to the chat in between status updates
|
|
fakeThis.chatContainer.addChild({ render: () => ["OTHER"], invalidate: () => {} });
|
|
expect(fakeThis.chatContainer.children).toHaveLength(3);
|
|
|
|
(InteractiveMode as any).prototype.showStatus.call(fakeThis, "STATUS_TWO");
|
|
// adds spacer + text
|
|
expect(fakeThis.chatContainer.children).toHaveLength(5);
|
|
expect(renderLastLine(fakeThis.chatContainer)).toContain("STATUS_TWO");
|
|
});
|
|
});
|
|
|
|
describe("InteractiveMode.showLoadedResources", () => {
|
|
beforeAll(() => {
|
|
initTheme("dark");
|
|
});
|
|
|
|
function createShowLoadedResourcesThis(options: {
|
|
quietStartup: boolean;
|
|
verbose?: boolean;
|
|
skills?: Array<{ filePath: string }>;
|
|
skillDiagnostics?: Array<{ type: "warning" | "error" | "collision"; message: string }>;
|
|
}) {
|
|
const fakeThis: any = {
|
|
options: { verbose: options.verbose ?? false },
|
|
chatContainer: new Container(),
|
|
settingsManager: {
|
|
getQuietStartup: () => options.quietStartup,
|
|
},
|
|
session: {
|
|
promptTemplates: [],
|
|
extensionRunner: undefined,
|
|
resourceLoader: {
|
|
getPathMetadata: () => new Map(),
|
|
getAgentsFiles: () => ({ agentsFiles: [] }),
|
|
getSkills: () => ({
|
|
skills: options.skills ?? [],
|
|
diagnostics: options.skillDiagnostics ?? [],
|
|
}),
|
|
getPrompts: () => ({ prompts: [], diagnostics: [] }),
|
|
getExtensions: () => ({ errors: [] }),
|
|
getThemes: () => ({ themes: [], diagnostics: [] }),
|
|
},
|
|
},
|
|
formatDisplayPath: (p: string) => p,
|
|
buildScopeGroups: () => [],
|
|
formatScopeGroups: () => "resource-list",
|
|
getShortPath: (p: string) => p,
|
|
formatDiagnostics: () => "diagnostics",
|
|
};
|
|
|
|
return fakeThis;
|
|
}
|
|
|
|
test("does not show verbose listing on quiet startup during reload", () => {
|
|
const fakeThis = createShowLoadedResourcesThis({
|
|
quietStartup: true,
|
|
skills: [{ filePath: "/tmp/skill/SKILL.md" }],
|
|
});
|
|
|
|
(InteractiveMode as any).prototype.showLoadedResources.call(fakeThis, {
|
|
extensionPaths: ["/tmp/ext/index.ts"],
|
|
force: false,
|
|
showDiagnosticsWhenQuiet: true,
|
|
});
|
|
|
|
expect(fakeThis.chatContainer.children).toHaveLength(0);
|
|
});
|
|
|
|
test("still shows diagnostics on quiet startup when requested", () => {
|
|
const fakeThis = createShowLoadedResourcesThis({
|
|
quietStartup: true,
|
|
skills: [{ filePath: "/tmp/skill/SKILL.md" }],
|
|
skillDiagnostics: [{ type: "warning", message: "duplicate skill name" }],
|
|
});
|
|
|
|
(InteractiveMode as any).prototype.showLoadedResources.call(fakeThis, {
|
|
force: false,
|
|
showDiagnosticsWhenQuiet: true,
|
|
});
|
|
|
|
const output = renderAll(fakeThis.chatContainer);
|
|
expect(output).toContain("[Skill conflicts]");
|
|
expect(output).not.toContain("[Skills]");
|
|
});
|
|
});
|