okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

dawntui: tree and context inspector on listView, shared confirm for agent delete78011da

main
2.7 KiB78 linesraw
1// Checkpoint picking filters by typing, shows each checkpoint's detail beside
2// the list, and asks before branching with enter and esc only. E2E cannot see
3// the question or the stray-key handling, so these assert the model directly.
4package tui
5
6import (
7	"albedo/cli/internal/daemon"
8	"errors"
9	"strings"
10	"testing"
11
12	tea "charm.land/bubbletea/v2"
13	"github.com/charmbracelet/x/ansi"
14)
15
16func treeFixture(t *testing.T) TreePickerModel {
17	t.Helper()
18	m := NewTreePickerModel(nil, "s")
19	m.SetSize(120, 30)
20	m, _ = m.Update(treeLoadedMsg{Gen: m.Generation, Items: []daemon.TreeCheckpoint{
21		{ID: "a", Type: "user", Preview: "fix the parser"},
22		{ID: "b", Type: "assistant", Preview: "wrote the adapter tests"},
23	}})
24	if m.Loading || len(m.Checkpoints) != 2 {
25		t.Fatal("fixture checkpoints did not load")
26	}
27	return m
28}
29
30func TestTreeFilterAndDetailWithoutEnter(t *testing.T) {
31	m := treeFixture(t)
32	for _, r := range "adapter" {
33		m, _ = m.Update(tea.KeyPressMsg{Code: r, Text: string(r)})
34	}
35	view := ansi.Strip(m.View())
36	if !strings.Contains(view, "wrote the adapter tests") || strings.Contains(view, "fix the parser") {
37		t.Fatalf("typing should filter the checkpoints:\n%s", view)
38	}
39	if !strings.Contains(view, "fresh Python namespace.") {
40		t.Fatalf("the branch detail should show beside the list without enter:\n%s", view)
41	}
42	if row, _ := m.highlighted(); row.key != "b" {
43		t.Fatalf("the filter left %q under the cursor", row.key)
44	}
45}
46
47func TestTreeAsksBeforeBranchingWithEnterAndEsc(t *testing.T) {
48	m := treeFixture(t)
49	m, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
50	if !m.Confirming() || !strings.Contains(ansi.Strip(m.View()), "Branch after user · fix the parser?") {
51		t.Fatalf("enter should ask before branching:\n%s", ansi.Strip(m.View()))
52	}
53	m, cmd := m.Update(tea.KeyPressMsg{Code: 'y', Text: "y"})
54	if cmd != nil || !m.Confirming() || m.Forking {
55		t.Fatal("a stray key answered the branch question")
56	}
57	m, cmd = m.Update(tea.KeyPressMsg{Code: tea.KeyEscape})
58	if cmd != nil || m.Confirming() {
59		t.Fatal("esc should cancel the question, not leave the picker")
60	}
61
62	m, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
63	m, cmd = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
64	if !m.Forking || cmd == nil {
65		t.Fatal("the second enter should start the branch")
66	}
67}
68
69func TestTreeForkFailureKeepsTheQuestionForRetry(t *testing.T) {
70	m := treeFixture(t)
71	m, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
72	m, _ = m.Update(tea.KeyPressMsg{Code: tea.KeyEnter})
73	m, _ = m.Update(treeForkedMsg{Gen: m.Generation, Err: errors.New("branch refused")})
74	view := ansi.Strip(m.View())
75	if m.Forking || !m.Confirming() || !strings.Contains(view, "branch refused") || !strings.Contains(view, "retry") {
76		t.Fatalf("a failed branch should keep the question open with retry:\n%s", view)
77	}
78}