spb/khaelor Public
KHAELOR — a terminal-native autonomous engineering agent powered by Anthropic.
TypeScript 82.9%
HTML 14.9%
CSS 1.1%
JavaScript 0.7%
1/**2 * KHAELOR3 * File: tests/tui/status.test.ts4 * Description: Status bar width-degradation tests and agent status line tests (real timers, no fabrication).5 *6 * Author: Simon-Pierre Boucher7 * Contact: contact@spboucher.ai8 */910import { describe, expect, it } from "vitest";11import { renderStatusBar } from "../../src/tui/components/status-bar.js";12import type { StatusBarData } from "../../src/tui/components/status-bar.js";13import { renderStatusLine } from "../../src/tui/components/status-line.js";14import { stripAnsi } from "../../src/tui/renderer/ansi.js";15import { resolveTheme } from "../../src/tui/theme.js";1617const mono = resolveTheme({ colorDepth: "mono" });1819const FULL: StatusBarData = {20 branch: "main",21 dirty: { added: 4, removed: 1 },22 model: "claude-sonnet-4-5",23 contextPct: 31,24 costUsd: 0.42,25 processCount: 2,26};2728describe("renderStatusBar degradation", () => {29 it("width ≥ 100: all segments, full model id", () => {30 expect(renderStatusBar(FULL, 120, mono)).toBe(31 " main +4 −1 │ claude-sonnet-4-5 │ context 31% │ $0.42 │ ● 2",32 );33 });3435 it("width 80–99: model shortens to its alias", () => {36 expect(renderStatusBar(FULL, 90, mono)).toBe(37 " main +4 −1 │ sonnet │ context 31% │ $0.42 │ ● 2",38 );39 });4041 it("width 60–79: cost and processes drop whole", () => {42 expect(renderStatusBar(FULL, 70, mono)).toBe(" main +4 −1 │ sonnet │ 31%");43 });4445 it("width < 60: branch and context percent only", () => {46 expect(renderStatusBar(FULL, 50, mono)).toBe(" main │ 31%");47 });4849 it("segments with no data are absent, not zeroed", () => {50 expect(renderStatusBar({ model: "claude-sonnet-4-5" }, 120, mono)).toBe(51 " claude-sonnet-4-5",52 );53 expect(renderStatusBar({}, 120, mono)).toBe(" ");54 });5556 it("processes drop when none are running", () => {57 const bar = renderStatusBar({ ...FULL, processCount: 0 }, 120, mono);58 expect(bar).not.toContain("●");59 });6061 it("context pressure appends the /compact nudge at ≥ 80%", () => {62 const bar = renderStatusBar({ ...FULL, contextPct: 85 }, 120, mono);63 expect(bar).toContain("context 85% · /compact");64 });6566 it("queued steering shows its count", () => {67 const bar = renderStatusBar({ ...FULL, queuedCount: 1 }, 120, mono);68 expect(bar).toContain("⋯ 1 queued");69 });7071 it("never exceeds the terminal width", () => {72 for (const width of [40, 60, 80, 100, 120]) {73 expect(stripAnsi(renderStatusBar(FULL, width, mono)).length).toBeLessThanOrEqual(width);74 }75 });76});7778describe("renderStatusLine", () => {79 it("collapses to nothing when idle", () => {80 expect(renderStatusLine({ kind: "idle", startedAt: 0 }, 5000, mono, 80)).toBeNull();81 });8283 it("shows the state, detail, and a REAL elapsed timer", () => {84 const line = renderStatusLine(85 { kind: "running", detail: "npm test", startedAt: 1000 },86 3300,87 mono,88 80,89 );90 expect(line).toBe(" ● Running · npm test · 2.3s");91 });9293 it("shows extracted progress instead of the timer when present", () => {94 const line = renderStatusLine(95 { kind: "running", detail: "npm test", startedAt: 1000, progress: "41/148" },96 9000,97 mono,98 80,99 );100 expect(line).toBe(" ● Running · npm test · 41/148");101 });102103 it("waits-for-permission state is spelled out", () => {104 const line = renderStatusLine({ kind: "waiting", startedAt: 1000 }, 1400, mono, 80);105 expect(line).toContain("Waiting for permission");106 });107108 it("stopping acknowledgment uses the ◌ glyph (shape carries state)", () => {109 expect(renderStatusLine({ kind: "stopping", startedAt: 0 }, 100, mono, 80)).toBe(110 " ◌ Stopping…",111 );112 });113114 it("pulses the dot between two shades in color, stable glyph in mono", () => {115 const dark = resolveTheme({ colorDepth: "truecolor" });116 const a = renderStatusLine({ kind: "thinking", startedAt: 0 }, 0, dark, 80) as string;117 const b = renderStatusLine({ kind: "thinking", startedAt: 0 }, 250, dark, 80) as string;118 expect(a).not.toBe(b); // different pulse shade119 expect(stripAnsi(a).slice(0, 3)).toBe(stripAnsi(b).slice(0, 3)); // same glyph120 });121});122