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.
109 lines
3.5 KiB
Go
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)
|
|
}
|
|
}
|