Frame now carries view state (scale/focus/query) to the main goroutine, which reads only the frame-receiver-stored snapshot. Renderer owns Gio widget editors (registered by ID) and the draw scale. Autosave timer sends a token to the logic goroutine instead of touching state; Shutdown waits for the owner to exit before FlushAll. Tests access state only through owner-side Inspect/WithState helpers (harness + in-package). Fixed double-Harness.Run race in two e2e tests, made TestWorkerPool_PriorityPreemption deterministic, and raised the lazy-loading test timeout that was too short under -race. go test -race ./... is now green.
83 lines
2.3 KiB
Go
83 lines
2.3 KiB
Go
package e2e_test
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"pad/internal/editor"
|
|
"pad/internal/test/e2e"
|
|
"pad/internal/ui"
|
|
)
|
|
|
|
// TestEditorClickToMoveCursor tests that clicking/tapping in the editor moves the cursor.
|
|
//
|
|
// All state access goes through the harness's owner-side helpers
|
|
// (WithState / CursorPosition): the logic goroutine is the sole owner of
|
|
// State (architecture.md §1).
|
|
func TestEditorClickToMoveCursor(t *testing.T) {
|
|
h := e2e.NewHarnessWithDefaults()
|
|
defer h.Cleanup()
|
|
|
|
// Switch to editor page and load some text
|
|
if err := h.WithState(func(st *editor.State) { editor.GoToEditor(nil) }); err != nil {
|
|
t.Fatalf("GoToEditor: %v", err)
|
|
}
|
|
h.SendConfig(780, 1688)
|
|
|
|
// Wait for frame to ensure page switch
|
|
_, err := e2e.WaitForNewFrame(h, h.FrameCount(), 5*time.Second)
|
|
if err != nil {
|
|
t.Fatalf("timeout waiting for frame: %v", err)
|
|
}
|
|
|
|
// Initialize GlyphLayout for the test (owner-side write)
|
|
if err := h.WithState(func(st *editor.State) {
|
|
st.Editor.GlyphLayout = ui.GlyphLayout{
|
|
ByteOffsets: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
|
|
X: []ui.Dp{10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120},
|
|
Y: []ui.Dp{70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 70},
|
|
Advance: []ui.Dp{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
|
|
}
|
|
}); err != nil {
|
|
t.Fatalf("set GlyphLayout: %v", err)
|
|
}
|
|
|
|
// Initial cursor position should be 0 (or end of text, depending on implementation)
|
|
// For this test, let's assume it starts at 0.
|
|
pos, err := h.CursorPosition()
|
|
if err != nil {
|
|
t.Fatalf("CursorPosition: %v", err)
|
|
}
|
|
if pos != 0 {
|
|
t.Errorf("expected initial cursor 0, got %d", pos)
|
|
}
|
|
|
|
// Simulate tap at a position that should move the cursor.
|
|
// We use SendInput to simulate the tap event handler for the editor's text field.
|
|
h.SendInput([]ui.InputEvent{
|
|
{
|
|
Handler: func(data any) {
|
|
if pt, ok := data.(ui.Point); ok {
|
|
editor.SetCursorFromPoint(float64(pt.X), float64(pt.Y))
|
|
}
|
|
},
|
|
Data: ui.Point{X: 30, Y: 70},
|
|
},
|
|
})
|
|
|
|
// Wait for potential re-render
|
|
_, err = e2e.WaitForNewFrame(h, h.FrameCount(), 1*time.Second)
|
|
if err != nil {
|
|
t.Logf("no new frame after tap, checking state anyway")
|
|
}
|
|
|
|
// Assert cursor moved
|
|
pos, err = h.CursorPosition()
|
|
if err != nil {
|
|
t.Fatalf("CursorPosition: %v", err)
|
|
}
|
|
if pos == 0 {
|
|
t.Errorf("expected cursor to move from 0, but it remained at 0")
|
|
}
|
|
}
|