okami.mom/albedo
git clone https://git.nkp.pet/okami.mom/albedo
7f83187
main
1// Transcript re-rendering on width changes, not height changes, prevents 2// expensive UI refreshes. The cost is invisible outside the process: the 3// rendered text is identical either way, so no e2e can catch a regression. 4// The sidebar's stacking, width, row folding and the stale-kernel note are 5// checked here too: a daemon run would need a live job, an active work item and a stale 6// kernel at once to show them. 7package tui 8 9import ( 10"strings" 11"testing" 12 13"albedo/cli/internal/daemon" 14"albedo/cli/internal/daemon/protocol" 15 16"github.com/charmbracelet/x/ansi" 17) 18 19func TestResizeRendersTranscriptOnlyForNewBodyWidth (t * testing.T ) { 20m := newTestChatModel (t ,& daemon.Session {ID :"s" }) 21m .SetSize (140 ,24 ) 22m .appendSettledEntry (HistoryEntry {Kind :EntryAssistant ,Speaker :"albedo" ,Text :"hello" }) 23const kept = "rendered once" 24m .settledLines [0 ]= kept 25 26m .Glances = []PageGlance {{Title :"pending work" ,Rows : []PageRow {{ID :"1" ,Text :"x" }}}} 27m .SetSize (140 ,30 ) 28if m .settledLines [0 ]!= kept { 29t .Fatal ("a resize keeping the body width rendered the transcript again" ) 30 } 31m .SetSize (80 ,30 ) 32if m .settledLines [0 ]== kept { 33t .Fatal ("a narrower body should render the transcript again" ) 34 } 35} 36 37func TestSidebarStacksActiveWorkAboveBackgroundJobs (t * testing.T ) { 38m := newTestChatModel (t ,& daemon.Session {ID :"s" }) 39m .Glances = []PageGlance {{Title :"active work" ,Rows : []PageRow {{ID :"7" ,Text :"fix the parser" ,Tone :ToneActive }}}} 40m .Status .RunningJobs = []protocol.KernelJob {{ID :"job1" ,Command :"go test ./..." }, {ID :"job2" ,Command :"python3 -c '\nimport os\nprint(1)\n'" }} 41m .SetSize (160 ,30 ) 42view := ansi .Strip (m .View ()) 43work ,jobs := strings .Index (view ,"active work" ),strings .Index (view ,"background jobs" ) 44if work < 0 || jobs < work || ! strings .Contains (view ,"fix the parser" )|| ! strings .Contains (view ,"go test ./..." ) { 45t .Fatalf ("the sidebar must show active work, then background jobs:\n%s" ,view ) 46 } 47if strings .Contains (view ,"job1" ) { 48t .Fatalf ("a background job shows its command, not its id:\n%s" ,view ) 49 } 50if counts := m .glanceCounts ();len (counts )!= 0 { 51t .Fatalf ("the footer repeats sections the sidebar shows: %v" ,counts ) 52 } 53if ! strings .Contains (view ,"python3 -c ' import os" ) { 54t .Fatalf ("a multi-line command must fold onto its row:\n%s" ,view ) 55 } 56m .Glances ,m .Status .RunningJobs = nil ,m .Status .RunningJobs [1 :] 57m .Status .KernelJobs = new (int64 (1 )) 58m .SetSize (60 ,20 ) 59for line := range strings .SplitSeq (ansi .Strip (m .View ()),"\n" ) { 60if strings .Contains (line ,"import os" )&& ! strings .Contains (line ,"background job running" ) { 61t .Fatalf ("the status line wrapped a job's command:\n%s" ,ansi .Strip (m .View ())) 62 } 63if ansi .StringWidth (line )> 60 { 64t .Fatalf ("a %d-cell line at width 60: %q" ,ansi .StringWidth (line ),line ) 65 } 66 } 67} 68 69func TestSidebarFoldsAMultiLineRowOntoOneLine (t * testing.T ) { 70m := newTestChatModel (t ,& daemon.Session {ID :"s" }) 71m .Glances = []PageGlance {{Title :"scheduled" ,Rows : []PageRow {{ID :"3" ,Text :"every 1h · ci\nlog" }}}} 72m .SetSize (160 ,30 ) 73if view := ansi .Strip (m .View ());! strings .Contains (view ,"#3 every 1h · ci log" ) { 74t .Fatalf ("a scheduled prompt's lines must fold onto its row:\n%s" ,view ) 75 } 76} 77 78func TestSidebarFitsBesideANarrowerTranscript (t * testing.T ) { 79m := newTestChatModel (t ,& daemon.Session {ID :"s" }) 80m .Glances = []PageGlance {{Title :"active work" ,Rows : []PageRow {{ID :"7" ,Text :"fix the parser" ,Tone :ToneActive }}}} 81m .SetSize (110 ,30 ) 82view := ansi .Strip (m .View ()) 83if ! strings .Contains (view ,"fix the parser" ) { 84t .Fatalf ("a 110-column terminal must show the sidebar:\n%s" ,view ) 85 } 86for line := range strings .SplitSeq (view ,"\n" ) { 87if ansi .StringWidth (line )> 110 { 88t .Fatalf ("a %d-cell line at width 110: %q" ,ansi .StringWidth (line ),line ) 89 } 90 } 91// without glances the transcript takes the width back 92m .Glances = nil 93m .SetSize (110 ,30 ) 94if m .sidebarWidth ()!= 0 || m .Renderer .BodyWidth != 100 { 95t .Fatalf ("no sidebar must leave the full body: sidebar %d body %d" ,m .sidebarWidth (),m .Renderer .BodyWidth ) 96 } 97} 98 99func TestAStaleKernelIsNamedOnlyWhileAJobKeepsIt (t * testing.T ) { 100m := newTestChatModel (t ,& daemon.Session {ID :"s" }) 101m .Status .KernelStale = true 102if counts := m .glanceCounts ();len (counts )!= 0 { 103t .Fatalf ("a stale kernel swaps before the next turn; nothing to say: %v" ,counts ) 104 } 105m .Status .KernelJobs = new (int64 (1 )) 106if counts := m .glanceCounts ();len (counts )== 0 || counts [0 ]!= "kernel older until jobs end" { 107t .Fatalf ("a job keeping a stale kernel must be named: %v" ,counts ) 108 } 109}