feat(tui-go): idea board viewer
OverlayIdeas (opened with I, global/cross-session) lists the idea board in the artifact-viewer shell: ↑↓ to move, x/d to remove (sends DiscardIdea, optimistic drop), esc to close. Pull-based via ListIdeas -> idea.list.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"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(tea.KeyMsg{Type: tea.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())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user