Files
2026-08-14 23:41:57 +08:00

165 lines
4.9 KiB
Go

package tui
import (
"strings"
"testing"
"github.com/smallnest/pigo/internal/agentcore"
"github.com/smallnest/pigo/internal/cli/ui"
)
// newTestStatusBar builds a status bar with a known cwd (already ~-abbreviated
// by the caller's intent) so tests do not depend on the real $HOME.
func newTestStatusBar() statusBar {
opts := Options{Model: "claude-opus", ThinkingLevel: agentcore.ThinkingHigh}
s := newStatusBar(DefaultTheme(), opts, "/tmp/project")
s.cwd = "~/project"
return s
}
func TestStatusBarRendersAllFields(t *testing.T) {
s := newTestStatusBar()
s.SetGit(gitInfoMsg{branch: "master", dirty: 3, ahead: 4, ok: true})
s.SetTelemetry(telemetryEventView{util: 0.42, window: 200000})
s.SetTask("running: Read")
const width = 200
out := s.Render(width)
for _, want := range []string{
"pigo", // app badge
"claude-opus", // model
"high", // thinking level
"~/project", // cwd
"master", // git branch
glyphDirty + "3", // dirty marker
glyphAhead + "4", // ahead marker
"42%", // context usage
"running: Read", // task
} {
if !strings.Contains(out, want) {
t.Errorf("render missing %q; got %q", want, out)
}
}
if w := ui.Width(out); w > width {
t.Errorf("render width %d exceeds terminal width %d", w, width)
}
}
func TestStatusBarHidesGitWhenNotRepo(t *testing.T) {
s := newTestStatusBar()
s.SetGit(gitInfoMsg{ok: false})
out := s.Render(120)
if strings.Contains(out, "master") || strings.Contains(out, "*") || strings.Contains(out, "+") {
t.Errorf("git segment should be hidden when ok=false; got %q", out)
}
}
func TestStatusBarHidesContextWhenUnknown(t *testing.T) {
s := newTestStatusBar()
// No telemetry set (window 0) → context segment hidden.
s.SetTelemetry(telemetryEventView{util: 0.5, window: 0})
out := s.Render(120)
if strings.Contains(out, glyphCtx) {
t.Errorf("context segment should be hidden when window unknown; got %q", out)
}
}
func TestStatusBarTruncationKeepsPriorityFields(t *testing.T) {
s := newTestStatusBar()
s.SetGit(gitInfoMsg{branch: "master", dirty: 3, ahead: 4, ok: true})
s.SetTelemetry(telemetryEventView{util: 0.42, window: 200000})
s.SetTask("TASK")
// Narrow width: only the highest-priority fields (task > model > token)
// should survive; cwd and git should drop first.
const width = 24
out := s.Render(width)
if w := ui.Width(out); w > width {
t.Fatalf("truncated render width %d exceeds %d: %q", w, width, out)
}
if !strings.Contains(out, "TASK") {
t.Errorf("highest-priority task field dropped under truncation: %q", out)
}
// cwd (lowest priority) must be gone before task.
if strings.Contains(out, "~/project") {
t.Errorf("lowest-priority cwd should drop first under truncation: %q", out)
}
}
func TestStatusBarVeryNarrowNeverOverflows(t *testing.T) {
s := newTestStatusBar()
s.SetTask("a-fairly-long-task-description-that-cannot-fit")
for _, width := range []int{1, 2, 3, 5, 8} {
out := s.Render(width)
if w := ui.Width(out); w > width {
t.Errorf("width %d: render width %d overflows: %q", width, w, out)
}
}
}
func TestStatusBarZeroWidthEmpty(t *testing.T) {
s := newTestStatusBar()
if out := s.Render(0); out != "" {
t.Errorf("zero width should render empty, got %q", out)
}
}
// TestStatusBarContextTokenCount checks the context segment shows a
// comma-grouped token count with the percentage once telemetry reports tokens.
func TestStatusBarContextTokenCount(t *testing.T) {
s := newTestStatusBar()
s.SetTelemetry(telemetryEventView{util: 0.46, window: 200000, tokens: 90866})
out := s.Render(200)
for _, want := range []string{glyphCtx, "90,866", "46%"} {
if !strings.Contains(out, want) {
t.Errorf("render missing %q; got %q", want, out)
}
}
}
func TestHumanizeInt(t *testing.T) {
cases := map[int]string{0: "0", 90866: "90,866", 1000: "1,000", 999: "999", 1234567: "1,234,567"}
for in, want := range cases {
if got := humanizeInt(in); got != want {
t.Errorf("humanizeInt(%d) = %q, want %q", in, got, want)
}
}
}
func TestAbbreviateHome(t *testing.T) {
home := homeDir()
if home == "" {
t.Skip("no home dir available")
}
if got := abbreviateHome(home); got != "~" {
t.Errorf("abbreviateHome(home) = %q, want ~", got)
}
if got := abbreviateHome(home + "/foo/bar"); got != "~/foo/bar" {
t.Errorf("abbreviateHome(home/foo/bar) = %q, want ~/foo/bar", got)
}
if got := abbreviateHome("/etc/passwd"); got != "/etc/passwd" {
t.Errorf("abbreviateHome(/etc/passwd) = %q, want unchanged", got)
}
}
// TestStatusBarGitTextFormatting checks the "*N +N" markers appear only when
// non-zero.
func TestStatusBarGitTextFormatting(t *testing.T) {
s := newTestStatusBar()
s.SetGit(gitInfoMsg{branch: "main", ok: true})
out := s.Render(120)
if strings.Contains(out, "*") || strings.Contains(out, "+") {
t.Errorf("clean tree should show no *N/+N markers: %q", out)
}
if !strings.Contains(out, "main") {
t.Errorf("branch name missing: %q", out)
}
}