package app import ( "encoding/json" "strings" "testing" "github.com/correx/tui-go/internal/protocol" "github.com/correx/tui-go/internal/ws" ) func ideaModel() (Model, *ws.Client) { client := ws.New("", 0) // unconnected; Send buffers, Drain reads it back m := NewModel(client) m.width, m.height = 120, 40 m.theme = NewTheme(SoftBlue) m.openIdeas() // sets OverlayIdeas + sends a ListIdeas frame m.applyServer(protocol.ServerMessage{ Type: protocol.TypeIdeaList, Ideas: []protocol.IdeaDto{ {IdeaID: "i1", Text: "cache the repo map", CapturedAtMs: 1000}, {IdeaID: "i2", Text: "add --dry-run", CapturedAtMs: 2000}, }, }) return m, client } func TestIdeaBoardOpensAndRenders(t *testing.T) { m, _ := ideaModel() if m.overlay != OverlayIdeas { t.Fatalf("want OverlayIdeas, got %v", m.overlay) } out := m.ideasModal() for _, want := range []string{"ideas", "cache the repo map", "add --dry-run"} { if !strings.Contains(out, want) { t.Fatalf("board missing %q:\n%s", want, out) } } } func TestIdeaRemoveSendsDiscardAndDropsRow(t *testing.T) { m, client := ideaModel() _ = client.Drain() // drop the ListIdeas frame from openIdeas updated, _ := m.handleOverlayKey(keyMsg{Type: keyRunes, Runes: []rune("x")}) m = updated.(Model) if len(m.ideas) != 1 || m.ideas[0].IdeaID != "i2" { t.Fatalf("expected i1 removed, got %+v", m.ideas) } frames := client.Drain() if len(frames) != 1 { t.Fatalf("expected one DiscardIdea frame, got %d", len(frames)) } var raw map[string]any if err := json.Unmarshal(frames[0], &raw); err != nil { t.Fatalf("unmarshal: %v", err) } if raw["type"] != "com.correx.apps.server.protocol.ClientMessage.DiscardIdea" || raw["ideaId"] != "i1" { t.Fatalf("expected DiscardIdea i1, got %+v", raw) } } func TestIdeaBoardEmptyState(t *testing.T) { m, _ := ideaModel() m.applyServer(protocol.ServerMessage{Type: protocol.TypeIdeaList, Ideas: nil}) if !strings.Contains(m.ideasModal(), "no ideas yet") { t.Fatalf("expected empty state, got:\n%s", m.ideasModal()) } }