165 lines
4.9 KiB
Go
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)
|
|
}
|
|
}
|