Pad/internal/test/e2e/edit_lifecycle_test.go
Greg Pomerantz 615957196e feat: virtual scrolling with chunked buffer for large files
- Add ChunkedBuffer for 64KB chunked file access with dirty-chunk
  eviction protection
- Add LineIndex for precise byte-offset-to-line-number mapping
- Refactor IO task system with context cancellation, typed priorities,
  and new task types (ReadChunk, BuildLineIndex, StatFile)
- Add ReadFileAt to FileSystem interface (mock + real implementations)
- Integrate virtual scrolling into editor layout
- Add comprehensive tests for chunked buffer eviction, dirty-chunk
  safety, and full edit lifecycle
2026-06-05 09:11:32 -04:00

91 lines
2.2 KiB
Go

package e2e_test
import (
"testing"
"time"
"pad/internal/editor"
"pad/internal/test/e2e"
"pad/internal/ui"
)
func TestEditLifecycle(t *testing.T) {
h := e2e.NewHarnessWithDefaults()
defer h.Cleanup()
// 1. Go to browser page
editor.GoToBrowser(nil)
time.Sleep(200 * time.Millisecond)
// 2. Open File
filename := "/notes.txt"
editor.OpenFile(filename)
// Wait for file to load
success := false
for i := 0; i < 20; i++ {
if h.State().Editor.ChunkedBuffer != nil && h.State().Editor.ChunkedBuffer.FileLen() > 0 {
success = true
break
}
time.Sleep(100 * time.Millisecond)
}
if !success {
t.Fatal("Timed out waiting for file to load")
}
// 3. Edit File
// Original content of notes.txt is 198 bytes, let's append " UPDATED"
editor.HandleInsert(" UPDATED")
time.Sleep(100 * time.Millisecond)
// 4. Close File (Go back to browser)
// This will trigger FlushAll()
editor.GoToBrowser(nil)
time.Sleep(500 * time.Millisecond) // Ensure it had time to process close
// 5. Re-open File
editor.OpenFile(filename)
time.Sleep(1000 * time.Millisecond) // Wait for re-open and load
// 6. Verify Edits
fullContent, err := h.State().Editor.ChunkedBuffer.FullContent()
if err != nil {
t.Fatalf("Failed to reconstruct content: %v", err)
}
if len(fullContent) <= 198 {
t.Errorf("Content did not persist. Length: %d, expected > 198", len(fullContent))
}
// 7. Verify Size on Screen (check bottom bar label)
frame := e2e.GetLastFrame(h)
var sizeText string
for _, elem := range frame {
if container, ok := elem.(ui.Container); ok {
// The bottom bar is a container, look for labels inside
for _, child := range container.Children {
if label, ok := child.(ui.Label); ok {
// The cursor position text is in the middle, containing "/"
if len(label.Text) > 0 && label.Text[0] != '/' && contains(label.Text, "/") {
sizeText = label.Text
}
}
}
}
}
expectedText := "0 / 206" // 198 original + " UPDATED" (8) = 206, cursor at top
if sizeText != expectedText {
t.Errorf("Bottom bar size label mismatch: got %q, expected %q", sizeText, expectedText)
}
}
func contains(s, substr string) bool {
for i := 0; i < len(s); i++ {
if s[i] == substr[0] {
return true
}
}
return false
}