The tree was formatted with an older gofmt; go1.27's gofmt additionally wants: EOF exactly one newline (no trailing blank lines), imports sorted alphabetically within a block, mixed-precedence binary expressions re-spaced for grouping ((a+b)/c), single-field composite literals un-aligned, adjacent one-line method signatures aligned, and one-line bodies containing a compound statement expanded. Applied repo-wide (31 files under internal/); pure formatting, no semantic changes — build and the full test suite pass.
303 lines
7.4 KiB
Go
303 lines
7.4 KiB
Go
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))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|