1package tui
2
3import (
4 "testing"
5
6 tea "charm.land/bubbletea/v2"
7)
8
9func TestProtocolComboboxCycles(t *testing.T) {
10 m := NewLogin(true)
11
12 // Starts focused on the protocol combobox with the default selection.
13 if got := m.protocol(); got != "imap" {
14 t.Fatalf("initial protocol = %q, want imap", got)
15 }
16
17 right := tea.KeyPressMsg{Code: tea.KeyRight}
18 want := []string{"jmap", "pop3", "maildir", "imap"} // wraps around
19 for _, w := range want {
20 model, _ := m.Update(right)
21 m = model.(*Login)
22 if got := m.protocol(); got != w {
23 t.Fatalf("after right, protocol = %q, want %q", got, w)
24 }
25 }
26
27 // Left cycles backwards, wrapping from imap to maildir.
28 model, _ := m.Update(tea.KeyPressMsg{Code: tea.KeyLeft})
29 m = model.(*Login)
30 if got := m.protocol(); got != "maildir" {
31 t.Fatalf("after left, protocol = %q, want maildir", got)
32 }
33}
34
35func TestProtocolComboboxIgnoresTyping(t *testing.T) {
36 m := NewLogin(true)
37 // Focused on the protocol field; typed characters must not edit it.
38 model, _ := m.Update(tea.KeyPressMsg{Code: 'x', Text: "x"})
39 m = model.(*Login)
40 if got := m.protocol(); got != "imap" {
41 t.Fatalf("after typing, protocol = %q, want imap (unchanged)", got)
42 }
43}
44
45func TestValidPort(t *testing.T) {
46 tests := []struct {
47 name string
48 input string
49 fallback int
50 want int
51 }{
52 {"empty string returns fallback", "", 993, 993},
53 {"valid port 993", "993", 143, 993},
54 {"valid port 1 (minimum)", "1", 993, 1},
55 {"valid port 65535 (maximum)", "65535", 993, 65535},
56 {"valid port 587", "587", 25, 587},
57 {"valid port 995", "995", 110, 995},
58 {"port 0 is invalid, returns fallback", "0", 993, 993},
59 {"negative port is invalid", "-1", 993, 993},
60 {"port over 65535 is invalid", "70000", 993, 993},
61 {"non-numeric returns fallback", "abc", 993, 993},
62 {"port with spaces returns fallback", " 993 ", 993, 993},
63 {"port 8080", "8080", 993, 8080},
64 {"large negative number", "-99999", 993, 993},
65 {"very large number", "9999999", 993, 993},
66 }
67
68 for _, tt := range tests {
69 t.Run(tt.name, func(t *testing.T) {
70 got := validPort(tt.input, tt.fallback)
71 if got != tt.want {
72 t.Errorf("validPort(%q, %d) = %d, want %d", tt.input, tt.fallback, got, tt.want)
73 }
74 })
75 }
76}
77
78func TestValidPortDifferentFallbacks(t *testing.T) {
79 if got := validPort("", 143); got != 143 {
80 t.Errorf("empty with fallback 143 = %d, want 143", got)
81 }
82 if got := validPort("", 587); got != 587 {
83 t.Errorf("empty with fallback 587 = %d, want 587", got)
84 }
85 if got := validPort("", 995); got != 995 {
86 t.Errorf("empty with fallback 995 = %d, want 995", got)
87 }
88 if got := validPort("bad", 25); got != 25 {
89 t.Errorf("invalid with fallback 25 = %d, want 25", got)
90 }
91}
92
93func TestSubmitFormPortValidation(t *testing.T) {
94 m := NewLogin(true)
95
96 tests := []struct {
97 name string
98 portVal string
99 want int
100 fallback int
101 }{
102 {"valid custom port 143", "143", 143, 993},
103 {"invalid port 0 falls back", "0", 993, 993},
104 {"invalid negative port falls back", "-1", 993, 993},
105 {"invalid overflow port falls back", "70000", 993, 993},
106 {"non-numeric port falls back", "abc", 993, 993},
107 {"empty port uses default", "", 993, 993},
108 {"boundary port 1", "1", 1, 993},
109 {"boundary port 65535", "65535", 65535, 993},
110 }
111
112 for _, tt := range tests {
113 t.Run(tt.name, func(t *testing.T) {
114 m.inputs[inputIMAPPort].SetValue(tt.portVal)
115 fn := m.submitForm()
116 msg := fn().(Credentials)
117 if msg.IMAPPort != tt.want {
118 t.Errorf("IMAPPort for input %q = %d, want %d", tt.portVal, msg.IMAPPort, tt.want)
119 }
120 })
121 }
122}