okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
78011da
main
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 { 17t .Helper () 18m := NewTreePickerModel (nil ,"s" ) 19m .SetSize (120 ,30 ) 20m ,_ = 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 }}) 24if m .Loading || len (m .Checkpoints )!= 2 { 25t .Fatal ("fixture checkpoints did not load" ) 26 } 27return m 28} 29 30func TestTreeFilterAndDetailWithoutEnter (t * testing.T ) { 31m := treeFixture (t ) 32for _ ,r := range "adapter" { 33m ,_ = m .Update (tea.KeyPressMsg {Code :r ,Text :string (r )}) 34 } 35view := ansi .Strip (m .View ()) 36if ! strings .Contains (view ,"wrote the adapter tests" )|| strings .Contains (view ,"fix the parser" ) { 37t .Fatalf ("typing should filter the checkpoints:\n%s" ,view ) 38 } 39if ! strings .Contains (view ,"fresh Python namespace." ) { 40t .Fatalf ("the branch detail should show beside the list without enter:\n%s" ,view ) 41 } 42if row ,_ := m .highlighted ();row .key != "b" { 43t .Fatalf ("the filter left %q under the cursor" ,row .key ) 44 } 45} 46 47func TestTreeAsksBeforeBranchingWithEnterAndEsc (t * testing.T ) { 48m := treeFixture (t ) 49m ,_ = m .Update (tea.KeyPressMsg {Code :tea .KeyEnter }) 50if ! m .Confirming ()|| ! strings .Contains (ansi .Strip (m .View ()),"Branch after user · fix the parser?" ) { 51t .Fatalf ("enter should ask before branching:\n%s" ,ansi .Strip (m .View ())) 52 } 53m ,cmd := m .Update (tea.KeyPressMsg {Code :'y' ,Text :"y" }) 54if cmd != nil || ! m .Confirming ()|| m .Forking { 55t .Fatal ("a stray key answered the branch question" ) 56 } 57m ,cmd = m .Update (tea.KeyPressMsg {Code :tea .KeyEscape }) 58if cmd != nil || m .Confirming () { 59t .Fatal ("esc should cancel the question, not leave the picker" ) 60 } 61 62m ,_ = m .Update (tea.KeyPressMsg {Code :tea .KeyEnter }) 63m ,cmd = m .Update (tea.KeyPressMsg {Code :tea .KeyEnter }) 64if ! m .Forking || cmd == nil { 65t .Fatal ("the second enter should start the branch" ) 66 } 67} 68 69func TestTreeForkFailureKeepsTheQuestionForRetry (t * testing.T ) { 70m := treeFixture (t ) 71m ,_ = m .Update (tea.KeyPressMsg {Code :tea .KeyEnter }) 72m ,_ = m .Update (tea.KeyPressMsg {Code :tea .KeyEnter }) 73m ,_ = m .Update (treeForkedMsg {Gen :m .Generation ,Err :errors .New ("branch refused" )}) 74view := ansi .Strip (m .View ()) 75if m .Forking || ! m .Confirming ()|| ! strings .Contains (view ,"branch refused" )|| ! strings .Contains (view ,"retry" ) { 76t .Fatalf ("a failed branch should keep the question open with retry:\n%s" ,view ) 77 } 78}