package tui import ( "strings" "testing" tea "github.com/charmbracelet/bubbletea" ) func TestConfirmModel_DefaultsToYes(t *testing.T) { m := confirmModel{prompt: "Download test?", yes: true} if !m.yes { t.Error("should default to yes") } } func TestConfirmModel_View_ContainsPrompt(t *testing.T) { m := confirmModel{prompt: "Download qwen3:8b?", yes: true} got := m.View() if !strings.Contains(got, "Download qwen3:8b?") { t.Error("should contain the prompt text") } } func TestConfirmModel_View_ContainsButtons(t *testing.T) { m := confirmModel{prompt: "Download?", yes: true} got := m.View() if !strings.Contains(got, "Yes") { t.Error("should contain Yes button") } if !strings.Contains(got, "No") { t.Error("should contain No button") } } func TestConfirmModel_View_ContainsCustomButtons(t *testing.T) { m := confirmModel{ prompt: "Connect a messaging app now?", yesLabel: "Yes", noLabel: "Set up later", yes: true, } got := m.View() if !strings.Contains(got, "Yes") { t.Error("should contain custom yes button") } if !strings.Contains(got, "Set up later") { t.Error("should contain custom no button") } } func TestConfirmModel_View_ContainsHelp(t *testing.T) { m := confirmModel{prompt: "Download?", yes: true} got := m.View() if !strings.Contains(got, "enter confirm") { t.Error("should contain help text") } } func TestConfirmModel_View_ClearsAfterConfirm(t *testing.T) { m := confirmModel{prompt: "Download?", confirmed: true} if m.View() != "" { t.Error("View should return empty string after confirmation") } } func TestConfirmModel_View_ClearsAfterCancel(t *testing.T) { m := confirmModel{prompt: "Download?", cancelled: true} if m.View() != "" { t.Error("View should return empty string after cancellation") } } func TestConfirmModel_EnterConfirmsYes(t *testing.T) { m := confirmModel{prompt: "Download?", yes: true} updated, cmd := m.Update(tea.KeyMsg{Type: tea.KeyEnter}) fm := updated.(confirmModel) if !fm.confirmed { t.Error("enter should set confirmed=true") } if !fm.yes { t.Error("enter with yes selected should keep yes=true") } if cmd == nil { t.Error("enter should return tea.Quit") } } func TestConfirmModel_EnterConfirmsNo(t *testing.T) { m := confirmModel{prompt: "Download?", yes: false} updated, cmd := m.Update(tea.KeyMsg{Type: tea.KeyEnter}) fm := updated.(confirmModel) if !fm.confirmed { t.Error("enter should set confirmed=true") } if fm.yes { t.Error("enter with no selected should keep yes=false") } if cmd == nil { t.Error("enter should return tea.Quit") } } func TestConfirmModel_EscCancels(t *testing.T) { m := confirmModel{prompt: "Download?", yes: true} updated, cmd := m.Update(tea.KeyMsg{Type: tea.KeyEsc}) fm := updated.(confirmModel) if !fm.cancelled { t.Error("esc should set cancelled=true") } if cmd == nil { t.Error("esc should return tea.Quit") } } func TestConfirmModel_CtrlCCancels(t *testing.T) { m := confirmModel{prompt: "Download?", yes: true} updated, cmd := m.Update(tea.KeyMsg{Type: tea.KeyCtrlC}) fm := updated.(confirmModel) if !fm.cancelled { t.Error("ctrl+c should set cancelled=true") } if cmd == nil { t.Error("ctrl+c should return tea.Quit") } } func TestConfirmModel_NDoesNothing(t *testing.T) { m := confirmModel{prompt: "Download?", yes: true} updated, cmd := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'n'}}) fm := updated.(confirmModel) if fm.cancelled { t.Error("'n' should not cancel") } if fm.confirmed { t.Error("'n' should not confirm") } if cmd != nil { t.Error("'n' should not quit") } } func TestConfirmModel_YDoesNothing(t *testing.T) { m := confirmModel{prompt: "Download?", yes: false} updated, cmd := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'y'}}) fm := updated.(confirmModel) if fm.confirmed { t.Error("'y' should not confirm") } if fm.yes { t.Error("'y' should not change selection") } if cmd != nil { t.Error("'y' should not quit") } } func TestConfirmModel_ArrowKeysNavigate(t *testing.T) { m := confirmModel{prompt: "Download?", yes: true} // Right moves to No updated, _ := m.Update(tea.KeyMsg{Type: tea.KeyRight}) fm := updated.(confirmModel) if fm.yes { t.Error("right should move to No") } if fm.confirmed || fm.cancelled { t.Error("navigation should not confirm or cancel") } // Left moves back to Yes updated, _ = fm.Update(tea.KeyMsg{Type: tea.KeyLeft}) fm = updated.(confirmModel) if !fm.yes { t.Error("left should move to Yes") } } func TestConfirmModel_TabDoesNothing(t *testing.T) { m := confirmModel{prompt: "Download?", yes: true} updated, _ := m.Update(tea.KeyMsg{Type: tea.KeyTab}) fm := updated.(confirmModel) if !fm.yes { t.Error("tab should not change selection") } if fm.confirmed || fm.cancelled { t.Error("tab should not confirm or cancel") } } func TestConfirmModel_WindowSizeUpdatesWidth(t *testing.T) { m := confirmModel{prompt: "Download?"} updated, _ := m.Update(tea.WindowSizeMsg{Width: 100, Height: 40}) fm := updated.(confirmModel) if fm.width != 100 { t.Errorf("expected width 100, got %d", fm.width) } } func TestConfirmModel_ResizeEntersAltScreen(t *testing.T) { m := confirmModel{prompt: "Download?", width: 80} _, cmd := m.Update(tea.WindowSizeMsg{Width: 100, Height: 40}) if cmd == nil { t.Error("resize (width already set) should return a command") } } func TestConfirmModel_InitialWindowSizeNoAltScreen(t *testing.T) { m := confirmModel{prompt: "Download?"} _, cmd := m.Update(tea.WindowSizeMsg{Width: 80, Height: 40}) if cmd != nil { t.Error("initial WindowSizeMsg should not return a command") } } func TestConfirmModel_ViewMaxWidth(t *testing.T) { m := confirmModel{prompt: "Download?", yes: true, width: 40} got := m.View() // Just ensure it doesn't panic and returns content if got == "" { t.Error("View with width set should still return content") } }