95 lines
3.2 KiB
Go
95 lines
3.2 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package atspi
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestNativeBrowserLogicalLines(t *testing.T) {
|
|
if os.Getenv("CTHULHU_NATIVE_BROWSER") != "1" {
|
|
t.Skip("requires private browser session")
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
|
|
defer cancel()
|
|
adapter := New()
|
|
if err := adapter.Start(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer adapter.Stop(context.Background())
|
|
if out, err := exec.CommandContext(ctx, "xdotool", "key", "--clearmodifiers", "Tab").CombinedOutput(); err != nil {
|
|
t.Fatalf("focus editable lines: %v %s", err, out)
|
|
}
|
|
var focus ObjectID
|
|
for !focus.Valid() {
|
|
select {
|
|
case event := <-adapter.Events():
|
|
if (event.Type == FocusEvent || event.Type == FocusedStateEvent && event.Detail1 == 1) && event.Source.Name == "Editable lines" {
|
|
focus = event.Source.ID()
|
|
}
|
|
case <-ctx.Done():
|
|
t.Fatal(ctx.Err())
|
|
}
|
|
}
|
|
tree, err := adapter.CaptureDocument(ctx, focus)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
text := findNamedReference(t, tree, "What's on your mind?")
|
|
for _, offset := range []int{0, 7, 10, 15, 20} {
|
|
line, err := adapter.ReadDocumentCaret(ctx, text, offset, DocumentCaretBrailleLine)
|
|
if err != nil || line.Start != 0 || line.End != 20 || line.Text != "What's on your mind?" {
|
|
t.Fatalf("line at %d = %+v, %v", offset, line, err)
|
|
}
|
|
}
|
|
remainder, err := adapter.ReadDocumentCaret(ctx, text, 7, DocumentCaretLineRemainder)
|
|
if err != nil || remainder.Start != 7 || remainder.Text != "on your mind?" {
|
|
t.Fatalf("remainder = %+v, %v", remainder, err)
|
|
}
|
|
for _, test := range []struct{ name, first string }{
|
|
{"First Second", "First"}, {"Code text", "Code"}, {"Editable lines", "First"},
|
|
} {
|
|
ref := findNamedReference(t, tree, test.name)
|
|
line, err := adapter.ReadDocumentCaret(ctx, ref, 0, DocumentCaretBrailleLine)
|
|
if err != nil || strings.TrimSpace(line.Text) != test.first {
|
|
t.Fatalf("preserved %s line = %+v, %v", test.name, line, err)
|
|
}
|
|
}
|
|
for _, name := range []string{"Truncated preview…", "猫🐈…", "…"} {
|
|
ref := findNamedReference(t, tree, name)
|
|
if !ref.Valid() {
|
|
t.Fatalf("ellipsis object %q missing", name)
|
|
}
|
|
offset := len([]rune(name)) - 1
|
|
for _, unit := range []DocumentCaretUnit{DocumentCaretLine, DocumentCaretBrailleLine, DocumentCaretLineRemainder} {
|
|
line, err := adapter.ReadDocumentCaret(ctx, ref, offset, unit)
|
|
if err != nil || line.End != offset+1 || !strings.HasSuffix(line.Text, "…") {
|
|
t.Fatalf("ellipsis %q unit %d = %+v, %v", name, unit, line, err)
|
|
}
|
|
}
|
|
if err := adapter.SetDocumentCaretPosition(ctx, ref, focus, offset); err != nil {
|
|
t.Fatalf("place caret on ellipsis: %v", err)
|
|
}
|
|
// Drain the actual native caret event, exercising snapshot text,
|
|
// character/word capture and logical-line repair before proceeding.
|
|
for {
|
|
select {
|
|
case event := <-adapter.Events():
|
|
if event.Type == CaretEvent && strings.HasSuffix(event.Source.BrailleLine, "…") {
|
|
goto caretCaptured
|
|
}
|
|
case <-ctx.Done():
|
|
t.Fatal("ellipsis caret event missing: ", ctx.Err())
|
|
}
|
|
}
|
|
caretCaptured:
|
|
}
|
|
}
|