118 lines
3.4 KiB
Go
118 lines
3.4 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/atspi"
|
|
"git.stormux.org/storm/cthulhu/internal/review"
|
|
)
|
|
|
|
func TestNativeBrowserDisabledFieldsRemainReadable(t *testing.T) {
|
|
if os.Getenv("CTHULHU_NATIVE_BROWSER") != "1" {
|
|
t.Skip("requires private browser session")
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 25*time.Second)
|
|
defer cancel()
|
|
adapter := atspi.New()
|
|
if err := adapter.Start(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer adapter.Stop(context.Background())
|
|
command := func(args ...string) {
|
|
t.Helper()
|
|
if output, err := exec.CommandContext(ctx, "xdotool", args...).CombinedOutput(); err != nil {
|
|
t.Fatalf("xdotool: %v: %s", err, output)
|
|
}
|
|
}
|
|
windows, err := exec.CommandContext(ctx, "xdotool", "search", "--onlyvisible", "--name", "Cthulhu Browser Probe").Output()
|
|
if err != nil || len(strings.Fields(string(windows))) == 0 {
|
|
t.Fatalf("fixture window: %s %v", windows, err)
|
|
}
|
|
command("windowfocus", strings.Fields(string(windows))[0])
|
|
var focus atspi.ObjectID
|
|
for attempt := 0; attempt < 12 && !focus.Valid(); attempt++ {
|
|
command("windowfocus", strings.Fields(string(windows))[0])
|
|
command("key", "--clearmodifiers", "Tab")
|
|
timer := time.NewTimer(300 * time.Millisecond)
|
|
waitFocus:
|
|
for {
|
|
select {
|
|
case event := <-adapter.Events():
|
|
if (event.Type == atspi.FocusEvent || event.Type == atspi.FocusedStateEvent && event.Detail1 == 1) && event.Source.Name == "Read only answer" {
|
|
focus = event.Source.ID()
|
|
break waitFocus
|
|
}
|
|
case <-timer.C:
|
|
break waitFocus
|
|
case <-ctx.Done():
|
|
t.Fatal(ctx.Err())
|
|
}
|
|
}
|
|
timer.Stop()
|
|
}
|
|
if !focus.Valid() {
|
|
t.Fatal("fixture focus missing")
|
|
}
|
|
// Capture before the transition to exercise invalidation of a retained tree.
|
|
if _, err := adapter.CaptureDocument(ctx, focus); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
command("windowfocus", strings.Fields(string(windows))[0])
|
|
command("key", "F9")
|
|
for {
|
|
select {
|
|
case event := <-adapter.Events():
|
|
if event.Type != "object:state-changed:sensitive" || event.Source.Name != "Answer" || event.Detail1 != 0 {
|
|
continue
|
|
}
|
|
case <-ctx.Done():
|
|
t.Fatal("disabled state update missing: ", ctx.Err())
|
|
}
|
|
break
|
|
}
|
|
worker, err := review.NewWorker(adapter, 2*time.Second)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := worker.Start(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer worker.Stop(context.Background())
|
|
worker.SyncFocus(focus)
|
|
for _, step := range []struct {
|
|
command review.DocumentCaretCommand
|
|
want string
|
|
}{
|
|
{review.CaretDocumentStart, "Before fields"},
|
|
{review.CaretNextLine, ""}, // Keep the named row, then enter its disabled fields.
|
|
{review.CaretNextLine, "Solved"},
|
|
{review.CaretNextCharacter, "o"},
|
|
{review.CaretNextLine, "Confirmed"},
|
|
{review.CaretNextLine, "After fields"},
|
|
{review.CaretPreviousLine, "Confirmed"},
|
|
{review.CaretPreviousLine, "Solved"},
|
|
} {
|
|
if err := worker.MoveDocumentCaret(step.command); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
select {
|
|
case result := <-worker.Results():
|
|
if result.Err != nil || !result.Moved || result.DocumentCaretText != step.want {
|
|
t.Fatalf("%s: text=%q moved=%t err=%v; want %q", step.command,
|
|
result.DocumentCaretText, result.Moved, result.Err, step.want)
|
|
}
|
|
case <-ctx.Done():
|
|
t.Fatal(ctx.Err())
|
|
}
|
|
}
|
|
}
|