244 lines
8.2 KiB
Go
244 lines
8.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestStateTransitions(t *testing.T) {
|
|
value := state{
|
|
Current: currentPair{Light: "old-light", Dark: "old-dark"},
|
|
Catalog: catalog{
|
|
FavoritePairs: []themePair{{Name: "swamp", Dark: "swamp-dark", Light: "swamp-light"}},
|
|
FavoriteThemes: []string{"favorite"},
|
|
OtherPairs: []themePair{{Name: "nord", Dark: "nord-dark", Light: "nord-light"}},
|
|
OtherThemes: []string{"swamp-dark", "nord-light", "other"},
|
|
},
|
|
}
|
|
value.Catalog.normalize()
|
|
if len(value.Catalog.OtherThemes) != 1 || value.Catalog.OtherThemes[0] != "other" {
|
|
t.Fatalf("pair themes leaked into other themes: %#v", value.Catalog.OtherThemes)
|
|
}
|
|
if !value.Catalog.favorite("nord") {
|
|
t.Fatal("named pair was treated as a theme")
|
|
}
|
|
value.Catalog.configurePair(themePair{Name: "nord", Dark: "new-dark", Light: "new-light"})
|
|
pair, favorite, found := value.Catalog.findPair("nord")
|
|
if !found || !favorite || pair.Dark != "new-dark" {
|
|
t.Fatalf("reconfigured pair lost favorite state: %#v", value.Catalog)
|
|
}
|
|
if !value.Catalog.unfavorite("nord") {
|
|
t.Fatal("favorite pair was treated as a theme")
|
|
}
|
|
|
|
path := filepath.Join(t.TempDir(), "state.toml")
|
|
if err := saveState(path, value); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
roundTrip, err := loadState(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if roundTrip.Current != value.Current {
|
|
t.Fatalf("current pair changed after round trip: %#v", roundTrip.Current)
|
|
}
|
|
if _, favorite, found := roundTrip.Catalog.findPair("nord"); !found || favorite {
|
|
t.Fatalf("pair did not remain in other pairs: %#v", roundTrip.Catalog)
|
|
}
|
|
config := readFile(t, path)
|
|
if strings.Contains(config, "\n ") {
|
|
t.Fatalf("configuration is unnecessarily indented:\n%s", config)
|
|
}
|
|
order := []string{"[current]", "[[favorites.pairs]]", "[favorites]", "[[others.pairs]]", "[others]"}
|
|
last := -1
|
|
for _, section := range order {
|
|
index := strings.Index(config, section)
|
|
if index <= last {
|
|
t.Fatalf("configuration section order is wrong:\n%s", config)
|
|
}
|
|
last = index
|
|
}
|
|
if strings.Contains(config, "current =") || strings.Contains(config, "[[favorite_themes]]") || strings.Contains(config, "[[other_themes]]") {
|
|
t.Fatalf("state entries used the wrong TOML shape:\n%s", config)
|
|
}
|
|
}
|
|
|
|
func TestCLIUpdatesAndAppliesPair(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
bin := filepath.Join(tmp, "bin")
|
|
if err := os.Mkdir(bin, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
log := filepath.Join(tmp, "applied")
|
|
writeExecutable(t, filepath.Join(bin, "tinty"), `#!/bin/sh
|
|
case "$1" in
|
|
info) [ "$2" != missing ] ;;
|
|
apply) printf '%s\n' "$2" >> "$TINTY_LOG" ;;
|
|
*) exit 2 ;;
|
|
esac
|
|
`)
|
|
statePath := filepath.Join(tmp, "state.toml")
|
|
t.Setenv("PATH", bin+":"+os.Getenv("PATH"))
|
|
t.Setenv("TINTY_LOG", log)
|
|
t.Setenv("TINT_STATE_FILE", statePath)
|
|
t.Setenv("TINTY_PAIR_APPEARANCE", "dark")
|
|
if err := saveState(statePath, state{Current: currentPair{Light: "old-light", Dark: "old-dark"}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var stdout, stderr bytes.Buffer
|
|
if err := runCLI([]string{"dark:new-dark"}, &stdout, &stderr); err != nil {
|
|
t.Fatalf("update failed: %v: %s", err, stderr.String())
|
|
}
|
|
value, err := loadState(statePath)
|
|
if err != nil || value.Current.Dark != "new-dark" || value.Current.Light != "old-light" {
|
|
t.Fatalf("wrong current pair: %#v, %v", value.Current, err)
|
|
}
|
|
if got := strings.TrimSpace(readFile(t, log)); got != "new-dark" {
|
|
t.Fatalf("wrong applied theme: %q", got)
|
|
}
|
|
|
|
if err := runCLI([]string{"pair", "nord", "dark:nord-dark", "light:nord-light"}, &stdout, &stderr); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := runCLI([]string{"favorite", "nord"}, &stdout, &stderr); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := runCLI([]string{"nord"}, &stdout, &stderr); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
value, err = loadState(statePath)
|
|
if err != nil || value.Current.Dark != "nord-dark" || value.Current.Light != "nord-light" {
|
|
t.Fatalf("named pair not applied: %#v, %v", value.Current, err)
|
|
}
|
|
if _, favorite, found := value.Catalog.findPair("nord"); !found || !favorite {
|
|
t.Fatalf("pair not favorited: %#v", value.Catalog)
|
|
}
|
|
stdout.Reset()
|
|
if err := runCLI([]string{"config"}, &stdout, &stderr); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if stdout.String() != readFile(t, statePath) {
|
|
t.Fatal("tint config did not print the state file verbatim")
|
|
}
|
|
}
|
|
|
|
func TestApplyAppsUsesTinted8Semantics(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
bin := filepath.Join(tmp, "bin")
|
|
if err := os.Mkdir(bin, 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
palette := map[string]tintyColor{
|
|
"black-normal": {Hex: "#000000"}, "black-bright": {Hex: "#111111"},
|
|
"gray-dim": {Hex: "#222222"}, "gray-normal": {Hex: "#333333"}, "gray-bright": {Hex: "#444444"},
|
|
"white-normal": {Hex: "#eeeeee"}, "white-bright": {Hex: "#ffffff"},
|
|
"red-normal": {Hex: "#aa0000"}, "orange-normal": {Hex: "#bb5500"},
|
|
"yellow-normal": {Hex: "#aaaa00"}, "green-normal": {Hex: "#00aa00"},
|
|
"cyan-normal": {Hex: "#00aaaa"}, "blue-normal": {Hex: "#0000aa"},
|
|
"magenta-normal": {Hex: "#aa00aa"}, "brown-normal": {Hex: "#663300"},
|
|
}
|
|
scheme := tintyScheme{
|
|
ID: "tinted8-test", System: "tinted8", Variant: "dark", Palette: palette,
|
|
UI: map[string]tintyColor{
|
|
"accent.normal": {Hex: "#123456"}, "global.background.normal": {Hex: "#010101"},
|
|
"global.foreground.normal": {Hex: "#fefefe"},
|
|
},
|
|
Syntax: map[string]tintyColor{"entity.name.function": {Hex: "#654321"}},
|
|
}
|
|
listPath := filepath.Join(tmp, "schemes.json")
|
|
data, err := json.Marshal([]tintyScheme{scheme})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(listPath, data, 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
writeExecutable(t, filepath.Join(bin, "tinty"), `#!/bin/sh
|
|
case "$1" in
|
|
current) printf '%s\n' "$TINTY_TEST_SCHEME" ;;
|
|
list) cat "$TINTY_TEST_LIST" ;;
|
|
*) exit 2 ;;
|
|
esac
|
|
`)
|
|
t.Setenv("HOME", tmp)
|
|
t.Setenv("PATH", bin+":"+os.Getenv("PATH"))
|
|
t.Setenv("TINTY_TEST_SCHEME", scheme.ID)
|
|
t.Setenv("TINTY_TEST_LIST", listPath)
|
|
if err := os.MkdirAll(filepath.Join(tmp, ".config", "herdr"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := os.WriteFile(filepath.Join(tmp, ".config", "herdr", "config.toml"), []byte("onboarding = false\n"), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
zed := filepath.Join(tmp, "zed.json")
|
|
if err := os.WriteFile(zed, []byte(`{"name":"old","themes":[{"name":"old"}]}`), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := applyApps(zed); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var omp struct {
|
|
Colors map[string]string `json:"colors"`
|
|
}
|
|
if err := json.Unmarshal([]byte(readFile(t, filepath.Join(tmp, ".omp", "agent", "themes", "tinty-current.json"))), &omp); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if omp.Colors["accent"] != "#123456" || omp.Colors["syntaxFunction"] != "#654321" {
|
|
t.Fatalf("semantic colors ignored: %#v", omp.Colors)
|
|
}
|
|
herdr := readFile(t, filepath.Join(tmp, ".config", "herdr", "config.toml"))
|
|
if !strings.Contains(herdr, `accent = "#123456"`) {
|
|
t.Fatalf("semantic Herdr accent missing:\n%s", herdr)
|
|
}
|
|
zedOutput := readFile(t, filepath.Join(tmp, ".config", "zed", "themes", "tinty-current.json"))
|
|
if !strings.Contains(zedOutput, `"name": "Tinty Current"`) {
|
|
t.Fatalf("Zed theme was not renamed:\n%s", zedOutput)
|
|
}
|
|
}
|
|
|
|
func TestBase16SemanticFallback(t *testing.T) {
|
|
palette := make(map[string]tintyColor)
|
|
for i, value := range []string{
|
|
"#000000", "#111111", "#222222", "#333333",
|
|
"#444444", "#555555", "#666666", "#777777",
|
|
"#880000", "#990000", "#aaaa00", "#00bb00",
|
|
"#00cccc", "#0000dd", "#ee00ee", "#ff00ff",
|
|
} {
|
|
palette[fmt.Sprintf("base%02X", i)] = tintyColor{Hex: value}
|
|
}
|
|
colors, err := semanticColors(tintyScheme{System: "base16", Palette: palette})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
omp, herdr := semanticTheme(colors, nil, nil)
|
|
if omp["accent"] != colors["base0D"] || omp["syntaxFunction"] != colors["base0D"] {
|
|
t.Fatalf("Base16 OMP fallback changed: %#v", omp)
|
|
}
|
|
if herdr["panel_bg"] != colors["base00"] || herdr["text"] != colors["base05"] {
|
|
t.Fatalf("Base16 Herdr fallback changed: %#v", herdr)
|
|
}
|
|
}
|
|
|
|
func writeExecutable(t *testing.T, path, content string) {
|
|
t.Helper()
|
|
if err := os.WriteFile(path, []byte(content), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func readFile(t *testing.T, path string) string {
|
|
t.Helper()
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return string(data)
|
|
}
|