Files
cthulhu/internal/review/document_tooltip_test.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)
}
}
}