diff --git a/internal/tui/components/alert_test.go b/internal/tui/components/alert_test.go index 2f8c50c..fc055bb 100644 --- a/internal/tui/components/alert_test.go +++ b/internal/tui/components/alert_test.go @@ -1,13 +1,13 @@ package components_test import ( - "strings" "testing" "tusshi/internal/tui/components" "tusshi/internal/tui/theme" tea "github.com/charmbracelet/bubbletea" + "github.com/stretchr/testify/assert" ) func TestAlertComponent(t *testing.T) { @@ -18,35 +18,30 @@ func TestAlertComponent(t *testing.T) { Theme: theme.Mock, } - if cmd := alert.Init(); cmd != nil { - t.Errorf("expected Init to return nil, got %v", cmd) - } + t.Run("init", func(t *testing.T) { + assert.Nil(t, alert.Init()) + }) - rendered := alert.View(50) - if !strings.Contains(rendered, "Backup Failed") { - t.Error("expected view to contain title") - } - if !strings.Contains(rendered, "Could not create initial pre-tuSSHi backup") { - t.Error("expected view to contain message") - } - if !strings.Contains(rendered, "OK") { - t.Error("expected view to contain OK button") - } + t.Run("view", func(t *testing.T) { + rendered := alert.View(50) + assert.Contains(t, rendered, "Backup Failed") + assert.Contains(t, rendered, "Could not create initial pre-tuSSHi backup") + assert.Contains(t, rendered, "OK") + }) - _, done := alert.Update(tea.KeyMsg{Type: tea.KeyEsc}) - if !done { - t.Error("expected Esc to dismiss alert") - } - _, done = alert.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("q")}) - if !done { - t.Error("expected 'q' to dismiss alert") - } - _, done = alert.Update(tea.KeyMsg{Type: tea.KeyEnter}) - if !done { - t.Error("expected Enter to dismiss alert") - } - _, done = alert.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("x")}) - if done { - t.Error("expected other keys to not dismiss alert") - } + t.Run("dismiss keys", func(t *testing.T) { + _, done := alert.Update(tea.KeyMsg{Type: tea.KeyEsc}) + assert.True(t, done) + + _, done = alert.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("q")}) + assert.True(t, done) + + _, done = alert.Update(tea.KeyMsg{Type: tea.KeyEnter}) + assert.True(t, done) + }) + + t.Run("non-dismiss keys", func(t *testing.T) { + _, done := alert.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("x")}) + assert.False(t, done) + }) } diff --git a/internal/tui/components/confirm_test.go b/internal/tui/components/confirm_test.go index c23f537..b821e07 100644 --- a/internal/tui/components/confirm_test.go +++ b/internal/tui/components/confirm_test.go @@ -1,13 +1,13 @@ package components_test import ( - "strings" "testing" "tusshi/internal/tui/components" "tusshi/internal/tui/theme" tea "github.com/charmbracelet/bubbletea" + "github.com/stretchr/testify/assert" ) func TestConfirmComponent(t *testing.T) { @@ -22,56 +22,40 @@ func TestConfirmComponent(t *testing.T) { }, } - if c.Title != "Test Confirm" { - t.Errorf("expected Title 'Test Confirm', got %q", c.Title) - } + t.Run("initial state", func(t *testing.T) { + assert.Equal(t, "Test Confirm", c.Title) + assert.False(t, c.YesSelected) + }) - if c.YesSelected { - t.Error("expected YesSelected to be false by default") - } + t.Run("move left", func(t *testing.T) { + _, done := c.Update(tea.KeyMsg{Type: tea.KeyLeft}) + assert.False(t, done) + assert.True(t, c.YesSelected) + }) - // Move left - _, done := c.Update(tea.KeyMsg{Type: tea.KeyLeft}) - if done { - t.Error("expected navigation to not finalize selection") - } - if !c.YesSelected { - t.Error("expected YesSelected to be true after left key press") - } - - // Move right - _, done = c.Update(tea.KeyMsg{Type: tea.KeyRight}) - if done { - t.Error("expected navigation to not finalize selection") - } - if c.YesSelected { - t.Error("expected YesSelected to be false after right key press") - } + t.Run("move right", func(t *testing.T) { + _, done := c.Update(tea.KeyMsg{Type: tea.KeyRight}) + assert.False(t, done) + assert.False(t, c.YesSelected) + }) - // Confirm 'No' - _, done = c.Update(tea.KeyMsg{Type: tea.KeyEnter}) - if !done { - t.Error("expected done to be true after enter key press") - } - if confirmedCalled { - t.Error("expected confirmedCalled to be false since 'No' was focused") - } + t.Run("confirm no", func(t *testing.T) { + _, done := c.Update(tea.KeyMsg{Type: tea.KeyEnter}) + assert.True(t, done) + assert.False(t, confirmedCalled) + }) - // Select Yes and Confirm 'Yes' - c.YesSelected = true - _, done = c.Update(tea.KeyMsg{Type: tea.KeyEnter}) - if !done { - t.Error("expected done to be true after enter key press on Yes") - } - if !confirmedCalled { - t.Error("expected confirmedCalled to be true since 'Yes' was focused") - } + t.Run("select yes and confirm yes", func(t *testing.T) { + c.YesSelected = true + _, done := c.Update(tea.KeyMsg{Type: tea.KeyEnter}) + assert.True(t, done) + assert.True(t, confirmedCalled) + }) - // Esc test - _, done = c.Update(tea.KeyMsg{Type: tea.KeyEsc}) - if !done { - t.Error("expected done to be true after esc key press") - } + t.Run("esc close", func(t *testing.T) { + _, done := c.Update(tea.KeyMsg{Type: tea.KeyEsc}) + assert.True(t, done) + }) } func TestConfirmCustomLabels(t *testing.T) { @@ -79,18 +63,18 @@ func TestConfirmCustomLabels(t *testing.T) { Theme: theme.Mock, } - // Verify defaults render when fields are left empty - viewEmpty := c.View(40) - if !strings.Contains(viewEmpty, " Yes ") || !strings.Contains(viewEmpty, " No ") { - t.Error("expected view to render default button labels when empty") - } + t.Run("default labels", func(t *testing.T) { + viewEmpty := c.View(40) + assert.Contains(t, viewEmpty, " Yes ") + assert.Contains(t, viewEmpty, " No ") + }) - // Apply Option 2 (direct mutation) - c.YesStr = " Delete " - c.NoStr = " Cancel " + t.Run("custom labels", func(t *testing.T) { + c.YesStr = " Delete " + c.NoStr = " Cancel " - viewCustom := c.View(40) - if !strings.Contains(viewCustom, " Delete ") || !strings.Contains(viewCustom, " Cancel ") { - t.Error("expected view to render custom button labels after mutation") - } + viewCustom := c.View(40) + assert.Contains(t, viewCustom, " Delete ") + assert.Contains(t, viewCustom, " Cancel ") + }) } diff --git a/internal/tui/components/form_test.go b/internal/tui/components/form_test.go index baf6d11..1212cb9 100644 --- a/internal/tui/components/form_test.go +++ b/internal/tui/components/form_test.go @@ -8,6 +8,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/huh" + "github.com/stretchr/testify/assert" ) func TestFormComponent(t *testing.T) { @@ -26,40 +27,33 @@ func TestFormComponent(t *testing.T) { }, } - // Test Init - cmd := f.Init() - if cmd == nil { - t.Error("expected Init to return a non-nil command for huh form initialization") - } - - // Test View rendering - rendered := f.View(40) - if rendered == "" { - t.Error("expected View to return a non-empty string representation of the form") - } - - // Test cancellation key: Esc - _, done := f.Update(tea.KeyMsg{Type: tea.KeyEsc}) - if !done { - t.Error("expected Esc to finish/cancel the form component") - } - - // Test manual completed state transition to trigger callback - f.Form.State = huh.StateCompleted - _, done = f.Update(nil) - if !done { - t.Error("expected completed form state to return done = true") - } - if !submitted { - t.Error("expected OnSubmit callback to be executed on form completion") - } - - // Test manual aborted state transition - f.Form.State = huh.StateAborted - _, done = f.Update(nil) - if !done { - t.Error("expected aborted form state to return done = true") - } + t.Run("init", func(t *testing.T) { + cmd := f.Init() + assert.NotNil(t, cmd) + }) + + t.Run("view rendering", func(t *testing.T) { + rendered := f.View(40) + assert.NotEmpty(t, rendered) + }) + + t.Run("cancellation", func(t *testing.T) { + _, done := f.Update(tea.KeyMsg{Type: tea.KeyEsc}) + assert.True(t, done) + }) + + t.Run("completed state transition", func(t *testing.T) { + f.Form.State = huh.StateCompleted + _, done := f.Update(nil) + assert.True(t, done) + assert.True(t, submitted) + }) + + t.Run("aborted state transition", func(t *testing.T) { + f.Form.State = huh.StateAborted + _, done := f.Update(nil) + assert.True(t, done) + }) } func TestFormValidationAndSubmission(t *testing.T) { @@ -85,36 +79,24 @@ func TestFormValidationAndSubmission(t *testing.T) { }, } - // Part 1: Validation fails - f.Form.State = huh.StateNormal - validationErr = errors.New("invalid field") - _, done := f.Update(tea.KeyMsg{Type: tea.KeyCtrlS}) - if done { - t.Error("expected form to not finalize when validation fails") - } - if !validated { - t.Error("expected validation function to be called") - } - if submitted { - t.Error("expected form not to submit when validation fails") - } - - // Part 2: validation succeeds - validated = false - submitted = false - - validationErr = nil - _, done = f.Update(tea.KeyMsg{Type: tea.KeyCtrlS}) - if !done { - t.Error("expected form to finalize when validation succeeds") - } - if !validated { - t.Error("expected validation function to be called") - } - if !submitted { - t.Error("expected form to submit when validation succeeds") - } - if f.Form.State != huh.StateCompleted { - t.Errorf("expected form state to be Completed, got %v", f.Form.State) - } + t.Run("validation fails", func(t *testing.T) { + f.Form.State = huh.StateNormal + validationErr = errors.New("invalid field") + _, done := f.Update(tea.KeyMsg{Type: tea.KeyCtrlS}) + assert.False(t, done) + assert.True(t, validated) + assert.False(t, submitted) + }) + + t.Run("validation succeeds", func(t *testing.T) { + validated = false + submitted = false + validationErr = nil + + _, done := f.Update(tea.KeyMsg{Type: tea.KeyCtrlS}) + assert.True(t, done) + assert.True(t, validated) + assert.True(t, submitted) + assert.Equal(t, huh.StateCompleted, f.Form.State) + }) } diff --git a/internal/tui/components/help_test.go b/internal/tui/components/help_test.go index cf2e6db..fc437b6 100644 --- a/internal/tui/components/help_test.go +++ b/internal/tui/components/help_test.go @@ -1,13 +1,13 @@ package components_test import ( - "strings" "testing" "tusshi/internal/tui/components" "tusshi/internal/tui/theme" tea "github.com/charmbracelet/bubbletea" + "github.com/stretchr/testify/assert" ) func TestHelpComponent(t *testing.T) { @@ -21,44 +21,32 @@ func TestHelpComponent(t *testing.T) { Theme: theme.Mock, } - // Test Init - if cmd := h.Init(); cmd != nil { - t.Error("expected Init to return nil") - } + t.Run("init", func(t *testing.T) { + assert.Nil(t, h.Init()) + }) - // Test View rendering - rendered := h.View(50) - if !strings.Contains(rendered, "Available Commands") { - t.Error("expected view to contain header") - } - if !strings.Contains(rendered, "j/k") || !strings.Contains(rendered, "Navigate list") { - t.Error("expected view to contain 'j/k' shortcut and description") - } - if !strings.Contains(rendered, "a") || !strings.Contains(rendered, "Add host") { - t.Error("expected view to contain 'a' shortcut and description") - } + t.Run("view", func(t *testing.T) { + rendered := h.View(50) + assert.Contains(t, rendered, "Available Commands") + assert.Contains(t, rendered, "j/k") + assert.Contains(t, rendered, "Navigate list") + assert.Contains(t, rendered, "a") + assert.Contains(t, rendered, "Add host") + }) - // Test closing keys: Esc - _, done := h.Update(tea.KeyMsg{Type: tea.KeyEsc}) - if !done { - t.Error("expected Esc to finish help dialog") - } + t.Run("closing keys", func(t *testing.T) { + _, done := h.Update(tea.KeyMsg{Type: tea.KeyEsc}) + assert.True(t, done) - // Test closing keys: q - _, done = h.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("q")}) - if !done { - t.Error("expected 'q' to finish help dialog") - } + _, done = h.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("q")}) + assert.True(t, done) - // Test closing keys: Enter - _, done = h.Update(tea.KeyMsg{Type: tea.KeyEnter}) - if !done { - t.Error("expected Enter to finish help dialog") - } + _, done = h.Update(tea.KeyMsg{Type: tea.KeyEnter}) + assert.True(t, done) + }) - // Test non-closing keys - _, done = h.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("x")}) - if done { - t.Error("expected other keys to not finish help dialog") - } + t.Run("non-closing keys", func(t *testing.T) { + _, done := h.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("x")}) + assert.False(t, done) + }) }