package browser import ( "testing" ) // --- Test: Scroll Clamp Minimum --- func TestScrollClamp_Minimum(t *testing.T) { s := NewBrowserState() s.TotalEntries = 500 s.VisibleCount = 20 s.EntryHeight = 48.0 s.ScrollOffset = 0 // Try to scroll above the top HandlePixelScroll(s, -5*48) // ScrollOffset should be clamped to 0 if s.ScrollOffset != 0 { t.Errorf("expected ScrollOffset=0 (clamped), got %.1f", s.ScrollOffset) } } // --- Test: Scroll Clamp Maximum --- func TestScrollClamp_Maximum(t *testing.T) { s := NewBrowserState() s.TotalEntries = 500 s.VisibleCount = 20 s.EntryHeight = 48.0 s.ScrollOffset = 0 // Scroll past the maximum to trigger clamp maxScroll := float64(s.TotalEntries-s.VisibleCount) * s.EntryHeight HandlePixelScroll(s, int(maxScroll)+1000) // ScrollOffset should be clamped to max (TotalEntries - VisibleCount) * EntryHeight if s.ScrollOffset != maxScroll { t.Errorf("expected ScrollOffset=%.1f (clamped to max), got %.1f", maxScroll, s.ScrollOffset) } } // --- Test: Scroll Delta Computation --- func TestScrollDelta_Computation(t *testing.T) { s := NewBrowserState() s.TotalEntries = 1000 s.VisibleCount = 20 s.EntryHeight = 48.0 s.ScrollOffset = 100 * 48 // Scroll down by 5 entries (positive delta) HandlePixelScroll(s, 5*48) expectedOffset := float64(105 * 48) if s.ScrollOffset != expectedOffset { t.Errorf("expected ScrollOffset=%.1f after scrolling down 5 entries, got %.1f", expectedOffset, s.ScrollOffset) } // Scroll up by 3 entries (negative delta) HandlePixelScroll(s, -3*48) expectedOffset = float64(102 * 48) if s.ScrollOffset != expectedOffset { t.Errorf("expected ScrollOffset=%.1f after scrolling up 3 entries, got %.1f", expectedOffset, s.ScrollOffset) } } // --- Test: Scroll Prefetch Triggered --- func TestScroll_PrefetchTriggered(t *testing.T) { s := NewBrowserState() s.TotalEntries = 2000 s.VisibleCount = 20 s.EntryHeight = 48.0 s.ScrollOffset = 500 * 48 // Page 5 // Load some pages around current position entries := make([]Entry, 2000) for i := 0; i < 2000; i++ { entries[i] = NewEntry("/test/file.txt", "file.txt", 100, s.TapTimestamp, false) } // Build position map for the default sort mode (NameAsc) s.SortIndex = &DirectoryIndex{ Entries: entries, SortOrders: map[string][]int{"name_asc": buildPositionMap(entries, SortModeNameAsc)}, } for pageIdx := 3; pageIdx <= 7; pageIdx++ { p := loadPageFromIndex(s, pageIdx) if p != nil { s.Pages[pageIdx] = p } } // Record current page count pagesBefore := len(s.Pages) // Scroll to trigger prefetch HandlePixelScroll(s, 200*48) // Scroll to page 7+ // After scroll, prefetch should have been triggered // (exact behavior depends on implementation) // For now, verify that scroll happened correctly if s.GetScrollIndex() <= 500 { t.Errorf("expected GetScrollIndex() > 500 after scrolling, got %d", s.GetScrollIndex()) } // Page count may have increased due to prefetch if len(s.Pages) < pagesBefore { t.Errorf("expected page count to not decrease after scroll, before=%d, after=%d", pagesBefore, len(s.Pages)) } } // --- Test: Scroll Eviction Triggered --- func TestScroll_EvictionTriggered(t *testing.T) { s := NewBrowserState() s.TotalEntries = 5000 s.VisibleCount = 20 s.EntryHeight = 48.0 s.ScrollOffset = 0 // Load pages at various positions entries := make([]Entry, 5000) for i := 0; i < 5000; i++ { entries[i] = NewEntry("/test/file.txt", "file.txt", 100, s.TapTimestamp, false) } // Build position map for the default sort mode (NameAsc) s.SortIndex = &DirectoryIndex{ Entries: entries, SortOrders: map[string][]int{"name_asc": buildPositionMap(entries, SortModeNameAsc)}, } for pageIdx := 0; pageIdx <= 10; pageIdx++ { p := loadPageFromIndex(s, pageIdx) if p != nil { s.Pages[pageIdx] = p } } // Scroll far away from initial position HandlePixelScroll(s, 4000*48) // Scroll to page 40+ // Pages far from current position should be evicted // Check that page 0 was evicted (it's far from page 40) if page, exists := s.Pages[0]; exists && page.Loaded { t.Error("expected page 0 to be evicted after scrolling far away") } } // --- Test: Scroll With Search Results --- func TestScroll_WithSearchResults(t *testing.T) { s := NewBrowserState() s.TotalEntries = 100 s.VisibleCount = 20 s.EntryHeight = 48.0 s.ScrollOffset = 0 // Set up search results (larger than VisibleCount to allow scrolling) s.SearchResults = make([]int, 25) for i := 0; i < 25; i++ { s.SearchResults[i] = i } // Scroll should work normally since search results (25) > VisibleCount (20) HandlePixelScroll(s, 10*48) expectedOffset := float64(10 * 48) if s.ScrollOffset != expectedOffset { t.Errorf("expected ScrollOffset=%.1f, got %.1f", expectedOffset, s.ScrollOffset) } } // --- Test: Scroll To Beginning --- func TestScroll_ToBeginning(t *testing.T) { s := NewBrowserState() s.TotalEntries = 1000 s.VisibleCount = 20 s.EntryHeight = 48.0 s.ScrollOffset = 500 * 48 // Scroll all the way to the beginning HandlePixelScroll(s, -100000) if s.ScrollOffset != 0 { t.Errorf("expected ScrollOffset=0 at beginning, got %.1f", s.ScrollOffset) } } // --- Test: Scroll To End --- func TestScroll_ToEnd(t *testing.T) { s := NewBrowserState() s.TotalEntries = 1000 s.VisibleCount = 20 s.EntryHeight = 48.0 s.ScrollOffset = 0 // Scroll all the way to the end maxScroll := float64(s.TotalEntries-s.VisibleCount) * s.EntryHeight HandlePixelScroll(s, int(maxScroll)+1000) if s.ScrollOffset != maxScroll { t.Errorf("expected ScrollOffset=%.1f at end, got %.1f", maxScroll, s.ScrollOffset) } } // --- Test: Scroll With Small Directory --- func TestScroll_SmallDirectory(t *testing.T) { s := NewBrowserState() s.TotalEntries = 10 // Smaller than VisibleCount s.VisibleCount = 20 s.EntryHeight = 48.0 s.ScrollOffset = 0 // Try to scroll down (should be clamped) HandlePixelScroll(s, 1000) // ScrollOffset should be 0 (can't scroll beyond available entries) if s.ScrollOffset != 0 { t.Errorf("expected ScrollOffset=0 for small directory, got %.1f", s.ScrollOffset) } } // --- Test: Scroll Prefetch Triggered On Page Boundary --- func TestScroll_PrefetchTriggeredOnPageBoundary(t *testing.T) { s := NewBrowserState() s.TotalEntries = 500 s.VisibleCount = 20 s.EntryHeight = 48.0 s.ScrollOffset = 90 * 48 // Near page boundary (page 0 ends at 100) // Load page 0 entries := make([]Entry, 500) for i := 0; i < 500; i++ { entries[i] = NewEntry("/test/file.txt", "file.txt", 100, s.TapTimestamp, false) } // Build position map for the default sort mode (NameAsc) s.SortIndex = &DirectoryIndex{ Entries: entries, SortOrders: map[string][]int{"name_asc": buildPositionMap(entries, SortModeNameAsc)}, } s.Pages[0] = loadPageFromIndex(s, 0) // Scroll across page boundary HandlePixelScroll(s, 15*48) // Should cross into page 1 // Verify scroll happened if s.GetScrollIndex() != 105 { t.Errorf("expected GetScrollIndex()=105, got %d", s.GetScrollIndex()) } }