102 lines
3.6 KiB
Go
102 lines
3.6 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package review
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/atspi"
|
|
)
|
|
|
|
func tooltip_cursor(t *testing.T, focusable, editable, hypertext bool) (*DocumentCursor, caretTextReader) {
|
|
t.Helper()
|
|
reader := caretTextReader{webID("/before"): "Project", webID("/tiptext"): "Project", webID("/after"): "After"}
|
|
node := func(path, role string, children ...atspi.ObjectID) atspi.TreeNode {
|
|
return atspi.TreeNode{Snapshot: atspi.Snapshot{BusName: webID(path).BusName, Path: path,
|
|
Role: role, StatesKnown: true, CharacterCount: len(reader[webID(path)])}, Children: children}
|
|
}
|
|
tip := node("/tip", "tool tip", webID("/tiptext"))
|
|
tip.Snapshot.Focusable, tip.Snapshot.Editable = focusable, editable
|
|
if hypertext {
|
|
// Text around an embedded child must not bypass subtree exclusion.
|
|
tip.Snapshot.CharacterCount = 3
|
|
tip.Snapshot.TextChildren = []atspi.TextChild{{Offset: 1, Child: webID("/tiptext")}}
|
|
reader[webID("/tip")] = "x\ufffcy"
|
|
}
|
|
tree, err := atspi.NewAccessibleTree(webID("/doc"), []atspi.TreeNode{
|
|
node("/doc", "document web", webID("/before"), webID("/tip"), webID("/after")),
|
|
node("/before", "static"), tip, node("/tiptext", "static"), node("/after", "static"),
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cursor, err := NewDocumentCursor(tree)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := cursor.SyncFocus(context.Background(), webID("/before")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return cursor, reader
|
|
}
|
|
|
|
func TestDocumentCaretSkipsPassiveTooltip(t *testing.T) {
|
|
for _, hypertext := range []bool{false, true} {
|
|
for _, command := range []DocumentCaretCommand{CaretNextLine, CaretNextCharacter, CaretNextWord} {
|
|
t.Run(command.String(), func(t *testing.T) {
|
|
cursor, reader := tooltip_cursor(t, false, false, hypertext)
|
|
if command != CaretNextLine {
|
|
cursor.places[0].offset = len("Project") - 1
|
|
}
|
|
move, err := cursor.MoveCaret(context.Background(), reader, command)
|
|
if err != nil || !move.Moved || move.Position.Object.ID() != webID("/after") {
|
|
t.Fatalf("forward: %+v, %v", move, err)
|
|
}
|
|
reverse := CaretPreviousLine
|
|
if command == CaretNextCharacter {
|
|
reverse = CaretPreviousCharacter
|
|
} else if command == CaretNextWord {
|
|
reverse = CaretPreviousWord
|
|
}
|
|
cursor.places[0].offset = 0
|
|
move, err = cursor.MoveCaret(context.Background(), reader, reverse)
|
|
if err != nil || !move.Moved || move.Position.Object.ID() != webID("/before") {
|
|
t.Fatalf("backward: %+v, %v", move, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDocumentCaretPreservesInteractiveTooltip(t *testing.T) {
|
|
for _, focusable := range []bool{false, true} {
|
|
cursor, reader := tooltip_cursor(t, focusable, !focusable, false)
|
|
move, err := cursor.MoveCaret(context.Background(), reader, CaretNextLine)
|
|
if err != nil || !move.Moved || move.Position.Object.ID() != webID("/tiptext") {
|
|
t.Fatalf("interactive tooltip: %+v, %v", move, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReadAllSkipsPassiveTooltip(t *testing.T) {
|
|
cursor, reader := tooltip_cursor(t, false, false, true)
|
|
worker, err := NewWorker(&readAllCapturer{tree: cursor.tree, caretTextReader: reader}, time.Second)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
worker.SyncFocus(webID("/before"))
|
|
result := worker.readAll(context.Background(), cursor)
|
|
if result.Err != nil || len(result.ReadAllLines) != 2 {
|
|
t.Fatalf("Read All: %+v", result)
|
|
}
|
|
for index, want := range []string{"Project", "After"} {
|
|
if got := result.ReadAllLines[index].Contents[0].Text; got != want {
|
|
t.Fatalf("line %d = %q, want %q", index, got, want)
|
|
}
|
|
}
|
|
}
|