diff options
Diffstat (limited to 'tui/tui_test.go')
| -rw-r--r-- | tui/tui_test.go | 188 |
1 files changed, 52 insertions, 136 deletions
diff --git a/tui/tui_test.go b/tui/tui_test.go index 764dd28..d2d2e5f 100644 --- a/tui/tui_test.go +++ b/tui/tui_test.go @@ -10,7 +10,6 @@ import ( "adammathes.com/neko/models" "adammathes.com/neko/models/feed" "adammathes.com/neko/models/item" - "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" ) @@ -40,23 +39,26 @@ func seedData(t *testing.T) { func TestNewModel(t *testing.T) { m := NewModel() - if m.state != viewFeeds { - t.Errorf("Expected initial state viewFeeds, got %v", m.state) + if m.state != sidebarFocus { + t.Errorf("Expected initial state sidebarFocus, got %v", m.state) } } func TestUpdateWindowSizeNoPanic(t *testing.T) { m := NewModel() - // This should not panic even if lists are empty msg := tea.WindowSizeMsg{Width: 80, Height: 24} newModel, _ := m.Update(msg) tm := newModel.(Model) if tm.width != 80 || tm.height != 24 { t.Errorf("Expected size 80x24, got %dx%d", tm.width, tm.height) } + if !tm.ready { + t.Error("Model should be ready after WindowSizeMsg") + } } func TestStateTransitions(t *testing.T) { + setupTestDB(t) m := NewModel() m1, _ := m.Update(tea.WindowSizeMsg{Width: 80, Height: 24}) m = m1.(Model) @@ -65,69 +67,51 @@ func TestStateTransitions(t *testing.T) { feeds := []*feed.Feed{{Id: 1, Title: "Test Feed"}} m2, _ := m.Update(feedsMsg(feeds)) tm2 := m2.(Model) - if len(tm2.feeds) != 1 { + if len(tm2.feedData) != 1 { t.Fatal("Expected 1 feed") } // Items loaded items := []*item.Item{{Id: 1, Title: "Test Item", Description: "Desc"}} - tm2.selectedFeed = feeds[0] // Simulate selection + // Simulate switching to item focus via Enter (which triggers loadItems) + // But explicitly setting state for unit test + tm2.state = sidebarFocus // Ensure we are in sidebar + // Update with itemsMsg m3, _ := tm2.Update(itemsMsg(items)) tm3 := m3.(Model) - if tm3.state != viewItems { - t.Errorf("Expected state viewItems, got %v", tm3.state) - } - if len(tm3.items) != 1 { + + // In the new implementation, loading items doesn't auto-switch focus unless logic says so. + // But let's check if the items are populated. + if len(tm3.itemData) != 1 { t.Fatal("Expected 1 item") } - // Back to feeds - m4, _ := tm3.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("q")}) - tm4 := m4.(Model) - if tm4.state != viewFeeds { - t.Errorf("Expected back to viewFeeds, got %v", tm4.state) - } + // Manually switch focus to items (simulating Tab or logic) + tm3.state = itemFocus // Test entering content view - tm5, _ := tm3.Update(tea.KeyMsg{Type: tea.KeyEnter}) - tm5m := tm5.(Model) - if tm5m.state != viewContent { - t.Errorf("Expected state viewContent, got %v", tm5m.state) - } - if !strings.Contains(tm5m.contentView.View(), "Test Item") { - t.Error("Expected content view to show item title") - } + // Needs selection first. In unit test, list selection might be 0 by default but items need to be set in list model. + // The list model update happens in Update command usually, but here we just updated data. + // We need to sync list items. + // In Update(itemsMsg), we do `m.items.SetItems(...)`. So list should have items. - // Back from content to items - tm6, _ := tm5.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("q")}) - tm6m := tm6.(Model) - if tm6m.state != viewItems { - t.Errorf("Expected back to viewItems, got %v", tm6m.state) - } + // Select item 0 + tm3.items.Select(0) - // Test View for all states - if v := m.View(); !strings.Contains(v, "NEKO TUI") { - t.Error("View should contain title") + m4, _ := tm3.Update(tea.KeyMsg{Type: tea.KeyEnter}) + tm4m := m4.(Model) + if tm4m.state != contentFocus { + t.Errorf("Expected state contentFocus, got %v", tm4m.state) } - if v := tm2.View(); !strings.Contains(v, "Feeds") { - t.Error("View should contain Feeds list") - } - if v := tm3.View(); !strings.Contains(v, "Items") && !strings.Contains(v, "Test Feed") { - t.Error("View should contain Items list or Feed title") - } - if v := tm5m.View(); !strings.Contains(v, "Test Item") { - t.Error("View should contain Item title in content view") + if !strings.Contains(tm4m.content.View(), "Test Item") { + t.Error("Expected content view to show item title") } - // Test scrolling in content view - // Scroll down - tmS1, _ := tm5m.Update(tea.KeyMsg{Type: tea.KeyDown}) - // Home/End - tmS2, _ := tmS1.(Model).Update(tea.KeyMsg{Type: tea.KeyEnd}) - tmS3, _ := tmS2.(Model).Update(tea.KeyMsg{Type: tea.KeyHome}) - - if tmS3.(Model).state != viewContent { - t.Errorf("Should stay in viewContent, got %v", tmS3.(Model).state) + // Back from content to items + m5, _ := tm4m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("q")}) + tm5m := m5.(Model) + if tm5m.state != itemFocus { + t.Errorf("Expected back to itemFocus, got %v", tm5m.state) } } @@ -154,12 +138,6 @@ func TestTuiCommands(t *testing.T) { } } -func TestItemString(t *testing.T) { - is := itemString("hello") - if is.FilterValue() != "hello" { - t.Errorf("Expected 'hello', got %s", is.FilterValue()) - } -} func TestUpdateError(t *testing.T) { m := NewModel() msg := errMsg(fmt.Errorf("test error")) @@ -173,96 +151,34 @@ func TestUpdateError(t *testing.T) { } } -func TestUpdateCtrlC(t *testing.T) { +func TestSwitchFocus(t *testing.T) { m := NewModel() - msg := tea.KeyMsg{Type: tea.KeyCtrlC} - _, cmd := m.Update(msg) - if cmd == nil { - t.Error("Expected tea.Quit command") - } -} + m.state = sidebarFocus -func TestViewError(t *testing.T) { - m := NewModel() - m.err = fmt.Errorf("fatal error") - v := m.View() - if !strings.Contains(v, "Error: fatal error") { - t.Errorf("Expected view to show error, got %q", v) + // Tab to switch + m1, _ := m.Update(tea.KeyMsg{Type: tea.KeyTab}) + if m1.(Model).state != itemFocus { + t.Error("Tab from sidebar should go to itemFocus") } -} -func TestUpdateEscNotFeeds(t *testing.T) { - m := NewModel() - m.state = viewItems - m1, _ := m.Update(tea.KeyMsg{Type: tea.KeyEsc}) - if m1.(Model).state != viewFeeds { - t.Error("Esc in viewItems should go to viewFeeds") + m2, _ := m1.Update(tea.KeyMsg{Type: tea.KeyTab}) + if m2.(Model).state != sidebarFocus { + t.Error("Tab from itemFocus should go back to sidebarFocus") } } -func TestLoadFeedsError(t *testing.T) { - setupTestDB(t) - models.DB.Close() - msg := loadFeeds() - if _, ok := msg.(errMsg); !ok { - t.Errorf("Expected errMsg on DB close, got %T", msg) - } -} - -func TestLoadItemsError(t *testing.T) { - setupTestDB(t) - models.DB.Close() - msg := loadItems(1)() - if _, ok := msg.(errMsg); !ok { - t.Errorf("Expected errMsg on DB close, got %T", msg) - } -} - -func TestUpdateItemsMsg(t *testing.T) { - m := NewModel() - msg := itemsMsg([]*item.Item{{Title: "Test Item"}}) - m1, _ := m.Update(msg) - tm := m1.(Model) - if tm.state != viewItems { - t.Errorf("Expected state viewItems, got %v", tm.state) - } - if len(tm.items) != 1 { - t.Errorf("Expected 1 item, got %d", len(tm.items)) - } -} - -func TestUpdateEnterItems(t *testing.T) { +func TestView(t *testing.T) { m := NewModel() - m.state = viewItems - m.items = []*item.Item{{Title: "Test Item", Description: "Content"}} - m.itemList = list.New([]list.Item{itemString("Test Item")}, list.NewDefaultDelegate(), 0, 0) - - m1, _ := m.Update(tea.KeyMsg{Type: tea.KeyEnter}) + // Trigger WindowSizeMsg to make it ready and size components + m1, _ := m.Update(tea.WindowSizeMsg{Width: 80, Height: 24}) tm := m1.(Model) - if tm.state != viewContent { - t.Errorf("Expected state viewContent, got %v", tm.state) - } -} - -func TestUpdateTuiMoreKeys(t *testing.T) { - m := NewModel() - - // Test 'q' - _, cmd := m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("q")}) - if cmd == nil { - t.Error("Expected Quit command for 'q'") - } - // Test 'r' - _, cmd = m.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("r")}) - if cmd == nil { - t.Error("Expected loadFeeds command for 'r'") + v := tm.View() + // It should render "Feeds" and "Items" (titles of lists) + if !strings.Contains(v, "Feeds") { + t.Error("View should contain Feeds") } - - // Test 'esc' when already at viewFeeds - m.state = viewFeeds - m3, _ := m.Update(tea.KeyMsg{Type: tea.KeyEsc}) - if m3.(Model).state != viewFeeds { - t.Error("Esc at viewFeeds should keep viewFeeds") + if !strings.Contains(v, "Items") { + t.Error("View should contain Items") } } |
