107 lines
3.4 KiB
Go
107 lines
3.4 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package atspi
|
|
|
|
import "testing"
|
|
|
|
func TestEchoEditFieldRequiresEditableMetadata(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
snapshot Snapshot
|
|
want bool
|
|
}{
|
|
{"entry", Snapshot{Role: "text", Editable: true}, true},
|
|
{"web contenteditable", Snapshot{Role: "section", Editable: true}, true},
|
|
{"editable combo", Snapshot{Role: "combo box", Editable: true}, true},
|
|
{"unknown text", Snapshot{Role: "text"}, false},
|
|
{"read only", Snapshot{Role: "text", Editable: true, ReadOnly: true}, false},
|
|
{"password", Snapshot{Role: "password text", Editable: true}, false},
|
|
{"terminal", Snapshot{Role: "terminal", Editable: true}, false},
|
|
{"defunct", Snapshot{Role: "text", Editable: true, Defunct: true}, false},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := IsEchoEditField(tc.snapshot); got != tc.want {
|
|
t.Fatalf("editable=%v want=%v", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestEchoFocusLifetime(t *testing.T) {
|
|
adapter := New()
|
|
source := Snapshot{BusName: ":1.5", Path: "/edit", Role: "text", Editable: true, Focused: true, StatesKnown: true}
|
|
event := Event{Type: FocusedStateEvent, Detail1: 1, Source: source, Window: NewObjectID(source.BusName, "/window"), OriginPath: "/child"}
|
|
if adapter.EditFieldFocused() {
|
|
t.Fatal("unknown focus allowed")
|
|
}
|
|
gain := func() {
|
|
adapter.observeEchoFocus(event)
|
|
if !adapter.EditFieldFocused() {
|
|
t.Fatal("focused editor rejected")
|
|
}
|
|
}
|
|
gain()
|
|
// Background updates and losses do not replace the current editor.
|
|
adapter.retireEchoFocus(FocusedStateEvent, 0, NewObjectID(":1.6", "/edit"))
|
|
other := event
|
|
other.Source.Path = "/other"
|
|
other.ForegroundMismatch = true
|
|
adapter.observeEchoFocus(other)
|
|
if !adapter.EditFieldFocused() {
|
|
t.Fatal("unrelated event retired editor")
|
|
}
|
|
for _, tc := range []struct {
|
|
kind string
|
|
detail int
|
|
id ObjectID
|
|
}{
|
|
{FocusedStateEvent, 0, source.ID()}, {FocusedStateEvent, 0, NewObjectID(source.BusName, "/child")},
|
|
{DefunctStateEvent, 1, source.ID()}, {WindowDestroyedEvent, 0, event.Window}, {WindowDeactivatedEvent, 0, event.Window},
|
|
{FocusedStateEvent, 1, NewObjectID(source.BusName, "/button")},
|
|
} {
|
|
gain()
|
|
adapter.retireEchoFocus(tc.kind, tc.detail, tc.id)
|
|
if adapter.EditFieldFocused() {
|
|
t.Fatalf("stale focus after %s", tc.kind)
|
|
}
|
|
}
|
|
gain()
|
|
readonly := event
|
|
readonly.Type = "object:state-changed:read-only"
|
|
readonly.Source.ReadOnly = true
|
|
adapter.observeEchoFocus(readonly)
|
|
if adapter.EditFieldFocused() {
|
|
t.Fatal("read-only change retained echo")
|
|
}
|
|
gain()
|
|
adapter.foreground.Store(&ForegroundState{Epoch: 1})
|
|
if adapter.EditFieldFocused() {
|
|
t.Fatal("foreground change retained echo")
|
|
}
|
|
adapter.observeEchoFocus(event)
|
|
if adapter.EditFieldFocused() {
|
|
t.Fatal("old event restored echo")
|
|
}
|
|
adapter.foreground.Store(nil)
|
|
gain()
|
|
unknownWindow := event
|
|
unknownWindow.Type = WindowActivatedEvent
|
|
unknownWindow.Window = NewObjectID(":1.6", "/window")
|
|
unknownWindow.Source = Snapshot{BusName: ":1.6", Path: "/window", Role: "frame"}
|
|
adapter.observeEchoFocus(unknownWindow)
|
|
if adapter.EditFieldFocused() {
|
|
t.Fatal("unknown window retained previous editor without X11 metadata")
|
|
}
|
|
gain()
|
|
adapter.SetSecureInputActive(true)
|
|
if adapter.EditFieldFocused() {
|
|
t.Fatal("lock retained echo")
|
|
}
|
|
adapter.SetSecureInputActive(false)
|
|
if adapter.EditFieldFocused() {
|
|
t.Fatal("unlock restored stale echo")
|
|
}
|
|
}
|