Files
beads/internal/ui/styles_test.go
Steve Yegge b45e68c5ce Add BD_AGENT_MODE for ultra-compact output
Adds agent-optimized output mode for `bd list` triggered by:
- BD_AGENT_MODE=1 environment variable (explicit)
- CLAUDE_CODE environment variable (auto-detect)

Agent mode provides:
- Ultra-compact format: just "ID: Title" per line
- Lower default limit (20 vs 50) for context efficiency
- No colors, no emojis, no pager
- Defaults to open/in_progress only (existing behavior)

(bd-x2ht)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 16:04:39 -08:00

178 lines
5.3 KiB
Go

package ui
import (
"fmt"
"strings"
"testing"
)
func TestRenderBasicStyles(t *testing.T) {
t.Run("semantic wrappers", func(t *testing.T) {
cases := []struct {
name string
got string
want string
}{
{"pass", RenderPass("ok"), PassStyle.Render("ok")},
{"warn", RenderWarn("careful"), WarnStyle.Render("careful")},
{"fail", RenderFail("boom"), FailStyle.Render("boom")},
{"muted", RenderMuted("note"), MutedStyle.Render("note")},
{"accent", RenderAccent("info"), AccentStyle.Render("info")},
{"category", RenderCategory("mixed Case"), CategoryStyle.Render("MIXED CASE")},
{"separator", RenderSeparator(), MutedStyle.Render(SeparatorLight)},
{"pass icon", RenderPassIcon(), PassStyle.Render(IconPass)},
{"warn icon", RenderWarnIcon(), WarnStyle.Render(IconWarn)},
{"fail icon", RenderFailIcon(), FailStyle.Render(IconFail)},
{"skip icon", RenderSkipIcon(), MutedStyle.Render(IconSkip)},
{"info icon", RenderInfoIcon(), AccentStyle.Render(IconInfo)},
{"bold", RenderBold("bold"), BoldStyle.Render("bold")},
{"command", RenderCommand("bd prime"), CommandStyle.Render("bd prime")},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if tc.got != tc.want {
t.Fatalf("%s mismatch: got %q want %q", tc.name, tc.got, tc.want)
}
})
}
})
}
func TestRenderStatusAndPriority(t *testing.T) {
statusCases := []struct {
status string
want string
}{
{"open", StatusOpenStyle.Render("open")},
{"in_progress", StatusInProgressStyle.Render("in_progress")},
{"blocked", StatusBlockedStyle.Render("blocked")},
{"pinned", StatusPinnedStyle.Render("pinned")},
{"hooked", StatusHookedStyle.Render("hooked")},
{"closed", StatusClosedStyle.Render("closed")},
{"custom", StatusOpenStyle.Render("custom")},
}
for _, tc := range statusCases {
if got := RenderStatus(tc.status); got != tc.want {
t.Fatalf("status %s mismatch: got %q want %q", tc.status, got, tc.want)
}
}
priorityCases := []struct {
priority int
want string
}{
{0, PriorityP0Style.Render("P0")},
{1, PriorityP1Style.Render("P1")},
{2, PriorityP2Style.Render("P2")},
{3, PriorityP3Style.Render("P3")},
{4, PriorityP4Style.Render("P4")},
{5, "P5"},
}
for _, tc := range priorityCases {
if got := RenderPriority(tc.priority); got != tc.want {
t.Fatalf("priority %d mismatch: got %q want %q", tc.priority, got, tc.want)
}
}
if got := RenderPriorityForStatus(0, "closed"); got != "P0" {
t.Fatalf("closed priority should be plain text, got %q", got)
}
if got := RenderPriorityForStatus(1, "open"); got != RenderPriority(1) {
t.Fatalf("open priority should use styling")
}
}
func TestRenderTypeVariants(t *testing.T) {
cases := []struct {
issueType string
want string
}{
{"bug", TypeBugStyle.Render("bug")},
{"feature", TypeFeatureStyle.Render("feature")},
{"task", TypeTaskStyle.Render("task")},
{"epic", TypeEpicStyle.Render("epic")},
{"chore", TypeChoreStyle.Render("chore")},
{"agent", TypeAgentStyle.Render("agent")},
{"role", TypeRoleStyle.Render("role")},
{"custom", "custom"},
}
for _, tc := range cases {
if got := RenderType(tc.issueType); got != tc.want {
t.Fatalf("type %s mismatch: got %q want %q", tc.issueType, got, tc.want)
}
}
if got := RenderTypeForStatus("bug", "closed"); got != "bug" {
t.Fatalf("closed type should be plain, got %q", got)
}
if got := RenderTypeForStatus("bug", "open"); got != RenderType("bug") {
t.Fatalf("open type should be styled")
}
}
func TestRenderIssueCompact(t *testing.T) {
open := RenderIssueCompact("bd-1", 0, "bug", "in_progress", "ship it")
wantOpen := fmt.Sprintf("%s [%s] [%s] %s - %s",
RenderID("bd-1"),
RenderPriority(0),
RenderType("bug"),
RenderStatus("in_progress"),
"ship it",
)
if open != wantOpen {
t.Fatalf("open issue line mismatch: got %q want %q", open, wantOpen)
}
closed := RenderIssueCompact("bd-2", 2, "task", "closed", "done")
raw := fmt.Sprintf("%s [P%d] [%s] %s - %s", "bd-2", 2, "task", "closed", "done")
if closed != StatusClosedStyle.Render(raw) {
t.Fatalf("closed issue line should be dimmed: got %q", closed)
}
}
func TestRenderClosedUtilities(t *testing.T) {
line := "bd-42 closed"
if got := RenderClosedLine(line); got != StatusClosedStyle.Render(line) {
t.Fatalf("closed line mismatch: got %q", got)
}
if got := RenderID("bd-5"); got != IDStyle.Render("bd-5") {
t.Fatalf("RenderID mismatch")
}
}
func TestRenderCommandAndCategoryAreUppercaseSafe(t *testing.T) {
got := RenderCategory(" already upper ")
if !strings.Contains(got, " ALREADY UPPER ") {
t.Fatalf("category should uppercase input, got %q", got)
}
cmd := RenderCommand("bd prime")
if !strings.Contains(cmd, "bd prime") {
t.Fatalf("command output missing text: %q", cmd)
}
}
func TestIsAgentMode(t *testing.T) {
// Test default (no env vars) - t.Setenv automatically restores after test
t.Setenv("BD_AGENT_MODE", "")
t.Setenv("CLAUDE_CODE", "")
if IsAgentMode() {
t.Fatal("expected false with no env vars")
}
// Test BD_AGENT_MODE=1
t.Setenv("BD_AGENT_MODE", "1")
t.Setenv("CLAUDE_CODE", "")
if !IsAgentMode() {
t.Fatal("expected true with BD_AGENT_MODE=1")
}
// Test CLAUDE_CODE auto-detection
t.Setenv("BD_AGENT_MODE", "")
t.Setenv("CLAUDE_CODE", "something")
if !IsAgentMode() {
t.Fatal("expected true with CLAUDE_CODE set")
}
}