package ui import ( "image/color" "strings" "testing" "github.com/gdamore/tcell/v2" "github.com/rivo/tview" ) func TestParseTerminalAnswers(t *testing.T) { for _, tc := range []struct { name string got string graphics bool bg color.RGBA hasBG bool }{ {"ghostty", "\x1b_Gi=31;OK\x1b\\\x1b]11;rgb:1e1e/1e1e/2e2e\x1b\\\x1b[?62;22c", true, color.RGBA{30, 30, 46, 255}, true}, {"text color too", "\x1b]10;rgb:4c4c/4f4f/6969\x1b\\\x1b]11;rgb:efef/f1f1/f5f5\x1b\\\x1b[?62c", false, color.RGBA{239, 241, 245, 255}, true}, {"bel and two digits", "\x1b]11;rgb:ff/80/00\a\x1b[?1;2c", false, color.RGBA{255, 128, 0, 255}, true}, {"one digit", "\x1b]11;rgb:f/0/8\x1b\\\x1b[?6c", false, color.RGBA{255, 0, 136, 255}, true}, {"only device attributes", "\x1b[?1;2c", false, color.RGBA{}, false}, {"garbage", "\x1b]11;rgb:zz/00/00\x1b\\\x1b[?6c", false, color.RGBA{}, false}, {"nothing", "", false, color.RGBA{}, false}, } { info := parseTerminalAnswers([]byte(tc.got)) if info.graphics != tc.graphics || info.hasBG != tc.hasBG || info.bg != tc.bg { t.Errorf("%s: %+v", tc.name, info) } } } func TestUseTerminalBackground(t *testing.T) { // tcell's terminal database also reads these from the real environment // (and keeps what it added to an entry because of them, so the test uses // a terminal nothing else looks up). t.Setenv("COLORTERM", "") t.Setenv("TCELL_TRUECOLOR", "") defer func(old tview.Theme, tab string) { tview.Styles, activeTabTag = old, tab }(tview.Styles, activeTabTag) env := func(m map[string]string) func(string) string { return func(k string) string { return m[k] } } dark := terminalInfo{bg: color.RGBA{30, 30, 46, 255}, hasBG: true} light := terminalInfo{bg: color.RGBA{239, 241, 245, 255}, hasBG: true, fg: color.RGBA{76, 79, 105, 255}, hasFG: true} truecolor := env(map[string]string{"COLORTERM": "truecolor"}) for _, tc := range []struct { name string info terminalInfo env func(string) string want bool }{ {"dark background, true color", dark, truecolor, true}, {"no true color", dark, env(map[string]string{"TERM": "vt100"}), false}, {"ghostty without COLORTERM", dark, env(map[string]string{"TERM": "xterm-ghostty"}), true}, {"true color disabled for tcell", dark, env(map[string]string{"TERM": "xterm-ghostty", "TCELL_TRUECOLOR": "disable"}), false}, {"light background", light, truecolor, true}, {"no answer", terminalInfo{}, truecolor, false}, } { tview.Styles.PrimitiveBackgroundColor = tcell.ColorBlack note := useTerminalBackground(tc.info, tc.env) if got := strings.Contains(note, "own color"); got != tc.want { t.Errorf("%s: %s", tc.name, note) } want := tcell.ColorBlack if tc.want { want = rgb(tc.info.bg) } if tview.Styles.PrimitiveBackgroundColor != want { t.Errorf("%s: background %v", tc.name, tview.Styles.PrimitiveBackgroundColor) } } } func TestLightBackgroundUsesTheTextColor(t *testing.T) { defer func(old tview.Theme, tab string) { tview.Styles, activeTabTag = old, tab }(tview.Styles, activeTabTag) latte := terminalInfo{bg: color.RGBA{239, 241, 245, 255}, hasBG: true, fg: color.RGBA{76, 79, 105, 255}, hasFG: true} useTerminalBackground(latte, func(k string) string { return map[string]string{"COLORTERM": "truecolor"}[k] }) st := tview.Styles if st.PrimaryTextColor != tcell.NewRGBColor(76, 79, 105) || st.BorderColor != st.PrimaryTextColor { t.Errorf("text %v, borders %v", st.PrimaryTextColor, st.BorderColor) } if st.ContrastBackgroundColor == tcell.ColorBlue { t.Error("input fields should not stay palette blue on a light background") } if activeTabTag != "[#eff1f5:#4c4f69]" { t.Errorf("active tab %s", activeTabTag) } // Without a text color answer the text is black. tview.Styles = st useTerminalBackground(terminalInfo{bg: latte.bg, hasBG: true}, func(k string) string { return map[string]string{"COLORTERM": "truecolor"}[k] }) if tview.Styles.PrimaryTextColor != tcell.NewRGBColor(0, 0, 0) { t.Errorf("fallback text %v", tview.Styles.PrimitiveBackgroundColor) } }