127 lines
3.1 KiB
Go
127 lines
3.1 KiB
Go
package cli
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
func TestFrameLines(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
filtered int
|
|
termRows int
|
|
searching bool
|
|
wantLines int
|
|
}{
|
|
{
|
|
name: "small list no search",
|
|
filtered: 5,
|
|
termRows: 24,
|
|
searching: false,
|
|
wantLines: 4 + 5, // fixed(4) + items(5)
|
|
},
|
|
{
|
|
name: "small list with search",
|
|
filtered: 5,
|
|
termRows: 24,
|
|
searching: true,
|
|
wantLines: 5 + 5, // fixed(5 search) + items(5)
|
|
},
|
|
{
|
|
name: "list exceeds terminal height",
|
|
filtered: 100,
|
|
termRows: 24,
|
|
searching: false,
|
|
wantLines: 4 + 20, // fixed(4) + viewport(24-4=20)
|
|
},
|
|
{
|
|
name: "list exceeds terminal with search",
|
|
filtered: 100,
|
|
termRows: 24,
|
|
searching: true,
|
|
wantLines: 5 + 19, // fixed(5) + viewport(24-5=19)
|
|
},
|
|
{
|
|
name: "empty list",
|
|
filtered: 0,
|
|
termRows: 24,
|
|
searching: false,
|
|
wantLines: 4 + 0, // fixed(4) + viewport(0 items)
|
|
},
|
|
{
|
|
name: "tiny terminal",
|
|
filtered: 10,
|
|
termRows: 8,
|
|
searching: false,
|
|
wantLines: 4 + 4, // fixed(4) + viewport(8-4=4)
|
|
},
|
|
{
|
|
name: "tiny terminal with search",
|
|
filtered: 10,
|
|
termRows: 8,
|
|
searching: true,
|
|
wantLines: 5 + 3, // fixed(5) + viewport(8-5=3)
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := FrameLines(tt.filtered, tt.termRows, tt.searching)
|
|
if got != tt.wantLines {
|
|
t.Errorf("FrameLines(%d, %d, %v) = %d, want %d",
|
|
tt.filtered, tt.termRows, tt.searching, got, tt.wantLines)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFrameLinesNeverExceedsTerminal(t *testing.T) {
|
|
// The frame must never print more lines than the terminal has rows.
|
|
// Otherwise the terminal scrolls and cursor repositioning drifts.
|
|
termRows := 24
|
|
for _, searching := range []bool{false, true} {
|
|
for n := 0; n <= 200; n++ {
|
|
lines := FrameLines(n, termRows, searching)
|
|
if lines > termRows {
|
|
t.Errorf("FrameLines(%d, %d, searching=%v) = %d, exceeds terminal",
|
|
n, termRows, searching, lines)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMaxViewportBounds(t *testing.T) {
|
|
// Viewport must be at least 1 even on impossibly small terminals.
|
|
vp := maxViewport(10, 3, false)
|
|
if vp < 1 {
|
|
t.Errorf("maxViewport(10, 3, false) = %d, want >= 1", vp)
|
|
}
|
|
// When items < available rows, viewport equals items.
|
|
vp = maxViewport(3, 24, false)
|
|
if vp != 3 {
|
|
t.Errorf("maxViewport(3, 24, false) = %d, want 3", vp)
|
|
}
|
|
}
|
|
|
|
func TestFilterMenuItems(t *testing.T) {
|
|
items := []menuItem{
|
|
{name: "deepseek-v4", desc: "DeepSeek V4"},
|
|
{name: "gpt-4o", desc: "OpenAI GPT-4o"},
|
|
{name: "mimo-pro", desc: "MiMo Pro"},
|
|
}
|
|
// Empty query returns all.
|
|
if got := filterMenuItems(items, ""); len(got) != 3 {
|
|
t.Errorf("empty query: got %d, want 3", len(got))
|
|
}
|
|
// Case-insensitive match on name.
|
|
if got := filterMenuItems(items, "GPT"); len(got) != 1 || got[0].name != "gpt-4o" {
|
|
t.Errorf("GPT: got %v", got)
|
|
}
|
|
// Case-insensitive match on desc.
|
|
if got := filterMenuItems(items, "mimo"); len(got) != 1 || got[0].name != "mimo-pro" {
|
|
t.Errorf("mimo: got %v", got)
|
|
}
|
|
// No match.
|
|
if got := filterMenuItems(items, "claude"); len(got) != 0 {
|
|
t.Errorf("claude: got %d, want 0", len(got))
|
|
}
|
|
}
|