package theming import ( "os" "path/filepath" "strings" "testing" "github.com/MDietermann/ArduinoArchSetup/internal/config" ) func TestListDiscoversThemes(t *testing.T) { themes := List() if len(themes) == 0 { t.Fatal("no themes discovered") } for _, th := range themes { if th.ID == "" || th.DisplayName == "" || th.NvChadTheme == "" { t.Errorf("incomplete theme: %+v", th) } } } func TestGetKnownThemeReturnsMatch(t *testing.T) { got, ok := Get("gruvbox") if !ok { t.Fatal("gruvbox should exist") } if got.ID != "gruvbox" || got.DisplayName == "" || got.NvChadTheme != "gruvbox" { t.Fatalf("got %+v", got) } } func TestGetUnknownThemeReturnsFalse(t *testing.T) { if _, ok := Get("does-not-exist"); ok { t.Fatal("bogus id should return ok=false") } } func TestListIsSortedByID(t *testing.T) { themes := List() for i := 1; i < len(themes); i++ { if themes[i].ID < themes[i-1].ID { t.Fatalf("not sorted: %s before %s", themes[i-1].ID, themes[i].ID) } } } func TestApplyWritesFourToolConfigs(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) if err := Apply(config.Config{}, "gruvbox"); err != nil { t.Fatal(err) } checks := []struct{ rel, wantPrefix string }{ {".config/starship.toml", "palette"}, {".config/zellij/config.kdl", "theme"}, {".config/yazi/theme.toml", ""}, {".config/lazygit/config.yml", "gui"}, } for _, c := range checks { data, err := os.ReadFile(filepath.Join(home, c.rel)) if err != nil { t.Errorf("%s missing: %v", c.rel, err) continue } if len(data) == 0 { t.Errorf("%s empty", c.rel) } if c.wantPrefix != "" && !strings.HasPrefix(strings.TrimSpace(string(data)), c.wantPrefix) { t.Errorf("%s: want prefix %q, got %q", c.rel, c.wantPrefix, string(data[:min(len(data), 40)])) } } } func TestApplyUnknownThemeErrors(t *testing.T) { if err := Apply(config.Config{}, "does-not-exist"); err == nil { t.Fatal("want error on unknown theme") } } func TestApplyUpdatesOverlayActive(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) if err := Apply(config.Config{}, "gruvbox"); err != nil { t.Fatal(err) } data, err := os.ReadFile(filepath.Join(home, ".config", "archduino", "overlay.yaml")) if err != nil { t.Fatalf("overlay missing: %v", err) } if !strings.Contains(string(data), "__active: gruvbox") { t.Fatalf("overlay missing active theme: %s", data) } } func TestApplyPreservesOtherOverlayKeys(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) op := filepath.Join(home, ".config", "archduino", "overlay.yaml") if err := os.MkdirAll(filepath.Dir(op), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(op, []byte("version: 1\nextra_packages:\n - foo\n"), 0o644); err != nil { t.Fatal(err) } if err := Apply(config.Config{}, "gruvbox"); err != nil { t.Fatal(err) } data, _ := os.ReadFile(op) if !strings.Contains(string(data), "- foo") { t.Fatalf("extra_packages lost: %s", data) } if !strings.Contains(string(data), "__active: gruvbox") { t.Fatalf("active theme missing after preserve: %s", data) } } func TestApplyRewritesChadrcIfPresent(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) nvimCfg := filepath.Join(home, ".config", "nvim", "lua") if err := os.MkdirAll(nvimCfg, 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(filepath.Join(nvimCfg, "chadrc.lua"), []byte(`M.base46 = { theme = "onedark" }`), 0o644); err != nil { t.Fatal(err) } // The real ChadrcWriter lives in the neovim package, which cannot be // imported here without creating an import cycle (neovim→theming). // Stub it with a minimal writer that covers what the test asserts. prev := ChadrcWriter t.Cleanup(func() { ChadrcWriter = prev }) ChadrcWriter = func(name string) error { return os.WriteFile(filepath.Join(nvimCfg, "chadrc.lua"), []byte(`theme = "`+name+`"`), 0o644) } if err := Apply(config.Config{}, "gruvbox"); err != nil { t.Fatal(err) } data, _ := os.ReadFile(filepath.Join(nvimCfg, "chadrc.lua")) if !strings.Contains(string(data), `theme = "gruvbox"`) { t.Fatalf("chadrc not rewritten: %s", data) } if strings.Contains(string(data), `theme = "onedark"`) { t.Fatalf("old theme still present: %s", data) } } func TestApplyNoChadrcIsFine(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) if err := Apply(config.Config{}, "gruvbox"); err != nil { t.Fatal(err) } } func TestAllFiveThemesPresent(t *testing.T) { want := []string{"catppuccin", "dracula", "gruvbox", "nord", "tokyonight"} themes := List() if len(themes) != len(want) { t.Fatalf("want %d themes, got %d", len(want), len(themes)) } for i, id := range want { if themes[i].ID != id { t.Fatalf("themes[%d].ID = %q, want %q", i, themes[i].ID, id) } } } func TestApplyEachThemeWritesStarship(t *testing.T) { for _, id := range []string{"catppuccin", "dracula", "nord", "tokyonight"} { id := id t.Run(id, func(t *testing.T) { home := t.TempDir() t.Setenv("HOME", home) if err := Apply(config.Config{}, id); err != nil { t.Fatal(err) } data, err := os.ReadFile(filepath.Join(home, ".config", "starship.toml")) if err != nil { t.Fatal(err) } if len(data) == 0 { t.Fatal("empty starship.toml") } }) } }