package main import ( "strings" "testing" "github.com/charmbracelet/lipgloss" ) func TestApplyThemeChangesAllRenderingThemes(t *testing.T) { defer func() { if err := applyTheme("dark"); err != nil { t.Fatal(err) } }() if err := applyTheme("dark"); err != nil { t.Fatal(err) } if codeHighlightTheme != "onedark" { t.Fatalf("dark syntax theme = %q, want onedark", codeHighlightTheme) } if err := applyTheme("light"); err != nil { t.Fatal(err) } if markdownStyleName != "light" || codeHighlightTheme != "github" { t.Fatalf("light theme did not propagate: markdown=%q code=%q", markdownStyleName, codeHighlightTheme) } if err := applyTheme("unknown"); err == nil { t.Fatal("unknown theme was accepted") } } func TestNoColorThemeDisablesSyntaxColors(t *testing.T) { defer applyTheme("dark") if err := applyTheme("no-color"); err != nil { t.Fatal(err) } rendered := highlightedSource("go", "func main() {}") if strings.Contains(rendered, "\x1b[") || colorEnabled { t.Fatalf("no-color source contains terminal colors: %q", rendered) } } func TestBuiltinThemesApply(t *testing.T) { defer applyTheme("dark") names := []string{ "dark", "light", "catppuccin", "catppuccin-mocha", "catppuccin-latte", "gruvbox", "gruvbox-dark", "gruvbox-light", "one-dark-pro", "github", "github-dark", "github-light", "high-contrast", "no-color", } for _, name := range names { t.Run(name, func(t *testing.T) { if err := applyTheme(name); err != nil { t.Fatal(err) } if len(authorPalette) == 0 { t.Fatal("theme has no author colors") } }) } } func TestLightBuiltinThemesSelectLightRendering(t *testing.T) { defer applyTheme("dark") for _, name := range []string{"light", "catppuccin-latte", "gruvbox-light", "github-light"} { if err := applyTheme(name); err != nil { t.Fatal(err) } if !themeIsLight || markdownStyleName != "light" { t.Fatalf("%s was not treated as a light theme", name) } } } func TestCustomThemeOverlaysBuiltinBase(t *testing.T) { defer applyTheme("dark") custom := CustomThemeConfig{ Base: "catppuccin-mocha", Title: "#010203", AuthorPalette: []string{"#112233", "#445566"}, SyntaxTheme: "gruvbox", } if err := applyTheme("custom", custom); err != nil { t.Fatal(err) } if currentThemeName != "custom" || titleStyle.GetForeground() != lipgloss.Color("#010203") || codeHighlightTheme != "gruvbox" { t.Fatalf( "custom theme was not applied: name=%q title=%q syntax=%q", currentThemeName, titleStyle.GetForeground(), codeHighlightTheme, ) } if len(authorPalette) != 2 || authorPalette[0] != lipgloss.Color("#112233") || authorPalette[1] != lipgloss.Color("#445566") { t.Fatalf("custom author palette = %#v", authorPalette) } if selectedLineBackground == "" { t.Fatal("custom theme did not inherit unspecified base colors") } } func TestCustomThemeValidation(t *testing.T) { tests := []struct { name string custom CustomThemeConfig want string }{ { name: "invalid color", custom: CustomThemeConfig{Title: "red"}, want: "title must be a #RRGGBB color", }, { name: "invalid syntax theme", custom: CustomThemeConfig{SyntaxTheme: "not-a-chroma-theme"}, want: "unknown Chroma syntax_theme", }, { name: "recursive base", custom: CustomThemeConfig{Base: "custom"}, want: "custom_theme.base cannot be custom", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { _, err := resolveThemePalette("custom", test.custom) if err == nil || !strings.Contains(err.Error(), test.want) { t.Fatalf("error = %v, want it to contain %q", err, test.want) } }) } } func TestCatppuccinUsesCanonicalMauveAccent(t *testing.T) { palette, err := resolveThemePalette("catppuccin-mocha", CustomThemeConfig{}) if err != nil { t.Fatal(err) } if palette.Title != "#CBA6F7" || palette.ActiveBackground != "#CBA6F7" { t.Fatalf( "Catppuccin accent is title=%q active=%q, want mauve", palette.Title, palette.ActiveBackground, ) } }