Pad/internal/test/e2e/scroll_cursor_test.go
Greg Pomerantz 2b7c9cd3eb Collapse editor top bar to one row (back + filename)
The cut/copy/paste icons were dead placeholders (no handlers);
clipboard actions live in the floating selection menu, so the second
icon row is removed. The bar goes from two rows at 52dp to one row at
32dp, giving the editor 20dp more height.

- filename_test.go now finds the label by type (back icon precedes it)
- menu tracking tests updated for the new editorY (reg.Y = 10+32)
- scroll_cursor e2e simulation updated to match
- On-device verified: back button works, floating menu places above
  with room and flips below on the first lines, handles unaffected.
2026-08-18 08:32:19 -04:00

109 lines
3.5 KiB
Go

package e2e_test
import (
"testing"
"time"
"pad/internal/editor"
"pad/internal/test/e2e"
"pad/internal/ui"
)
// TestEditorClickToMoveCursorWithScroll verifies that a tap in the editor
// accounts for the current scroll offset and lands on the right line.
//
// State setup/assertions use the harness's owner-side helpers (WithState /
// CursorPosition / Inspect); the tap handler closure runs on the logic
// goroutine via SendInput, so its reads of editor.TheState are safe.
func TestEditorClickToMoveCursorWithScroll(t *testing.T) {
// NewHarnessWithDefaults already starts the logic goroutine.
h := e2e.NewHarnessWithDefaults()
defer h.Cleanup()
// Switch to editor page (owner-side)
if err := h.WithState(func(st *editor.State) { editor.GoToEditor(nil) }); err != nil {
t.Fatalf("GoToEditor: %v", err)
}
h.SendConfig(780, 1688)
// Give it a moment to initialize
time.Sleep(200 * time.Millisecond)
// Wait for frame
_, err := h.WaitForFrameCount(1, 5*time.Second)
if err != nil {
t.Fatalf("timeout waiting for frame: %v", err)
}
// Setup:
// - 2 lines, each 10 chars + newline.
// - Line height = 14 * 1.2 = 16.8 (rounded?) Let's check EditorLineHeight()
// EditorLineHeight is 14 * 1.2 = 16.8.
lineHeight := 16.8
if err := h.WithState(func(st *editor.State) {
st.Editor.Buffer = "Line 1\nLine 2\nLine 3"
// Scroll to start of Line 2 (skip Line 1)
st.ScrollOffset = ui.Dp(lineHeight)
// GlyphLayout:
// Line 1: y = 0
// Line 2: y = 16.8
// Line 3: y = 33.6
st.Editor.GlyphLayout = ui.GlyphLayout{
ByteOffsets: []int{0, 7, 14}, // Start of each line
X: []ui.Dp{10, 10, 10},
Y: []ui.Dp{ui.Dp(0), ui.Dp(lineHeight), ui.Dp(lineHeight * 2)},
Advance: []ui.Dp{10, 10, 10},
}
}); err != nil {
t.Fatalf("WithState: %v", err)
}
// Tap at y=10 (within the text area).
// Because of scroll=16.8, this should correspond to:
// visualLine = (y + scroll) / lineHeight = (10 + 16.8) / 16.8 = 26.8 / 16.8 = 1.59 -> 1
// Line 1 is index 0. Line 2 is index 1.
// So visualLine 1 should be Line 2.
// The problem is that SetCursorFromPoint expects y in DP, but receives it as raw pixels
// if we're not careful. Let's pass the y value properly.
// The test harness sends raw pixel coordinates to handler.
// EditorLayout converts tap point:
// localY := float64(pt.Y - editorRegion.Y + TheState.ScrollOffset)
// So the handler receives Y as pt.Y, where pt.Y is relative to the screen.
// The test harness doesn't seem to account for region offset.
// Let's debug by printing in the test (owner-side read).
v, err := h.Inspect(func(st *editor.State) any { return float64(st.ScrollOffset) })
if err != nil {
t.Fatalf("Inspect: %v", err)
}
t.Logf("ScrollOffset: %v", v)
h.SendInput([]ui.InputEvent{
{
Handler: func(data any) {
if pt, ok := data.(ui.Point); ok {
// Manually simulate the offset correction that EditorLayout does
// editorRegion.Y is margin(10) + statusBarH(32) = 42.
// Let's set pt.Y to 10 + 42 = 52.
localY := float64(pt.Y) - 42.0 + float64(editor.TheState.ScrollOffset)
editor.SetCursorFromPoint(float64(pt.X), localY)
}
},
Data: ui.Point{X: 10, Y: 52},
},
})
// Assert cursor moved to start of Line 2 (offset 7)
time.Sleep(100 * time.Millisecond) // Give logic goroutine a moment to process input
pos, err := h.CursorPosition()
if err != nil {
t.Fatalf("CursorPosition: %v", err)
}
t.Logf("Cursor position: %d", pos)
if pos != 7 {
t.Errorf("expected cursor to move to 7 (Line 2), but got %d", pos)
}
}