Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 25 additions & 30 deletions internal/tui/components/alert_test.go
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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)
})
}
100 changes: 42 additions & 58 deletions internal/tui/components/confirm_test.go
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -22,75 +22,59 @@ 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) {
c := &components.Confirm{
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 ")
})
}
114 changes: 48 additions & 66 deletions internal/tui/components/form_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/huh"
"github.com/stretchr/testify/assert"
)

func TestFormComponent(t *testing.T) {
Expand All @@ -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) {
Expand All @@ -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)
})
}
Loading
Loading