From 4d96c0a5316db55d610816e6908c72b582ac572e Mon Sep 17 00:00:00 2001 From: Hayden Hargreaves Date: Wed, 8 Apr 2026 12:57:33 -0700 Subject: [PATCH] test: updated colorscheme related tests. tested loader --- internal/command/handlers_test.go | 63 +++++++++++++--- internal/theme/loader_test.go | 117 ++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 10 deletions(-) create mode 100644 internal/theme/loader_test.go diff --git a/internal/command/handlers_test.go b/internal/command/handlers_test.go index f756dfd..960be9f 100644 --- a/internal/command/handlers_test.go +++ b/internal/command/handlers_test.go @@ -9,6 +9,7 @@ import ( "git.gophernest.net/azpect/TextEditor/internal/action" "git.gophernest.net/azpect/TextEditor/internal/core" + "git.gophernest.net/azpect/TextEditor/internal/theme" tea "github.com/charmbracelet/bubbletea" ) @@ -5506,6 +5507,12 @@ func TestCmdDeleteBuffer(t *testing.T) { // ================================================== func TestCmdColorscheme(t *testing.T) { + pickTheme := func(m *action.MockModel) string { + for name := range m.Themes() { + return name + } + return "" + } // -------------------------------------------------- // Group 1: Valid name — styles are updated @@ -5540,8 +5547,12 @@ func TestCmdColorscheme(t *testing.T) { t.Run("valid name sets no error output", func(t *testing.T) { m := action.NewMockModel() + name := pickTheme(m) + if name == "" { + t.Fatal("expected at least one theme in mock") + } - cmdColorscheme(m, []string{"default"}, false) + cmdColorscheme(m, []string{name}, false) if m.CommandOutputVal != nil && m.CommandOutputVal.IsError { t.Error("expected no error output for a valid colorscheme name") @@ -5550,8 +5561,12 @@ func TestCmdColorscheme(t *testing.T) { t.Run("valid name returns nil tea.Cmd", func(t *testing.T) { m := action.NewMockModel() + name := pickTheme(m) + if name == "" { + t.Fatal("expected at least one theme in mock") + } - cmd := cmdColorscheme(m, []string{"default"}, false) + cmd := cmdColorscheme(m, []string{name}, false) if cmd != nil { t.Error("expected nil tea.Cmd for colorscheme command") @@ -5650,8 +5665,12 @@ func TestCmdColorscheme(t *testing.T) { t.Run("extra args beyond name do not panic", func(t *testing.T) { m := action.NewMockModel() + name := pickTheme(m) + if name == "" { + t.Fatal("expected at least one theme in mock") + } - cmdColorscheme(m, []string{"default", "extra", "args"}, false) + cmdColorscheme(m, []string{name, "extra", "args"}, false) }) // -------------------------------------------------- @@ -5660,8 +5679,12 @@ func TestCmdColorscheme(t *testing.T) { t.Run("force flag with valid name still sets styles", func(t *testing.T) { m := action.NewMockModel() + name := pickTheme(m) + if name == "" { + t.Fatal("expected at least one theme in mock") + } - cmdColorscheme(m, []string{"default"}, true) + cmdColorscheme(m, []string{name}, true) if m.CommandOutputVal != nil && m.CommandOutputVal.IsError { t.Error("expected styles to change with force=true and valid name") @@ -5737,11 +5760,17 @@ func TestCmdListColorschemes(t *testing.T) { t.Run("commandOutput lines contains known built-in styles", func(t *testing.T) { m := action.NewMockModel() + m.SetThemes(map[string]theme.EditorTheme{ + "kanagawa": {}, + "kanagawa-dragon": {}, + "kanagawa-lotus": {}, + "tokyonight-storm": {}, + }) cmdListColorschemes(m, []string{}, false) lines := m.CommandOutputVal.Lines - known := []string{"default"} + known := []string{"kanagawa", "kanagawa-dragon", "kanagawa-lotus", "tokyonight-storm"} for _, name := range known { found := false for _, l := range lines { @@ -5756,14 +5785,18 @@ func TestCmdListColorschemes(t *testing.T) { } }) - t.Run("commandOutput lines matches styles.Names()", func(t *testing.T) { + t.Run("commandOutput lines matches themes map size", func(t *testing.T) { m := action.NewMockModel() + m.SetThemes(map[string]theme.EditorTheme{ + "kanagawa": {}, + "kanagawa-dragon": {}, + "kanagawa-lotus": {}, + }) cmdListColorschemes(m, []string{}, false) - expected := []string{"default"} - if len(m.CommandOutputVal.Lines) != len(expected) { - t.Errorf("expected %d colorschemes, got %d", len(expected), len(m.CommandOutputVal.Lines)) + if len(m.CommandOutputVal.Lines) != len(m.Themes()) { + t.Errorf("expected %d colorschemes, got %d", len(m.Themes()), len(m.CommandOutputVal.Lines)) } }) @@ -5782,9 +5815,19 @@ func TestCmdListColorschemes(t *testing.T) { t.Run("args and force are ignored", func(t *testing.T) { m1 := action.NewMockModel() m2 := action.NewMockModel() + m1.SetThemes(map[string]theme.EditorTheme{ + "kanagawa": {}, + "kanagawa-dragon": {}, + "kanagawa-lotus": {}, + }) + m2.SetThemes(map[string]theme.EditorTheme{ + "kanagawa": {}, + "kanagawa-dragon": {}, + "kanagawa-lotus": {}, + }) cmdListColorschemes(m1, []string{}, false) - cmdListColorschemes(m2, []string{"default", "extra"}, true) + cmdListColorschemes(m2, []string{"kanagawa", "extra"}, true) if len(m1.CommandOutputVal.Lines) != len(m2.CommandOutputVal.Lines) { t.Error("expected args and force to have no effect on list output") diff --git a/internal/theme/loader_test.go b/internal/theme/loader_test.go new file mode 100644 index 0000000..18ce37d --- /dev/null +++ b/internal/theme/loader_test.go @@ -0,0 +1,117 @@ +package theme + +import ( + "fmt" + "testing" +) + +func TestLoadEmbeddedThemesJSON_LoadsExpectedThemes(t *testing.T) { + themesJSON, err := LoadEmbeddedThemesJSON() + if err != nil { + t.Fatalf("LoadEmbeddedThemesJSON returned error: %v", err) + } + + want := []string{"kanagawa", "kanagawa-dragon", "kanagawa-lotus"} + for _, name := range want { + th, ok := themesJSON[name] + if !ok { + t.Fatalf("expected embedded theme %q to be loaded", name) + } + if th.Name == "" { + t.Fatalf("expected embedded theme %q to have a name", name) + } + if th.Line.BG == "" { + t.Fatalf("expected embedded theme %q to include line background", name) + } + } +} + +func TestMapEmbeddedThemeToEditorTheme_MapsStylesAndNormalizesSyntaxKeys(t *testing.T) { + in := map[string]ThemeJSON{ + "custom": { + Name: "custom", + Line: ColorStyleJSON{FG: "#dddddd", BG: "#101010"}, + Background: ColorStyleJSON{BG: "#101010"}, + VisualHighlight: ColorStyleJSON{BG: "#202020"}, + Cursors: CursorJSON{ + Normal: ColorStyleJSON{FG: "#101010", BG: "#dddddd"}, + Insert: ColorStyleJSON{FG: "#101010", BG: "#cccccc"}, + Command: ColorStyleJSON{FG: "#101010", BG: "#bbbbbb"}, + Replace: ColorStyleJSON{FG: "#101010", BG: "#aaaaaa"}, + }, + Gutter: GutterJSON{ + Default: ColorStyleJSON{FG: "#666666", BG: "#0a0a0a"}, + CurrentLine: ColorStyleJSON{FG: "#eeeeee", BG: "#0a0a0a"}, + }, + StatusBar: StatusBarJSON{ + Default: ColorStyleJSON{FG: "#cccccc", BG: "#0a0a0a"}, + }, + CommandLine: CommandLineJSON{ + Error: ColorStyleJSON{FG: "#ff0000", BG: "#101010"}, + OutputBorder: ColorStyleJSON{FG: "#dddddd", BG: "#0a0a0a"}, + ContinueMessage: ColorStyleJSON{FG: "#00aaff", BG: "#101010"}, + }, + Syntax: SyntaxJSON{ + Group: map[string]string{ + " String ": "#00ff00", + }, + Exact: map[string]string{ + " @KEYWORD.Return ": "#ff00ff", + }, + }, + }, + } + + out := MapEmbeddedThemeToEditorTheme(in) + th, ok := out["custom"] + if !ok { + t.Fatalf("expected mapped theme with key %q", "custom") + } + + if got := colorHex(th.Line.GetForeground()); got != "#dddddd" { + t.Fatalf("line fg mismatch: got %q want %q", got, "#dddddd") + } + if got := colorHex(th.Line.GetBackground()); got != "#101010" { + t.Fatalf("line bg mismatch: got %q want %q", got, "#101010") + } + + if got := colorHex(th.Syntax.Exact["keyword.return"].GetForeground()); got != "#ff00ff" { + t.Fatalf("exact capture fg mismatch: got %q want %q", got, "#ff00ff") + } + if got := colorHex(th.Syntax.Exact["keyword.return"].GetBackground()); got != "#101010" { + t.Fatalf("exact capture bg mismatch: got %q want %q", got, "#101010") + } + if got := colorHex(th.Syntax.Group["string"].GetForeground()); got != "#00ff00" { + t.Fatalf("group capture fg mismatch: got %q want %q", got, "#00ff00") + } + + if got := colorHex(th.CommandLine.Error.GetForeground()); got != "#ff0000" { + t.Fatalf("command error fg mismatch: got %q want %q", got, "#ff0000") + } +} + +func TestMapEmbededThemeToEditorTheme_AliasMatchesPrimary(t *testing.T) { + in := map[string]ThemeJSON{ + "x": { + Name: "x", + Line: ColorStyleJSON{FG: "#ffffff", BG: "#000000"}, + Syntax: SyntaxJSON{ + Group: map[string]string{"keyword": "#123456"}, + }, + }, + } + + a := MapEmbededThemeToEditorTheme(in) + b := MapEmbeddedThemeToEditorTheme(in) + + if len(a) != len(b) { + t.Fatalf("alias map size mismatch: %d vs %d", len(a), len(b)) + } + if colorHex(a["x"].Syntax.Group["keyword"].GetForeground()) != colorHex(b["x"].Syntax.Group["keyword"].GetForeground()) { + t.Fatalf("alias should produce identical mapped styles") + } +} + +func colorHex(c any) string { + return fmt.Sprint(c) +}