Pad/internal/editor/cursor_test.go
Greg Pomerantz bef3929f40 fix: update tests and fix SetCursorFromPoint panic
- Rewrite cursor_test.go to use the current GlyphLayout-based API
  instead of the deleted byteOffsetToLineCol function
- Add guard for empty/missing layout slices in SetCursorFromPoint
  to prevent index-out-of-range panic on empty documents
- Add missing Advance field to e2e test GlyphLayout initialization
- Add cmd/pad/pad binary to .gitignore
2026-06-04 07:24:44 -04:00

92 lines
3.1 KiB
Go

package editor
import (
"testing"
"pad/internal/ui"
)
// TestCursorPositioning_Basic verifies cursor positioning via GlyphLayout.
func TestCursorPositioning_Basic(t *testing.T) {
TheState = NewState()
TheState.Editor.Buffer = "hello\nworld"
// Two lines: "hello\n" (bytes 0-5) and "world" (bytes 6-10)
// Line 0 glyphs at Y=70, Line 1 glyphs at Y=140
TheState.Editor.GlyphLayout = ui.GlyphLayout{
ByteOffsets: []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
X: []ui.Dp{10, 20, 30, 40, 50, 10, 20, 30, 40, 50, 60},
Y: []ui.Dp{70, 70, 70, 70, 70, 70, 140, 140, 140, 140, 140},
Advance: []ui.Dp{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10},
}
// Test tapping on first line near X=30 (glyph index 2, byteOffset 2)
SetCursorFromPoint(25, 70)
if TheState.Editor.CursorPosition < 0 || TheState.Editor.CursorPosition > len(TheState.Editor.Buffer) {
t.Errorf("cursor position %d out of bounds after first tap", TheState.Editor.CursorPosition)
}
// Test tapping on second line
SetCursorFromPoint(35, 140)
if TheState.Editor.CursorPosition < 0 || TheState.Editor.CursorPosition > len(TheState.Editor.Buffer) {
t.Errorf("cursor position %d out of bounds after second tap", TheState.Editor.CursorPosition)
}
}
// TestCursorPositioning_EmptyDocument verifies cursor positioning handles empty documents.
func TestCursorPositioning_EmptyDocument(t *testing.T) {
TheState = NewState()
TheState.Editor.Buffer = ""
TheState.Editor.GlyphLayout = ui.GlyphLayout{}
// Should not panic on empty layout
SetCursorFromPoint(50, 50)
if TheState.Editor.CursorPosition != 0 {
t.Errorf("expected cursor at 0 for empty document, got %d", TheState.Editor.CursorPosition)
}
}
// TestCursorPositioning_IncompleteLayout verifies cursor positioning handles missing layout slices.
func TestCursorPositioning_IncompleteLayout(t *testing.T) {
TheState = NewState()
TheState.Editor.Buffer = "test"
// GlyphLayout with ByteOffsets and X but no Advance (partial data)
TheState.Editor.GlyphLayout = ui.GlyphLayout{
ByteOffsets: []int{0, 1, 2, 3},
X: []ui.Dp{10, 20, 30, 40},
Y: []ui.Dp{70, 70, 70, 70},
// Advance intentionally omitted
}
// Should not panic when Advance is missing
SetCursorFromPoint(25, 70)
}
// TestHandleCursorMove_Bounds verifies cursor movement stays within buffer bounds.
func TestHandleCursorMove_Bounds(t *testing.T) {
TheState = NewState()
TheState.Editor.Buffer = "hello"
TheState.Editor.CursorPosition = 0
// Move right 3 times
HandleCursorMove(1)
HandleCursorMove(1)
HandleCursorMove(1)
if TheState.Editor.CursorPosition != 3 {
t.Errorf("expected cursor at 3, got %d", TheState.Editor.CursorPosition)
}
// Move right past end — should clamp to buffer length
HandleCursorMove(10)
if TheState.Editor.CursorPosition != len(TheState.Editor.Buffer) {
t.Errorf("expected cursor at %d (end of buffer), got %d", len(TheState.Editor.Buffer), TheState.Editor.CursorPosition)
}
// Move left past start — should clamp to 0
HandleCursorMove(-100)
if TheState.Editor.CursorPosition != 0 {
t.Errorf("expected cursor at 0, got %d", TheState.Editor.CursorPosition)
}
}