trash_archive_test.go

  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}