package browser import ( "fmt" "testing" "time" "pad/internal/ui" ) // --- Helper: create a browser state with known entries --- func makeTestState(t *testing.T, entryCount int, scrollIndex int, visibleCount int) *BrowserState { t.Helper() s := NewBrowserState() s.CurrentPath = "/test/path" s.EntryHeight = 48.0 s.ScrollOffset = float64(scrollIndex) * s.EntryHeight s.VisibleCount = visibleCount s.TotalEntries = entryCount entries := make([]Entry, entryCount) for i := 0; i < entryCount; i++ { entries[i] = NewEntry( fmt.Sprintf("/test/path/file%d.txt", i), fmt.Sprintf("file%d.txt", i), int64(i*100), time.Time{}, false, ) } // Build position maps for sort modes s.SortIndex = &DirectoryIndex{ Entries: entries, SortOrders: map[string][]int{ "name_asc": buildPositionMap(entries, SortModeNameAsc), "date_desc": buildPositionMap(entries, SortModeDateDesc), }, } // Load pages for pageIdx := 0; pageIdx <= entryCount/PageSize; pageIdx++ { p := loadPageFromIndex(s, pageIdx) if p != nil { s.Pages[pageIdx] = p } } return s } // --- Helper: find ListView from elements --- func findListView(elements []ui.Element) (ui.ListView, bool) { for _, el := range elements { if lv, ok := el.(ui.ListView); ok { return lv, true } } return ui.ListView{}, false } // --- Test: Browser Layout - Element Count --- func TestBrowserLayout_ElementCount(t *testing.T) { s := makeTestState(t, 200, 0, 20) elements := BrowserLayout(ui.Dp(800), ui.Dp(1200), s, nil, nil) // Should produce: header + search bar + list view = 3 elements if len(elements) < 3 { t.Errorf("expected at least 3 elements, got %d", len(elements)) } } // --- Test: Browser Layout - Header Region --- func TestBrowserLayout_HeaderRegion(t *testing.T) { s := makeTestState(t, 200, 0, 20) elements := BrowserLayout(ui.Dp(800), ui.Dp(1200), s, nil, nil) // First element should be the header container if len(elements) == 0 { t.Fatal("no elements returned") } header := elements[0] region := header.Region() // Header should be near the top if region.Y > ui.Dp(50) { t.Errorf("expected header Y <= 50, got %d", int(region.Y)) } // Header should span most of the width if region.W < ui.Dp(700) { t.Errorf("expected header W >= 700, got %d", int(region.W)) } } // --- Test: Browser Layout - List Region --- func TestBrowserLayout_ListRegion(t *testing.T) { s := makeTestState(t, 200, 0, 20) elements := BrowserLayout(ui.Dp(800), ui.Dp(1200), s, nil, nil) listView, found := findListView(elements) if !found { t.Fatal("ListView element not found in layout") } region := listView.Region() // List should be below the header and search bar if region.Y < ui.Dp(50) { t.Errorf("expected list Y >= 50, got %d", int(region.Y)) } // List should have significant height if region.H < ui.Dp(500) { t.Errorf("expected list H >= 500, got %d", int(region.H)) } } // --- Test: Browser Layout - Visible Entries Count --- func TestBrowserLayout_VisibleEntriesCount(t *testing.T) { s := makeTestState(t, 500, 100, 20) elements := BrowserLayout(ui.Dp(800), ui.Dp(1200), s, nil, nil) listView, found := findListView(elements) if !found { t.Fatal("ListView element not found in layout") } // Should only render visible entries (approximately VisibleCount) // Allow some tolerance for partial pages maxExpected := s.VisibleCount + PageSize if len(listView.Items) > maxExpected { t.Errorf("expected at most %d visible items, got %d", maxExpected, len(listView.Items)) } } // --- Test: Browser Layout - Empty Directory --- func TestBrowserLayout_EmptyDirectory(t *testing.T) { s := NewBrowserState() s.CurrentPath = "/empty/dir" s.TotalEntries = 0 elements := BrowserLayout(ui.Dp(800), ui.Dp(1200), s, nil, nil) if len(elements) < 3 { t.Errorf("expected at least 3 elements for empty dir, got %d", len(elements)) } listView, found := findListView(elements) if !found { t.Fatal("ListView element not found in layout") } if len(listView.Items) != 0 { t.Errorf("expected 0 items for empty directory, got %d", len(listView.Items)) } } // --- Test: Browser Layout - Single Entry --- func TestBrowserLayout_SingleEntry(t *testing.T) { s := NewBrowserState() s.CurrentPath = "/test" s.TotalEntries = 1 s.VisibleCount = 20 s.EntryHeight = 48.0 entries := []Entry{ NewEntry("/test/only.txt", "only.txt", 1024, time.Time{}, false), } s.Pages[0] = NewPage(0, entries) elements := BrowserLayout(ui.Dp(800), ui.Dp(1200), s, nil, nil) listView, found := findListView(elements) if !found { t.Fatal("ListView element not found in layout") } if len(listView.Items) != 1 { t.Errorf("expected 1 item, got %d", len(listView.Items)) } } // --- Test: Browser Layout - Partial Page --- func TestBrowserLayout_PartialPage(t *testing.T) { s := NewBrowserState() s.CurrentPath = "/test" s.TotalEntries = 150 // 1 full page + 50 in second page s.EntryHeight = 48.0 s.ScrollOffset = 120 * 48 // Near the end s.VisibleCount = 20 entries := make([]Entry, 150) for i := 0; i < 150; i++ { entries[i] = NewEntry( fmt.Sprintf("/test/file%d.txt", i), fmt.Sprintf("file%d.txt", i), int64(i*100), time.Time{}, false, ) } s.Pages[0] = NewPage(0, entries[:100]) s.Pages[1] = NewPage(1, entries[100:]) elements := BrowserLayout(ui.Dp(800), ui.Dp(1200), s, nil, nil) listView, found := findListView(elements) if !found { t.Fatal("ListView element not found in layout") } // Should have entries from the partial page if len(listView.Items) == 0 { t.Error("expected items from partial page") } } // --- Test: Browser Layout - Unloaded Page Shows Placeholder --- func TestBrowserLayout_UnloadedPage(t *testing.T) { s := NewBrowserState() s.CurrentPath = "/test" s.TotalEntries = 500 s.EntryHeight = 48.0 s.ScrollOffset = 250 * 48 s.VisibleCount = 20 // Only load page 0, not page 2 or 3 entries := make([]Entry, 100) s.Pages[0] = NewPage(0, entries) elements := BrowserLayout(ui.Dp(800), ui.Dp(1200), s, nil, nil) // Layout should not crash with unloaded pages if len(elements) < 3 { t.Errorf("expected at least 3 elements, got %d", len(elements)) } } // --- Test: Browser Layout - With Search Query --- func TestBrowserLayout_WithSearchQuery(t *testing.T) { s := makeTestState(t, 100, 0, 20) s.Query = "file1" // Set up search results for i := 0; i < s.TotalEntries; i++ { if i == 1 || i == 10 || i == 11 || i == 12 || i == 13 || i == 14 || i == 15 || i == 16 || i == 17 || i == 18 || i == 19 { s.SearchResults = append(s.SearchResults, i) } } elements := BrowserLayout(ui.Dp(800), ui.Dp(1200), s, nil, nil) // Should still produce valid layout if len(elements) < 3 { t.Errorf("expected at least 3 elements with search, got %d", len(elements)) } } // --- Test: Browser Layout - Screen Dimensions --- func TestBrowserLayout_ScreenDimensions(t *testing.T) { s := makeTestState(t, 100, 0, 20) // Test with different screen sizes for _, w := range []ui.Dp{400, 800, 1200} { for _, h := range []ui.Dp{600, 1200, 1800} { elements := BrowserLayout(w, h, s, nil, nil) if len(elements) < 3 { t.Errorf("layout failed for %dx%d: expected >= 3 elements, got %d", int(w), int(h), len(elements)) } // All elements should fit within screen for _, el := range elements { region := el.Region() if region.X+region.W > w { t.Errorf("element exceeds screen width at %dx%d", int(w), int(h)) } if region.Y+region.H > h { t.Errorf("element exceeds screen height at %dx%d", int(w), int(h)) } } } } }