diff --git a/fetcher/fetcher_test.go b/fetcher/fetcher_test.go new file mode 100644 index 0000000000000000000000000000000000000000..93de6d79654ce502a2566abff6a92b6b60cc8682 --- /dev/null +++ b/fetcher/fetcher_test.go @@ -0,0 +1,49 @@ +package fetcher + +import ( + "testing" + + "github.com/andrinoff/email-cli/config" +) + +// TestFetchEmails is an integration test that requires a live IMAP server and valid credentials. +// NOTE: This test will be skipped if it cannot load a configuration file, +// making it safe to run in a CI environment without credentials. +// To run this test locally, ensure you have a valid `config.json` file. +func TestFetchEmails(t *testing.T) { + // Attempt to load the configuration. + cfg, err := config.LoadConfig() + if err != nil { + // If config doesn't exist, skip the test. This is useful for CI environments. + t.Skipf("Skipping TestFetchEmails: could not load config: %v", err) + } + + // If the password is a placeholder, skip the test to avoid failed auth attempts. + if cfg.Password == "" || cfg.Password == "supersecret" { + t.Skip("Skipping TestFetchEmails: placeholder or empty password found in config.") + } + + emails, err := FetchEmails(cfg) + if err != nil { + t.Fatalf("FetchEmails() failed with error: %v", err) + } + + if len(emails) == 0 { + // This is not necessarily a failure, but we can log it. + t.Log("FetchEmails() returned 0 emails. This might be expected.") + } + + // Check that the emails are sorted from newest to oldest. + if len(emails) > 1 { + if emails[0].Date.Before(emails[len(emails)-1].Date) { + t.Error("Emails do not appear to be sorted from newest to oldest.") + } + } + + // Check a sample email for expected content. + for _, email := range emails { + if email.Subject == "" && email.From == "" { + t.Errorf("Fetched email has empty subject and from fields: %+v", email) + } + } +} diff --git a/tui/composer_test.go b/tui/composer_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d6db5f8bbc4d6ac8cb8a5312efe829619ade78fb --- /dev/null +++ b/tui/composer_test.go @@ -0,0 +1,81 @@ +package tui + +import ( + "reflect" + "testing" + + tea "github.com/charmbracelet/bubbletea" +) + +// TestComposerUpdate verifies the state transitions in the email composer. +func TestComposerUpdate(t *testing.T) { + // Initialize a new composer. + composer := NewComposer("test@example.com") + + t.Run("Focus cycling", func(t *testing.T) { + // Initial focus is on the 'To' input (index 0). + if composer.focusIndex != 0 { + t.Errorf("Initial focusIndex should be 0, got %d", composer.focusIndex) + } + + // Simulate pressing Tab to move to the 'Subject' field. + model, _ := composer.Update(tea.KeyMsg{Type: tea.KeyTab}) + composer = model.(Composer) + if composer.focusIndex != 1 { + t.Errorf("After one Tab, focusIndex should be 1, got %d", composer.focusIndex) + } + + // Simulate pressing Tab again to move to the 'Body' field. + model, _ = composer.Update(tea.KeyMsg{Type: tea.KeyTab}) + composer = model.(Composer) + if composer.focusIndex != 2 { + t.Errorf("After two Tabs, focusIndex should be 2, got %d", composer.focusIndex) + } + + // Simulate pressing Tab again to move to the 'Send' button. + model, _ = composer.Update(tea.KeyMsg{Type: tea.KeyTab}) + composer = model.(Composer) + if composer.focusIndex != 3 { + t.Errorf("After three Tabs, focusIndex should be 3 (Send), got %d", composer.focusIndex) + } + + // Simulate one more Tab to wrap around to the 'To' field. + model, _ = composer.Update(tea.KeyMsg{Type: tea.KeyTab}) + composer = model.(Composer) + if composer.focusIndex != 0 { + t.Errorf("After four Tabs, focusIndex should wrap to 0, got %d", composer.focusIndex) + } + }) + + t.Run("Send email message", func(t *testing.T) { + // Set values for the email fields. + composer.toInput.SetValue("recipient@example.com") + composer.subjectInput.SetValue("Test Subject") + composer.bodyInput.SetValue("This is the body.") + // Set focus to the Send button. + composer.focusIndex = 3 + + // Simulate pressing Enter to send the email. + _, cmd := composer.Update(tea.KeyMsg{Type: tea.KeyEnter}) + if cmd == nil { + t.Fatal("Expected a command to be returned, but got nil.") + } + + // Execute the command and check the resulting message. + msg := cmd() + sendMsg, ok := msg.(SendEmailMsg) + if !ok { + t.Fatalf("Expected a SendEmailMsg, but got %T", msg) + } + + // Verify the content of the message. + expectedMsg := SendEmailMsg{ + To: "recipient@example.com", + Subject: "Test Subject", + Body: "This is the body.", + } + if !reflect.DeepEqual(sendMsg, expectedMsg) { + t.Errorf("Mismatched SendEmailMsg.\nGot: %+v\nWant: %+v", sendMsg, expectedMsg) + } + }) +} diff --git a/tui/inbox_test.go b/tui/inbox_test.go new file mode 100644 index 0000000000000000000000000000000000000000..e0ff3b41c0175a227f8220e442a0bf2edf9da9e6 --- /dev/null +++ b/tui/inbox_test.go @@ -0,0 +1,46 @@ +package tui + +import ( + "testing" + "time" + + "github.com/andrinoff/email-cli/fetcher" + tea "github.com/charmbracelet/bubbletea" +) + +// TestInboxUpdate verifies the state transitions in the inbox view. +func TestInboxUpdate(t *testing.T) { + // Create a sample list of emails. + sampleEmails := []fetcher.Email{ + {From: "a@example.com", Subject: "Email 1", Date: time.Now()}, + {From: "b@example.com", Subject: "Email 2", Date: time.Now().Add(-time.Hour)}, + {From: "c@example.com", Subject: "Email 3", Date: time.Now().Add(-2 * time.Hour)}, + } + + inbox := NewInbox(sampleEmails) + + t.Run("Select email to view", func(t *testing.T) { + // By default, the first item is selected (index 0). + // Move down to the second item (index 1). + inbox.list, _ = inbox.list.Update(tea.KeyMsg{Type: tea.KeyDown}) + + // Simulate pressing Enter to view the selected email. + _, cmd := inbox.Update(tea.KeyMsg{Type: tea.KeyEnter}) + if cmd == nil { + t.Fatal("Expected a command, but got nil.") + } + + // Check the resulting message. + msg := cmd() + viewMsg, ok := msg.(ViewEmailMsg) + if !ok { + t.Fatalf("Expected a ViewEmailMsg, but got %T", msg) + } + + // The index should match the selected item in the list. + expectedIndex := 1 + if viewMsg.Index != expectedIndex { + t.Errorf("Expected index %d, but got %d", expectedIndex, viewMsg.Index) + } + }) +}