| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 |
- package list
- import (
- "testing"
- tea "github.com/charmbracelet/bubbletea/v2"
- "github.com/sst/opencode/internal/styles"
- )
- // testItem is a simple test implementation of ListItem
- type testItem struct {
- value string
- }
- func (t testItem) Render(
- selected bool,
- width int,
- isFirstInViewport bool,
- baseStyle styles.Style,
- ) string {
- return t.value
- }
- func (t testItem) Selectable() bool {
- return true
- }
- // createTestList creates a list with test items for testing
- func createTestList() *listComponent[testItem] {
- items := []testItem{
- {value: "item1"},
- {value: "item2"},
- {value: "item3"},
- }
- list := NewListComponent(
- WithItems(items),
- WithMaxVisibleItems[testItem](5),
- WithFallbackMessage[testItem]("empty"),
- WithAlphaNumericKeys[testItem](false),
- WithRenderFunc(
- func(item testItem, selected bool, width int, baseStyle styles.Style) string {
- return item.Render(selected, width, false, baseStyle)
- },
- ),
- WithSelectableFunc(func(item testItem) bool {
- return item.Selectable()
- }),
- WithHeightFunc(func(item testItem, isFirstInViewport bool) int {
- return 1
- }),
- )
- return list.(*listComponent[testItem])
- }
- func TestArrowKeyNavigation(t *testing.T) {
- list := createTestList()
- // Test down arrow navigation
- downKey := tea.KeyPressMsg{Code: tea.KeyDown}
- updatedModel, _ := list.Update(downKey)
- list = updatedModel.(*listComponent[testItem])
- _, idx := list.GetSelectedItem()
- if idx != 1 {
- t.Errorf("Expected selected index 1 after down arrow, got %d", idx)
- }
- // Test up arrow navigation
- upKey := tea.KeyPressMsg{Code: tea.KeyUp}
- updatedModel, _ = list.Update(upKey)
- list = updatedModel.(*listComponent[testItem])
- _, idx = list.GetSelectedItem()
- if idx != 0 {
- t.Errorf("Expected selected index 0 after up arrow, got %d", idx)
- }
- }
- func TestJKKeyNavigation(t *testing.T) {
- items := []testItem{
- {value: "item1"},
- {value: "item2"},
- {value: "item3"},
- }
- // Create list with alpha keys enabled
- list := NewListComponent(
- WithItems(items),
- WithMaxVisibleItems[testItem](5),
- WithFallbackMessage[testItem]("empty"),
- WithAlphaNumericKeys[testItem](true),
- WithRenderFunc(
- func(item testItem, selected bool, width int, baseStyle styles.Style) string {
- return item.Render(selected, width, false, baseStyle)
- },
- ),
- WithSelectableFunc(func(item testItem) bool {
- return item.Selectable()
- }),
- WithHeightFunc(func(item testItem, isFirstInViewport bool) int {
- return 1
- }),
- )
- // Test j key (down)
- jKey := tea.KeyPressMsg{Code: 'j', Text: "j"}
- updatedModel, _ := list.Update(jKey)
- list = updatedModel.(*listComponent[testItem])
- _, idx := list.GetSelectedItem()
- if idx != 1 {
- t.Errorf("Expected selected index 1 after 'j' key, got %d", idx)
- }
- // Test k key (up)
- kKey := tea.KeyPressMsg{Code: 'k', Text: "k"}
- updatedModel, _ = list.Update(kKey)
- list = updatedModel.(*listComponent[testItem])
- _, idx = list.GetSelectedItem()
- if idx != 0 {
- t.Errorf("Expected selected index 0 after 'k' key, got %d", idx)
- }
- }
- func TestCtrlNavigation(t *testing.T) {
- list := createTestList()
- // Test Ctrl-N (down)
- ctrlN := tea.KeyPressMsg{Code: 'n', Mod: tea.ModCtrl}
- updatedModel, _ := list.Update(ctrlN)
- list = updatedModel.(*listComponent[testItem])
- _, idx := list.GetSelectedItem()
- if idx != 1 {
- t.Errorf("Expected selected index 1 after Ctrl-N, got %d", idx)
- }
- // Test Ctrl-P (up)
- ctrlP := tea.KeyPressMsg{Code: 'p', Mod: tea.ModCtrl}
- updatedModel, _ = list.Update(ctrlP)
- list = updatedModel.(*listComponent[testItem])
- _, idx = list.GetSelectedItem()
- if idx != 0 {
- t.Errorf("Expected selected index 0 after Ctrl-P, got %d", idx)
- }
- }
- func TestNavigationBoundaries(t *testing.T) {
- list := createTestList()
- // Test up arrow at first item (should stay at 0)
- upKey := tea.KeyPressMsg{Code: tea.KeyUp}
- updatedModel, _ := list.Update(upKey)
- list = updatedModel.(*listComponent[testItem])
- _, idx := list.GetSelectedItem()
- if idx != 0 {
- t.Errorf("Expected to stay at index 0 when pressing up at first item, got %d", idx)
- }
- // Move to last item
- downKey := tea.KeyPressMsg{Code: tea.KeyDown}
- updatedModel, _ = list.Update(downKey)
- list = updatedModel.(*listComponent[testItem])
- updatedModel, _ = list.Update(downKey)
- list = updatedModel.(*listComponent[testItem])
- _, idx = list.GetSelectedItem()
- if idx != 2 {
- t.Errorf("Expected to be at index 2, got %d", idx)
- }
- // Test down arrow at last item (should stay at 2)
- updatedModel, _ = list.Update(downKey)
- list = updatedModel.(*listComponent[testItem])
- _, idx = list.GetSelectedItem()
- if idx != 2 {
- t.Errorf("Expected to stay at index 2 when pressing down at last item, got %d", idx)
- }
- }
- func TestEmptyList(t *testing.T) {
- emptyList := NewListComponent(
- WithItems([]testItem{}),
- WithMaxVisibleItems[testItem](5),
- WithFallbackMessage[testItem]("empty"),
- WithAlphaNumericKeys[testItem](false),
- WithRenderFunc(
- func(item testItem, selected bool, width int, baseStyle styles.Style) string {
- return item.Render(selected, width, false, baseStyle)
- },
- ),
- WithSelectableFunc(func(item testItem) bool {
- return item.Selectable()
- }),
- WithHeightFunc(func(item testItem, isFirstInViewport bool) int {
- return 1
- }),
- )
- // Test navigation on empty list (should not crash)
- downKey := tea.KeyPressMsg{Code: tea.KeyDown}
- upKey := tea.KeyPressMsg{Code: tea.KeyUp}
- ctrlN := tea.KeyPressMsg{Code: 'n', Mod: tea.ModCtrl}
- ctrlP := tea.KeyPressMsg{Code: 'p', Mod: tea.ModCtrl}
- updatedModel, _ := emptyList.Update(downKey)
- emptyList = updatedModel.(*listComponent[testItem])
- updatedModel, _ = emptyList.Update(upKey)
- emptyList = updatedModel.(*listComponent[testItem])
- updatedModel, _ = emptyList.Update(ctrlN)
- emptyList = updatedModel.(*listComponent[testItem])
- updatedModel, _ = emptyList.Update(ctrlP)
- emptyList = updatedModel.(*listComponent[testItem])
- // Verify empty list behavior
- _, idx := emptyList.GetSelectedItem()
- if idx != -1 {
- t.Errorf("Expected index -1 for empty list, got %d", idx)
- }
- if !emptyList.IsEmpty() {
- t.Error("Expected IsEmpty() to return true for empty list")
- }
- }
|