110 lines
3.7 KiB
Go
110 lines
3.7 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package review
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
"unicode/utf8"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/atspi"
|
|
)
|
|
|
|
func editable_caret_cursor(t *testing.T) (*DocumentCursor, caretTextReader) {
|
|
t.Helper()
|
|
reader := caretTextReader{
|
|
webID("/before"): "Suggested wording",
|
|
webID("/editor"): "\ufffc\ufffc",
|
|
webID("/first"): "Review the code.\nReport actionable issues.",
|
|
webID("/second"): "Explain the evidence.",
|
|
webID("/secret"): "Private fixture text",
|
|
webID("/after"): "Following paragraph",
|
|
}
|
|
node := func(path, role string, editable bool, children ...string) atspi.TreeNode {
|
|
snapshot := atspi.NewSnapshot(path, "", role, "", "")
|
|
snapshot.BusName = webID(path).BusName
|
|
snapshot.Editable = editable
|
|
snapshot.CharacterCount = utf8.RuneCountInString(reader[webID(path)])
|
|
result := atspi.TreeNode{Snapshot: snapshot}
|
|
for _, child := range children {
|
|
result.Children = append(result.Children, webID(child))
|
|
}
|
|
return result
|
|
}
|
|
tree, err := atspi.NewAccessibleTree(webID("/doc"), []atspi.TreeNode{
|
|
node("/doc", "document web", false, "/before", "/editor", "/password", "/after"),
|
|
node("/before", "static", false),
|
|
node("/editor", "entry", true, "/paragraph", "/second"),
|
|
node("/paragraph", "paragraph", true, "/first"),
|
|
node("/first", "static", true),
|
|
node("/second", "static", true),
|
|
node("/password", "password text", true, "/secret"),
|
|
// Even a provider which omits editable state on a password child
|
|
// must not expose that child's text through browse navigation.
|
|
node("/secret", "static", false),
|
|
node("/after", "static", false),
|
|
})
|
|
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 TestDocumentCaretReadsEditableDescendants(t *testing.T) {
|
|
ctx := context.Background()
|
|
cursor, reader := editable_caret_cursor(t)
|
|
for _, step := range []struct {
|
|
command DocumentCaretCommand
|
|
want string
|
|
}{
|
|
{CaretNextLine, "Review the code."},
|
|
{CaretNextLine, "Report actionable issues."},
|
|
{CaretNextLine, "Explain the evidence."},
|
|
{CaretNextLine, "Following paragraph"},
|
|
{CaretPreviousLine, "Explain the evidence."},
|
|
{CaretPreviousLine, "Report actionable issues."},
|
|
{CaretLineStart, "R"},
|
|
{CaretNextCharacter, "e"},
|
|
{CaretNextWord, "Report"},
|
|
{CaretPreviousLine, "Review the code."},
|
|
{CaretPreviousLine, "Suggested wording"},
|
|
} {
|
|
move, err := cursor.MoveCaret(ctx, reader, step.command)
|
|
if err != nil || !move.Moved || strings.TrimSpace(move.Text) != step.want {
|
|
t.Fatalf("%s: %q at %s, moved %t, %v; want %q", step.command, move.Text,
|
|
move.Position.Snapshot.Path, move.Moved, err, step.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReadAllIncludesEditableDescendants(t *testing.T) {
|
|
cursor, reader := editable_caret_cursor(t)
|
|
capturer := &readAllCapturer{tree: cursor.tree, caretTextReader: reader}
|
|
worker, err := NewWorker(capturer, time.Second)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
worker.SyncFocus(webID("/before"))
|
|
result := worker.readAll(context.Background(), cursor)
|
|
want := []string{"Suggested wording", "Review the code.", "Report actionable issues.", "Explain the evidence.", "Following paragraph"}
|
|
if result.Err != nil || len(result.ReadAllLines) != len(want) {
|
|
t.Fatalf("Read All: %d lines, %v; want %d", len(result.ReadAllLines), result.Err, len(want))
|
|
}
|
|
for index, line := range result.ReadAllLines {
|
|
if got := strings.TrimSpace(line.Contents[0].Text); got != want[index] {
|
|
t.Fatalf("Read All line %d: %q; want %q", index, got, want[index])
|
|
}
|
|
}
|
|
}
|