import { type TUI, visibleWidth } from "@earendil-works/pi-tui"; import { afterEach, describe, expect, it, vi } from "vitest"; import { KeybindingsManager } from "../src/core/keybindings.ts"; import { CustomEditor } from "../src/modes/interactive/components/custom-editor.ts"; import { IdleStatus, RetryStatusIndicator, WorkingStatusIndicator, } from "../src/modes/interactive/components/status-indicator.ts"; import { getEditorTheme, initTheme, theme } from "../src/modes/interactive/theme/theme.ts"; import { stripAnsi } from "../src/utils/ansi.ts"; describe("status indicators", () => { afterEach(() => { vi.useRealTimers(); }); it("keeps idle status at the same height as standalone status indicators", () => { const idleStatus = new IdleStatus(); const lines = idleStatus.render(20); expect(lines).toHaveLength(2); expect(lines).toEqual([" ".repeat(20), " ".repeat(20)]); }); it("keeps the top border unchanged unless the editor opts in", () => { initTheme("dark"); const tui = { requestRender: vi.fn(), terminal: { rows: 10 }, } as unknown as TUI; const editor = new CustomEditor(tui, getEditorTheme(), KeybindingsManager.create()); const indicator = new WorkingStatusIndicator(tui, "Working"); editor.setWorkingStatusIndicator(indicator); expect(stripAnsi(editor.render(20)[0]!)).toBe("─".repeat(20)); const standaloneLine = indicator.render(20)[1]!; expect(standaloneLine).toContain(theme.getFgAnsi("accent")); expect(standaloneLine).toContain(theme.getFgAnsi("muted")); indicator.dispose(); }); it("embeds the working indicator when the editor opts in", () => { initTheme("dark"); const tui = { requestRender: vi.fn(), terminal: { rows: 10 }, } as unknown as TUI; const editor = new CustomEditor(tui, getEditorTheme(), KeybindingsManager.create(), { embedWorkingStatus: true, }); expect(editor.embedWorkingStatus).toBe(true); editor.borderColor = theme.getThinkingBorderColor("high"); const indicator = new WorkingStatusIndicator(tui, "Working", undefined, (text) => editor.borderColor(text)); editor.setWorkingStatusIndicator(indicator); const topBorder = editor.render(20)[0]!; expect(stripAnsi(topBorder)).toBe("── ⠋ Working ───────"); expect(visibleWidth(topBorder)).toBe(20); expect(topBorder.split(theme.getFgAnsi("thinkingHigh"))).toHaveLength(5); indicator.dispose(); }); it("disposes retry countdown updates", () => { initTheme("dark"); vi.useFakeTimers(); const requestRender = vi.fn(); const tui = { requestRender } as unknown as TUI; const indicator = new RetryStatusIndicator(tui, 1, 3, 1000); const callsBeforeDispose = requestRender.mock.calls.length; indicator.dispose(); vi.advanceTimersByTime(2000); expect(requestRender).toHaveBeenCalledTimes(callsBeforeDispose); }); });