package tui import ( "fmt" "strings" "testing" "github.com/charmbracelet/bubbles/spinner" "github.com/charmbracelet/bubbles/textinput" "once-add/internal/config" ) // testModel builds a model without touching /etc, with components initialised // so View() never panics. func testModel() model { sp := spinner.New() sp.Spinner = spinner.Dot return model{ customInput: textinput.New(), nameInput: textinput.New(), spinner: sp, apps: []config.App{ {Name: "Writebook", Image: "ghcr.io/basecamp/writebook", Description: "Books", Suggested: "book"}, }, } } func TestViewAllStates(t *testing.T) { states := []state{ stateMenu, stateSelectApp, stateCustomImage, stateName, stateDeploying, stateRemovePick, stateRemoveConfirm, stateRemoving, stateDone, stateError, } for _, st := range states { m := testModel() m.state = st m.name = "book" m.image = "ghcr.io/basecamp/writebook" m.resultURL = "http://book.local" m.removeHosts = []string{"book.local", "chat.local"} m.removeTarget = "chat.local" m.doneMsg = "Removed chat.local" m.err = fmt.Errorf("boom") out := m.View() if !strings.Contains(out, "once-add") { t.Errorf("state %d: view missing title:\n%s", st, out) } } } func TestRemoveDoneWording(t *testing.T) { m := testModel() m.state = stateDone m.op = opRemove m.doneMsg = "Removed chat.local" if !strings.Contains(m.View(), "Removed chat.local") { t.Errorf("remove done view missing message:\n%s", m.View()) } } func TestEmptyRemovePick(t *testing.T) { m := testModel() m.state = stateRemovePick m.removeHosts = nil if !strings.Contains(m.View(), "No deployed") { t.Errorf("empty remove pick should say nothing to remove:\n%s", m.View()) } } func TestSelectShowsCustomRow(t *testing.T) { m := testModel() out := m.viewSelect() if !strings.Contains(out, "Writebook") || !strings.Contains(out, "Custom image") { t.Errorf("select view missing rows:\n%s", out) } }