Pad/internal/editor/integration_test.go
Greg Pomerantz 72b3c3f9c1 editor+ui: complete Android IME wiring (SelectionCmd, SnippetCmd, InputHintOp)
Finish Phase 1 items 1, 2, 4, on top of the range-handling done in 9b78219:

- TextField.Draw now emits, when focused:
  * key.InputHintOp{HintText}        (item 4: enable text keyboard/autocorrect)
  * key.SnippetCmd (the visible window as the snippet, Range {0,len})
                                              (item 2: swipe/autocorrect source)
  * key.SelectionCmd (caret, window-relative rune index)
                                              (item 1: IME selection sync)
  The snippet is the visible window (not the whole file), so the IME treats
  the window as the document and reports EditEvent.Range window-relative.

- HandleReplaceRange now resolves the window-relative range against
  IMEWindowText and offsets by IMEWindowStartByte to address the buffer
  (string and chunked paths). Falls back to the whole buffer when layout
  has not set the window (tests).

- EditorState gains IMEWindowStartByte / IMEWindowText, set during layout.

- Add runeCount helper (utf8 leading-byte scan) in the ui package.

- Fix a data race in three editor e2e/integration tests: they called
  OpenFile from the test goroutine while the logic goroutine ran layout;
  now wrapped in withState so the state write happens on the owner.

Tests: ime_range_test.go gains windowed-path coverage (string+chunked).
go build, go test, go vet, and go test -race are all green.
2026-08-16 02:43:58 -04:00

57 lines
1.2 KiB
Go

package editor
import (
"testing"
"time"
"pad/internal/io/pool/mock"
)
func TestOpenFileIntegration(t *testing.T) {
// 1. Setup Logic
mockFS := mock.NewFileSystem()
filename := "/README.md"
content := "Hello World"
mockFS.AddFile(filename, []byte(content), time.Now())
l := NewLogic(mockFS, "/", func(string) {})
go l.Run()
defer l.Shutdown()
// Drain frameChan to prevent deadlocks
go func() {
for range l.FrameChan() {
}
}()
// Initialize global state on the owner
withState(t, l, func(st *State) {
TheState = st
})
// 2. Open File
// OpenFile dispatches the task to openFileChan, which Run() will consume
withState(t, l, func(st *State) {
TheState = st
OpenFile(filename)
})
// 3. Process the Result
// We wait for the state to update, which happens when ReadChunkTask completes
success := false
for i := 0; i < 20; i++ {
v, ok := l.Inspect(func(st *State) any { return st.Editor.GetBuffer() })
if ok && v.(string) == content {
success = true
break
}
time.Sleep(100 * time.Millisecond)
}
// 4. Assert
if !success {
got, _ := l.Inspect(func(st *State) any { return st.Editor.Buffer })
t.Errorf("Expected content %q, got %q", content, got)
}
}