Files
beads/internal/ui/terminal_test.go
Steve Yegge f6f9ef260d feat: auto-detect non-TTY and adjust output (bd-xrwy)
Add TTY detection to automatically disable ANSI colors when stdout is
piped or redirected. Respects standard conventions:
- NO_COLOR environment variable (no-color.org)
- CLICOLOR=0 disables color
- CLICOLOR_FORCE enables color even in non-TTY

Also adds ShouldUseEmoji() helper controlled by BD_NO_EMOJI.
Pager already disabled non-TTY in previous work (bd-jdz3).

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-30 10:19:44 -08:00

140 lines
3.2 KiB
Go

package ui
import (
"os"
"testing"
)
func TestShouldUseColor(t *testing.T) {
// Save original env vars
origNoColor := os.Getenv("NO_COLOR")
origCliColor := os.Getenv("CLICOLOR")
origCliColorForce := os.Getenv("CLICOLOR_FORCE")
defer func() {
setEnv("NO_COLOR", origNoColor)
setEnv("CLICOLOR", origCliColor)
setEnv("CLICOLOR_FORCE", origCliColorForce)
}()
tests := []struct {
name string
noColor string
cliColor string
cliColorForce string
wantColor bool
skipTTYDepCheck bool // Some tests don't depend on TTY state
}{
{
name: "NO_COLOR disables color",
noColor: "1",
wantColor: false,
skipTTYDepCheck: true,
},
{
name: "NO_COLOR empty string value still disables",
noColor: "", // will be unset
cliColor: "",
cliColorForce: "",
wantColor: false, // depends on TTY, but we're in test = no TTY
skipTTYDepCheck: false,
},
{
name: "CLICOLOR=0 disables color",
cliColor: "0",
wantColor: false,
skipTTYDepCheck: true,
},
{
name: "CLICOLOR_FORCE enables color even in non-TTY",
cliColorForce: "1",
wantColor: true,
skipTTYDepCheck: true,
},
{
name: "NO_COLOR takes precedence over CLICOLOR_FORCE",
noColor: "1",
cliColorForce: "1",
wantColor: false,
skipTTYDepCheck: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Clear all vars first
os.Unsetenv("NO_COLOR")
os.Unsetenv("CLICOLOR")
os.Unsetenv("CLICOLOR_FORCE")
// Set test-specific vars
if tt.noColor != "" {
os.Setenv("NO_COLOR", tt.noColor)
}
if tt.cliColor != "" {
os.Setenv("CLICOLOR", tt.cliColor)
}
if tt.cliColorForce != "" {
os.Setenv("CLICOLOR_FORCE", tt.cliColorForce)
}
got := ShouldUseColor()
if tt.skipTTYDepCheck && got != tt.wantColor {
t.Errorf("ShouldUseColor() = %v, want %v", got, tt.wantColor)
}
})
}
}
func TestShouldUseEmoji(t *testing.T) {
// Save original env var
origNoEmoji := os.Getenv("BD_NO_EMOJI")
defer setEnv("BD_NO_EMOJI", origNoEmoji)
tests := []struct {
name string
noEmoji string
wantEmoji bool
}{
{
name: "BD_NO_EMOJI disables emoji",
noEmoji: "1",
wantEmoji: false,
},
{
name: "No BD_NO_EMOJI falls back to TTY check",
noEmoji: "",
wantEmoji: false, // In test, stdout is not a TTY
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
os.Unsetenv("BD_NO_EMOJI")
if tt.noEmoji != "" {
os.Setenv("BD_NO_EMOJI", tt.noEmoji)
}
got := ShouldUseEmoji()
if got != tt.wantEmoji {
t.Errorf("ShouldUseEmoji() = %v, want %v", got, tt.wantEmoji)
}
})
}
}
func TestIsTerminal(t *testing.T) {
// When running under go test, stdout is typically not a TTY
got := IsTerminal()
// We can't easily assert the value, but we can verify it doesn't panic
t.Logf("IsTerminal() = %v (expected false in test environment)", got)
}
// setEnv sets or unsets an environment variable
func setEnv(key, value string) {
if value == "" {
os.Unsetenv(key)
} else {
os.Setenv(key, value)
}
}