1package tui
2
3import (
4 "testing"
5 "time"
6
7 tea "charm.land/bubbletea/v2"
8 "github.com/floatpane/matcha/config"
9 "github.com/floatpane/matcha/fetcher"
10)
11
12// TestNewTrashArchive verifies that a new TrashArchive is created correctly.
13func TestNewTrashArchive(t *testing.T) {
14 accounts := []config.Account{
15 {ID: "account-1", Email: "test@example.com", FetchEmail: "fetch@example.com"},
16 }
17
18 trashEmails := []fetcher.Email{
19 {UID: 1, From: "a@example.com", Subject: "Trash Email 1", AccountID: "account-1"},
20 }
21
22 archiveEmails := []fetcher.Email{
23 {UID: 2, From: "b@example.com", Subject: "Archive Email 1", AccountID: "account-1"},
24 }
25
26 ta := NewTrashArchive(trashEmails, archiveEmails, accounts)
27
28 // Default view should be Trash
29 if ta.activeView != MailboxTrash {
30 t.Errorf("Expected default view to be MailboxTrash, got %v", ta.activeView)
31 }
32
33 // GetActiveMailbox should return Trash
34 if ta.GetActiveMailbox() != MailboxTrash {
35 t.Errorf("Expected GetActiveMailbox to return MailboxTrash, got %v", ta.GetActiveMailbox())
36 }
37
38 // GetActiveInbox should return trashInbox
39 if ta.GetActiveInbox() != ta.trashInbox {
40 t.Error("Expected GetActiveInbox to return trashInbox")
41 }
42}
43
44// TestTrashArchiveToggle verifies toggling between trash and archive views.
45func TestTrashArchiveToggle(t *testing.T) {
46 accounts := []config.Account{
47 {ID: "account-1", Email: "test@example.com"},
48 }
49
50 trashEmails := []fetcher.Email{
51 {UID: 1, From: "a@example.com", Subject: "Trash Email", AccountID: "account-1"},
52 }
53
54 archiveEmails := []fetcher.Email{
55 {UID: 2, From: "b@example.com", Subject: "Archive Email", AccountID: "account-1"},
56 }
57
58 ta := NewTrashArchive(trashEmails, archiveEmails, accounts)
59
60 // Initially should be Trash
61 if ta.activeView != MailboxTrash {
62 t.Fatalf("Expected initial view to be MailboxTrash, got %v", ta.activeView)
63 }
64
65 // Press tab to switch to Archive
66 ta.Update(tea.KeyPressMsg{Code: tea.KeyTab})
67
68 if ta.activeView != MailboxArchive {
69 t.Errorf("Expected view to be MailboxArchive after tab, got %v", ta.activeView)
70 }
71
72 if ta.GetActiveInbox() != ta.archiveInbox {
73 t.Error("Expected GetActiveInbox to return archiveInbox after toggle")
74 }
75
76 // Press tab again to switch back to Trash
77 ta.Update(tea.KeyPressMsg{Code: tea.KeyTab})
78
79 if ta.activeView != MailboxTrash {
80 t.Errorf("Expected view to be MailboxTrash after second tab, got %v", ta.activeView)
81 }
82}
83
84// TestTrashArchiveRemoveEmail verifies removing emails from the correct inbox.
85func TestTrashArchiveRemoveEmail(t *testing.T) {
86 accounts := []config.Account{
87 {ID: "account-1", Email: "test@example.com"},
88 }
89
90 trashEmails := []fetcher.Email{
91 {UID: 1, From: "a@example.com", Subject: "Trash Email 1", AccountID: "account-1"},
92 {UID: 2, From: "b@example.com", Subject: "Trash Email 2", AccountID: "account-1"},
93 }
94
95 archiveEmails := []fetcher.Email{
96 {UID: 3, From: "c@example.com", Subject: "Archive Email 1", AccountID: "account-1"},
97 {UID: 4, From: "d@example.com", Subject: "Archive Email 2", AccountID: "account-1"},
98 }
99
100 ta := NewTrashArchive(trashEmails, archiveEmails, accounts)
101
102 // Remove a trash email
103 ta.RemoveEmail(1, "account-1", MailboxTrash)
104
105 if len(ta.trashInbox.allEmails) != 1 {
106 t.Errorf("Expected 1 trash email after removal, got %d", len(ta.trashInbox.allEmails))
107 }
108
109 // Archive emails should be unchanged
110 if len(ta.archiveInbox.allEmails) != 2 {
111 t.Errorf("Expected 2 archive emails unchanged, got %d", len(ta.archiveInbox.allEmails))
112 }
113
114 // Remove an archive email
115 ta.RemoveEmail(3, "account-1", MailboxArchive)
116
117 if len(ta.archiveInbox.allEmails) != 1 {
118 t.Errorf("Expected 1 archive email after removal, got %d", len(ta.archiveInbox.allEmails))
119 }
120}
121
122// TestTrashArchiveSetEmails verifies updating emails in trash and archive.
123func TestTrashArchiveSetEmails(t *testing.T) {
124 accounts := []config.Account{
125 {ID: "account-1", Email: "test@example.com"},
126 }
127
128 ta := NewTrashArchive(nil, nil, accounts)
129
130 // Set trash emails
131 newTrashEmails := []fetcher.Email{
132 {UID: 10, From: "new@example.com", Subject: "New Trash", AccountID: "account-1"},
133 }
134 ta.SetTrashEmails(newTrashEmails, accounts)
135
136 if len(ta.trashInbox.allEmails) != 1 {
137 t.Errorf("Expected 1 trash email after SetTrashEmails, got %d", len(ta.trashInbox.allEmails))
138 }
139
140 // Set archive emails
141 newArchiveEmails := []fetcher.Email{
142 {UID: 20, From: "archive@example.com", Subject: "New Archive", AccountID: "account-1"},
143 {UID: 21, From: "archive2@example.com", Subject: "New Archive 2", AccountID: "account-1"},
144 }
145 ta.SetArchiveEmails(newArchiveEmails, accounts)
146
147 if len(ta.archiveInbox.allEmails) != 2 {
148 t.Errorf("Expected 2 archive emails after SetArchiveEmails, got %d", len(ta.archiveInbox.allEmails))
149 }
150}
151
152// TestTrashArchiveWindowSizeMsg verifies window size is passed to both inboxes.
153func TestTrashArchiveWindowSizeMsg(t *testing.T) {
154 accounts := []config.Account{
155 {ID: "account-1", Email: "test@example.com"},
156 }
157
158 ta := NewTrashArchive(nil, nil, accounts)
159
160 ta.Update(tea.WindowSizeMsg{Width: 100, Height: 50})
161
162 if ta.width != 100 {
163 t.Errorf("Expected width 100, got %d", ta.width)
164 }
165 if ta.height != 50 {
166 t.Errorf("Expected height 50, got %d", ta.height)
167 }
168}
169
170// TestTrashArchiveViewEmailMsg verifies that selecting an email generates correct message.
171func TestTrashArchiveViewEmailMsg(t *testing.T) {
172 accounts := []config.Account{
173 {ID: "account-1", Email: "test@example.com"},
174 }
175
176 trashEmails := []fetcher.Email{
177 {UID: 100, From: "sender@example.com", Subject: "Test Trash", AccountID: "account-1", Date: time.Now()},
178 }
179
180 ta := NewTrashArchive(trashEmails, nil, accounts)
181
182 // Simulate pressing Enter on trash view
183 _, cmd := ta.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
184 if cmd == nil {
185 t.Fatal("Expected a command, but got nil")
186 }
187
188 msg := cmd()
189 viewMsg, ok := msg.(ViewEmailMsg)
190 if !ok {
191 t.Fatalf("Expected ViewEmailMsg, got %T", msg)
192 }
193
194 if viewMsg.UID != 100 {
195 t.Errorf("Expected UID 100, got %d", viewMsg.UID)
196 }
197
198 if viewMsg.Mailbox != MailboxTrash {
199 t.Errorf("Expected Mailbox MailboxTrash, got %v", viewMsg.Mailbox)
200 }
201}
202
203// TestTrashArchiveDeleteEmailMsg verifies delete messages from trash/archive views.
204func TestTrashArchiveDeleteEmailMsg(t *testing.T) {
205 accounts := []config.Account{
206 {ID: "account-1", Email: "test@example.com"},
207 }
208
209 archiveEmails := []fetcher.Email{
210 {UID: 200, From: "sender@example.com", Subject: "Test Archive", AccountID: "account-1", Date: time.Now()},
211 }
212
213 ta := NewTrashArchive(nil, archiveEmails, accounts)
214
215 // Switch to archive view
216 ta.Update(tea.KeyPressMsg{Code: tea.KeyTab})
217
218 // Simulate pressing 'd' to delete
219 _, cmd := ta.Update(tea.KeyPressMsg{Code: 'd', Text: "d"})
220 if cmd == nil {
221 t.Fatal("Expected a command, but got nil")
222 }
223
224 msg := cmd()
225 deleteMsg, ok := msg.(DeleteEmailMsg)
226 if !ok {
227 t.Fatalf("Expected DeleteEmailMsg, got %T", msg)
228 }
229
230 if deleteMsg.UID != 200 {
231 t.Errorf("Expected UID 200, got %d", deleteMsg.UID)
232 }
233
234 if deleteMsg.Mailbox != MailboxArchive {
235 t.Errorf("Expected Mailbox MailboxArchive, got %v", deleteMsg.Mailbox)
236 }
237}
238
239// TestTrashArchiveMultipleAccounts verifies tabs are created for multiple accounts.
240func TestTrashArchiveMultipleAccounts(t *testing.T) {
241 accounts := []config.Account{
242 {ID: "account-1", Email: "test1@example.com", FetchEmail: "fetch1@example.com"},
243 {ID: "account-2", Email: "test2@example.com", FetchEmail: "fetch2@example.com"},
244 }
245
246 trashEmails := []fetcher.Email{
247 {UID: 1, From: "a@example.com", Subject: "Trash 1", AccountID: "account-1"},
248 {UID: 2, From: "b@example.com", Subject: "Trash 2", AccountID: "account-2"},
249 }
250
251 ta := NewTrashArchive(trashEmails, nil, accounts)
252
253 // Both trash and archive inboxes should have 3 tabs (ALL + 2 accounts)
254 if len(ta.trashInbox.tabs) != 3 {
255 t.Errorf("Expected 3 tabs in trashInbox, got %d", len(ta.trashInbox.tabs))
256 }
257
258 if len(ta.archiveInbox.tabs) != 3 {
259 t.Errorf("Expected 3 tabs in archiveInbox, got %d", len(ta.archiveInbox.tabs))
260 }
261
262 // Verify FetchEmail is used for tab labels
263 if ta.trashInbox.tabs[1].Label != "fetch1@example.com" {
264 t.Errorf("Expected tab label 'fetch1@example.com', got %q", ta.trashInbox.tabs[1].Label)
265 }
266}
267
268// TestTrashInboxMailboxKind verifies that trash inbox has correct mailbox kind.
269func TestTrashInboxMailboxKind(t *testing.T) {
270 accounts := []config.Account{
271 {ID: "account-1", Email: "test@example.com"},
272 }
273
274 inbox := NewTrashInbox(nil, accounts)
275
276 if inbox.GetMailbox() != MailboxTrash {
277 t.Errorf("Expected MailboxTrash, got %v", inbox.GetMailbox())
278 }
279}
280
281// TestArchiveInboxMailboxKind verifies that archive inbox has correct mailbox kind.
282func TestArchiveInboxMailboxKind(t *testing.T) {
283 accounts := []config.Account{
284 {ID: "account-1", Email: "test@example.com"},
285 }
286
287 inbox := NewArchiveInbox(nil, accounts)
288
289 if inbox.GetMailbox() != MailboxArchive {
290 t.Errorf("Expected MailboxArchive, got %v", inbox.GetMailbox())
291 }
292}
293
294// TestInboxGetBaseTitle verifies correct titles for different mailbox kinds.
295func TestInboxGetBaseTitle(t *testing.T) {
296 accounts := []config.Account{
297 {ID: "account-1", Email: "test@example.com"},
298 }
299
300 tests := []struct {
301 name string
302 mailbox MailboxKind
303 expected string
304 }{
305 {"Inbox", MailboxInbox, "Inbox"},
306 {"Sent", MailboxSent, "Sent"},
307 {"Trash", MailboxTrash, "Trash"},
308 {"Archive", MailboxArchive, "Archive"},
309 }
310
311 for _, tt := range tests {
312 t.Run(tt.name, func(t *testing.T) {
313 inbox := NewInboxWithMailbox(nil, accounts, tt.mailbox)
314 title := inbox.getBaseTitle()
315 if title != tt.expected {
316 t.Errorf("Expected title %q, got %q", tt.expected, title)
317 }
318 })
319 }
320}
321
322// TestInboxFetchEmailUsedForTabs verifies that FetchEmail is used for tab labels.
323func TestInboxFetchEmailUsedForTabs(t *testing.T) {
324 accounts := []config.Account{
325 {ID: "account-1", Email: "login@example.com", FetchEmail: "display@example.com"},
326 {ID: "account-2", Email: "login2@example.com"}, // No FetchEmail, should fallback to Email
327 }
328
329 inbox := NewInbox(nil, accounts)
330
331 // First account should use FetchEmail
332 if inbox.tabs[1].Label != "display@example.com" {
333 t.Errorf("Expected tab label 'display@example.com', got %q", inbox.tabs[1].Label)
334 }
335
336 // Second account should fallback to Email
337 if inbox.tabs[2].Label != "login2@example.com" {
338 t.Errorf("Expected tab label 'login2@example.com', got %q", inbox.tabs[2].Label)
339 }
340}