package theme import ( "os" "path/filepath" "strings" "testing" ) // write drops a theme file into dir and returns the directory, so each test // works against its own themes directory rather than the user's. func write(t *testing.T, dir, name, body string) { t.Helper() if err := os.WriteFile(filepath.Join(dir, name+".yaml"), []byte(body), 0o644); err != nil { t.Fatal(err) } } func TestLoad_EmptyNameIsTheDefault(t *testing.T) { got, err := Load("") if err != nil { t.Fatalf("unexpected error: %v", err) } if got.Name != Default.Name { t.Errorf("got theme %q, want %q", got.Name, Default.Name) } } func TestLoadFrom_BuiltinsResolveWithoutAFile(t *testing.T) { dir := t.TempDir() for _, name := range BuiltinNames() { got, err := loadFrom(dir, name) if err != nil { t.Fatalf("%s: unexpected error: %v", name, err) } if got.Name != name { t.Errorf("got theme %q, want %q", got.Name, name) } if got.Primary == "" { t.Errorf("%s: primary is unset", name) } } } func TestLoadFrom_ExtendsOverridesOnlyWhatIsNamed(t *testing.T) { dir := t.TempDir() write(t, dir, "mine", "extends: gruvbox-dark\nprimary: \"#d3869b\"\n") got, err := loadFrom(dir, "mine") if err != nil { t.Fatalf("unexpected error: %v", err) } if got.Name != "mine" { t.Errorf("got name %q, want %q", got.Name, "mine") } if got.Primary != "#d3869b" { t.Errorf("got primary %q, want the override", got.Primary) } if got.Muted != GruvboxDark.Muted { t.Errorf("got muted %q, want inherited %q", got.Muted, GruvboxDark.Muted) } if got.SevInfo != GruvboxDark.SevInfo { t.Errorf("got sev_info %q, want inherited %q", got.SevInfo, GruvboxDark.SevInfo) } } func TestLoadFrom_UserFileShadowsABuiltin(t *testing.T) { dir := t.TempDir() write(t, dir, "gruvbox-dark", "extends: gruvbox-dark\naccent: \"#fabd2f\"\n") got, err := loadFrom(dir, "gruvbox-dark") if err != nil { t.Fatalf("unexpected error: %v", err) } if got.Accent != "#fabd2f" { t.Errorf("got accent %q, want the shadowing file's value", got.Accent) } } func TestLoadFrom_WithoutExtendsEveryTokenIsRequired(t *testing.T) { dir := t.TempDir() write(t, dir, "partial", "primary: \"#83a598\"\n") _, err := loadFrom(dir, "partial") if err == nil { t.Fatal("expected an error for a theme missing tokens") } for _, want := range []string{"muted", "sev_info", "extends"} { if !strings.Contains(err.Error(), want) { t.Errorf("error should mention %q, got: %v", want, err) } } } func TestLoadFrom_CompleteThemeNeedsNoExtends(t *testing.T) { dir := t.TempDir() write(t, dir, "full", `primary: "#000001" on_primary: "#000002" text: "#000003" muted: "#000004" accent: "#000005" firing: "#000006" resolved: "#000007" error: "#000008" sev_critical: "#000009" sev_error: "#00000a" sev_warning: "#00000b" sev_info: "#00000c" `) got, err := loadFrom(dir, "full") if err != nil { t.Fatalf("unexpected error: %v", err) } if got.Primary != "#000001" || got.SevInfo != "#00000c" { t.Errorf("tokens not applied: %+v", got) } } func TestLoadFrom_Errors(t *testing.T) { tests := []struct { name string file string // empty means: write no file at all body string want string }{ {"unknown name", "", "", "unknown theme"}, {"unknown key", "typo", "extends: gruvbox-dark\nprimry: \"#83a598\"\n", `unknown key "primry"`}, {"unknown key lists the valid ones", "typo2", "primry: \"#83a598\"\n", "Valid keys: extends, primary,"}, {"bad colour", "bad", "extends: gruvbox-dark\nprimary: notacolour\n", "invalid colour"}, {"bad colour names the token", "badkey", "extends: gruvbox-dark\nsev_warning: \"#gggggg\"\n", "sev_warning"}, {"unknown base", "orphan", "extends: solarized\nprimary: \"#83a598\"\n", "unknown theme \"solarized\""}, {"malformed yaml", "broken", "extends: [\n", "invalid theme"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { dir := t.TempDir() name := tt.file if name == "" { name = "missing" } else { write(t, dir, name, tt.body) } _, err := loadFrom(dir, name) if err == nil { t.Fatal("expected an error") } if !strings.Contains(err.Error(), tt.want) { t.Errorf("error should mention %q, got: %v", tt.want, err) } }) } } func TestLoadFrom_RejectsPathsAsNames(t *testing.T) { dir := t.TempDir() for _, name := range []string{"../secrets", "sub/theme", ".."} { if _, err := loadFrom(dir, name); err == nil { t.Errorf("%q: expected an error", name) } } } func TestParseColor(t *testing.T) { ok := []string{"#83a598", "#FFF", "#abc", "0", "15", "255"} for _, s := range ok { if _, err := parseColor(s); err != nil { t.Errorf("parseColor(%q) = %v, want no error", s, err) } } bad := []string{"", "83a598", "#ab", "#abcd", "#gggggg", "256", "-1", "+7", "007", "red"} for _, s := range bad { if _, err := parseColor(s); err == nil { t.Errorf("parseColor(%q) = nil, want an error", s) } } }