Files
zero/internal/tui/selectable_list_test.go
Anandan 9c1cc69b59 Migrate TUI to Bubble Tea v2 (#222)
* Migrate TUI to Bubble Tea v2

Move the TUI stack to charm.land Bubble Tea, Bubbles, and Lip Gloss v2, including key, paste, mouse, and view handling changes for the v2 APIs.

Add compatibility helpers for the new input event shapes and update TUI tests accordingly.

Refresh related Go dependencies and make the PDF truncation fixture deterministic across poppler and pure-Go extraction.

Tested: GOCACHE=/tmp/zero-go-cache GOMODCACHE=/tmp/zero-go-mod-cache go test ./internal/tui

Tested: GOCACHE=/tmp/zero-go-cache GOMODCACHE=/tmp/zero-go-mod-cache go test ./internal/imageinput -count=1 -v

Tested: GOCACHE=/tmp/zero-go-cache GOMODCACHE=/tmp/zero-go-mod-cache go test ./...

* Address provider wizard OAuth review feedback

Remove the unused mouseButton compatibility helper flagged by lint.

Correlate provider wizard OAuth and device-code async results with the active provider and attempt id so stale results from abandoned attempts are ignored.

Add regression coverage for stale browser OAuth and stale device-code results.

Tested: GOCACHE=/tmp/zero-go-cache GOMODCACHE=/tmp/zero-go-mod-cache go test ./internal/tui -run 'ProviderWizard.*OAuth|ProviderWizard.*Device|ApplyProviderWizardOAuth|RenderCredentialStepShowsOAuth' -count=1 -v

Tested: GOCACHE=/tmp/zero-go-cache GOMODCACHE=/tmp/zero-go-mod-cache go test ./internal/tui

Tested: GOCACHE=/tmp/zero-go-cache GOMODCACHE=/tmp/zero-go-mod-cache go test ./...

Tested: git diff --check

* Smooth TUI mouse scrolling
2026-06-16 19:04:38 +05:30

105 lines
3.1 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package tui
import (
"fmt"
"strings"
"testing"
"charm.land/lipgloss/v2"
)
func TestSelectableListAnchorsSelectionAroundFourthVisibleRow(t *testing.T) {
items := make([]selectableListItem, 10)
for i := range items {
items[i] = selectableListItem{Label: fmt.Sprintf("/cmd%d", i), Description: "command"}
}
rendered := renderSelectableList(selectableListOptions{
Items: items,
Selected: 5,
Width: 40,
MaxVisible: 5,
})
lines := strings.Split(plainRender(t, rendered), "\n")
if len(lines) != 6 {
t.Fatalf("rendered %d lines, want 5 rows plus count line: %q", len(lines), lines)
}
for _, hidden := range []string{"/cmd0", "/cmd1", "/cmd7", "/cmd8", "/cmd9"} {
if strings.Contains(rendered, hidden) {
t.Fatalf("visible window should hide %s, got %q", hidden, plainRender(t, rendered))
}
}
for _, visible := range []string{"/cmd2", "/cmd3", "/cmd4", "/cmd5", "/cmd6"} {
if !strings.Contains(rendered, visible) {
t.Fatalf("visible window should include %s, got %q", visible, plainRender(t, rendered))
}
}
if !strings.Contains(lines[3], "❯ /cmd5") {
t.Fatalf("selected item should be anchored on fourth visible row, got %q", lines[3])
}
if !strings.Contains(lines[5], "5 more") {
t.Fatalf("count line should summarize hidden rows, got %q", lines[5])
}
}
func TestSelectableListAlignsAndTruncatesDescriptions(t *testing.T) {
rendered := renderSelectableList(selectableListOptions{
Items: []selectableListItem{
{Label: "/m", Description: "Short"},
{Label: "/model", Description: "Switch model and provider for this session"},
},
Selected: 0,
Width: 32,
MaxVisible: 8,
})
lines := strings.Split(plainRender(t, rendered), "\n")
if len(lines) != 2 {
t.Fatalf("rendered lines = %d, want 2: %q", len(lines), lines)
}
shortAt := strings.Index(lines[0], "Short")
switchAt := strings.Index(lines[1], "Switch")
if shortAt < 0 || switchAt < 0 {
t.Fatalf("descriptions missing from rows: %q", lines)
}
shortColumn := lipgloss.Width(lines[0][:shortAt])
switchColumn := lipgloss.Width(lines[1][:switchAt])
if shortColumn != switchColumn {
t.Fatalf("descriptions should align, Short at column %d Switch at column %d in %q", shortColumn, switchColumn, lines)
}
if got := lipgloss.Width(lines[1]); got > 32 {
t.Fatalf("row width = %d, want <= 32: %q", got, lines[1])
}
if !strings.Contains(lines[1], "…") {
t.Fatalf("long description should truncate with ellipsis, got %q", lines[1])
}
}
func TestSelectableListMarksOnlySelectedRow(t *testing.T) {
rendered := renderSelectableList(selectableListOptions{
Items: []selectableListItem{
{Label: "/help", Description: "Show help"},
{Label: "/model", Description: "Switch model"},
{Label: "/quit", Description: "Quit"},
},
Selected: 1,
Width: 48,
MaxVisible: 8,
})
lines := strings.Split(plainRender(t, rendered), "\n")
selected := 0
for _, line := range lines {
if strings.HasPrefix(line, "❯ ") {
selected++
if !strings.Contains(line, "/model") {
t.Fatalf("selected marker is on wrong row: %q", line)
}
}
}
if selected != 1 {
t.Fatalf("selected marker count = %d, want 1 in %q", selected, lines)
}
}