keybinds_test.go

 1package config
 2
 3import (
 4	"os"
 5	"path/filepath"
 6	"strings"
 7	"testing"
 8)
 9
10func TestValidateKeybinds_NoConflicts(t *testing.T) {
11	kb := defaultKeybinds()
12	conflicts := ValidateKeybinds(kb)
13	if len(conflicts) != 0 {
14		t.Errorf("default keybinds have conflicts: %v", conflicts)
15	}
16}
17
18func TestValidateKeybinds_InboxConflict(t *testing.T) {
19	kb := defaultKeybinds()
20	kb.Inbox.Archive = kb.Inbox.Delete // same key as delete
21	conflicts := ValidateKeybinds(kb)
22	if len(conflicts) == 0 {
23		t.Fatal("expected conflict, got none")
24	}
25	found := false
26	for _, c := range conflicts {
27		if strings.Contains(c, "inbox") {
28			found = true
29			break
30		}
31	}
32	if !found {
33		t.Errorf("expected inbox conflict, got: %v", conflicts)
34	}
35}
36
37func TestValidateKeybinds_CrossAreaNotConflict(t *testing.T) {
38	kb := defaultKeybinds()
39	// "d" is delete in both inbox and email — intentional, not a conflict
40	kb.Inbox.Delete = "d"
41	kb.Email.Delete = "d"
42	conflicts := ValidateKeybinds(kb)
43	if len(conflicts) != 0 {
44		t.Errorf("cross-area duplicates should not be conflicts: %v", conflicts)
45	}
46}
47
48func TestValidateKeybinds_EmptyKeySkipped(t *testing.T) {
49	kb := defaultKeybinds()
50	kb.Drafts.Delete = ""
51	kb.Drafts.Open = ""
52	conflicts := ValidateKeybinds(kb)
53	if len(conflicts) != 0 {
54		t.Errorf("empty keys should not produce conflicts: %v", conflicts)
55	}
56}
57
58func TestLoadKeybindsFromDir_WritesDefault(t *testing.T) {
59	dir := t.TempDir()
60	if err := LoadKeybindsFromDir(dir); err != nil {
61		t.Fatalf("LoadKeybindsFromDir: %v", err)
62	}
63	if Keybinds.Inbox.Delete == "" {
64		t.Error("expected inbox.delete to be set after loading defaults")
65	}
66}
67
68func TestLoadKeybindsFromDir_ParsesCustom(t *testing.T) {
69	dir := t.TempDir()
70	// Write defaults first
71	if err := LoadKeybindsFromDir(dir); err != nil {
72		t.Fatalf("write defaults: %v", err)
73	}
74
75	// Override inbox delete key
76	custom := `{"inbox":{"delete":"x","archive":"a","refresh":"r","open":"enter","next_tab":"l","prev_tab":"h","visual_mode":"v"},"global":{"quit":"ctrl+c","cancel":"esc","nav_up":"k","nav_down":"j"},"email":{"reply":"r","forward":"f","delete":"d","archive":"a","toggle_images":"i","rsvp_accept":"1","rsvp_decline":"2","rsvp_tentative":"3","focus_attachments":"tab"},"composer":{"external_editor":"ctrl+e","next_field":"tab","prev_field":"shift+tab","undo_send":"u"},"folder":{"next_folder":"tab","prev_folder":"shift+tab","move":"m","focus_preview":"]","focus_inbox":"["},"drafts":{"open":"enter","delete":"d"}}`
77	if err := os.WriteFile(filepath.Join(dir, "keybinds.json"), []byte(custom), 0600); err != nil {
78		t.Fatalf("write custom: %v", err)
79	}
80	if err := LoadKeybindsFromDir(dir); err != nil {
81		t.Fatalf("LoadKeybindsFromDir custom: %v", err)
82	}
83	if Keybinds.Inbox.Delete != "x" {
84		t.Errorf("expected inbox.delete=x, got %q", Keybinds.Inbox.Delete)
85	}
86	if Keybinds.Composer.UndoSend != "u" {
87		t.Errorf("expected composer.undo_send=u, got %q", Keybinds.Composer.UndoSend)
88	}
89}