package ui import ( "image/color" "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 TestChooseTheme(t *testing.T) { dark := terminalInfo{bg: color.RGBA{30, 30, 46, 255}, hasBG: true} light := terminalInfo{bg: color.RGBA{239, 241, 245, 255}, hasBG: true} for _, tc := range []struct { setting string info terminalInfo want string }{ {"", light, "light"}, {"auto", dark, "dark"}, {"auto", terminalInfo{}, "dark"}, {"dark", light, "dark"}, {"light", dark, "light"}, } { if th, note := chooseTheme(tc.setting, tc.info); th.name != tc.want { t.Errorf("%q with %+v: %s (%s)", tc.setting, tc.info, th.name, note) } } } func TestUseTokens(t *testing.T) { oldStyles := tview.Styles oldNames := map[string]tcell.Color{} for k, v := range tcell.ColorNames { oldNames[k] = v } defer func() { tview.Styles = oldStyles for k, v := range oldNames { tcell.ColorNames[k] = v } }() useTokens() if tview.Styles.PrimitiveBackgroundColor != tokens.bg || tview.Styles.PrimaryTextColor != tokens.text { t.Error("tview styles not set") } // Color names in texts mean the roles. if named("yellow") != tokens.yellow || tcell.GetColor("black") != tokens.bg || tcell.GetColor("white") != tokens.text { t.Error("color names not set") } } func TestThemedScreenSwitchesLive(t *testing.T) { sim := tcell.NewSimulationScreen("") if err := sim.Init(); err != nil { t.Fatal(err) } defer sim.Fini() sim.SetSize(4, 1) s := newThemedScreen(sim, darkTheme) poster := tcell.NewRGBColor(10, 20, 30) draw := func() { s.SetContent(0, 0, 'a', nil, tcell.StyleDefault.Foreground(tokens.yellow).Background(tokens.bg)) s.SetContent(1, 0, 'b', nil, tcell.StyleDefault.Foreground(poster).Background(poster)) } check := func(th theme) { _, _, st, _ := sim.GetContent(0, 0) if fg, bg, _ := st.Decompose(); fg != th.yellow || bg != th.bg { t.Errorf("%s: role colors drawn as %v on %v", th.name, fg, bg) } _, _, st, _ = sim.GetContent(1, 0) if fg, bg, _ := st.Decompose(); fg != poster || bg != poster { t.Errorf("%s: other colors changed to %v on %v", th.name, fg, bg) } } draw() check(darkTheme) s.setTheme(lightTheme) draw() check(lightTheme) // A poster color that equals a role color is nudged off it. if c := notToken(tokens.red); c == tokens.red { t.Error("role color in a picture not nudged") } if notToken(poster) != poster { t.Error("ordinary color changed") } } func TestThemeColorsSurvive256Colors(t *testing.T) { // Every theme color is its own nearest match in the 256-color palette, so // a terminal without 24-bit color shows it exactly and never picks one of // the first 16 colors, which color themes change. palette := make([]tcell.Color, 256) for i := range palette { palette[i] = tcell.PaletteColor(i) } for _, th := range []theme{darkTheme, lightTheme} { for _, c := range []tcell.Color{th.bg, th.text, th.border, th.field, th.fieldMuted, th.list, th.pressed, th.toast, th.gray, th.yellow, th.green, th.red, th.blue, th.purple} { if got := tcell.FindColor(c, palette); got < tcell.PaletteColor(16) { t.Errorf("%s theme: %v maps to palette color %d", th.name, c, got-tcell.ColorValid) } } } }