96 lines
3.9 KiB
Go
96 lines
3.9 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/atspi"
|
|
)
|
|
|
|
func TestGUITableNormalizedFocusRetainsOwnerAndTitle(t *testing.T) {
|
|
speech := &fakeSpeechQueue{spoken: make(chan string, 16), requests: make(chan speechRequest, 16)}
|
|
adapter := &fakeAdapter{events: make(chan atspi.Event, 16)}
|
|
inputs := &sequencedInputActivity{}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
completed := make(chan struct{})
|
|
go func() {
|
|
processEventsWithInput(ctx, slog.New(slog.DiscardHandler), speech, adapter,
|
|
nil, nil, 0, nil, inputs, nil, nil)
|
|
close(completed)
|
|
}()
|
|
window := atspi.NewObjectID(":1.22", "/preferences")
|
|
cell_event := func(path, name, rowText string, row, column int, eventType string) atspi.Event {
|
|
cell := atspi.NewSnapshot(path, name, "table cell", "", "")
|
|
cell.BusName = window.BusName
|
|
cell.TablePath, cell.TableName = "/preferences/reading", "Reading settings"
|
|
cell.TableRowIndex, cell.TableColumnIndex = row, column
|
|
cell.TablePositionKnown, cell.GUITable = true, true
|
|
cell.TableRowText = rowText
|
|
event := atspi.NewManagedDescendantEvent(eventType, cell.TablePath, cell)
|
|
event.Window = window
|
|
if eventType == atspi.FocusedStateEvent {
|
|
event.Detail1 = 1
|
|
}
|
|
return event
|
|
}
|
|
expect_speech := func(event atspi.Event, sequence uint64, want string) {
|
|
t.Helper()
|
|
inputs.set(sequence)
|
|
adapter.events <- event
|
|
select {
|
|
case request := <-speech.requests:
|
|
if request.Text != want {
|
|
t.Fatalf("%s: got %q, want %q", event.Type, request.Text, want)
|
|
}
|
|
case <-time.After(time.Second):
|
|
t.Fatalf("%s produced no speech", event.Type)
|
|
}
|
|
}
|
|
// The adapter resolves the table's initial focus to a selected cell. GTK
|
|
// need not send another focused-state event as Up/Down changes rows.
|
|
first := cell_event("/reading/0/state", "unchecked", "unchecked, Only speak displayed text", 0, 0, atspi.FocusedStateEvent)
|
|
expect_speech(first, 1, "Reading settings, unchecked, Only speak displayed text")
|
|
second := cell_event("/reading/1/state", "checked", "checked, Break speech into chunks between pauses", 1, 0, atspi.SelectionChangedEvent)
|
|
expect_speech(second, 2, "checked, Break speech into chunks between pauses")
|
|
duplicate := second
|
|
duplicate.Type = atspi.ActiveDescendantEvent
|
|
adapter.events <- duplicate
|
|
right := cell_event("/reading/1/label", "Break speech into chunks between pauses", second.Source.TableRowText, 1, 1, atspi.ActiveDescendantEvent)
|
|
expect_speech(right, 3, "Break speech into chunks between pauses")
|
|
left := second
|
|
left.Type = atspi.ActiveDescendantEvent
|
|
expect_speech(left, 4, "checked")
|
|
up := first
|
|
up.Type = atspi.SelectionChangedEvent
|
|
expect_speech(up, 5, "unchecked, Only speak displayed text")
|
|
// Background tables and another bus cannot inherit the focused table.
|
|
other := second
|
|
other.OriginPath, other.Source.TablePath = "/unrelated", "/unrelated"
|
|
other.Source.Path = "/unrelated/cell"
|
|
adapter.events <- other
|
|
other.OriginPath, other.Source.TablePath = first.OriginPath, first.Source.TablePath
|
|
other.Source.BusName = ":1.99"
|
|
adapter.events <- other
|
|
expect_speech(second, 6, "checked, Break speech into chunks between pauses")
|
|
button := atspi.NewSnapshot("/preferences/apply", "Apply", "button", "", "")
|
|
button.BusName = window.BusName
|
|
focus := atspi.NewEvent(atspi.FocusEvent, button)
|
|
focus.Window = window
|
|
expect_speech(focus, 7, "Apply, button")
|
|
adapter.events <- up // A table selection after leaving the table is noise.
|
|
// Returning through a cell focus event establishes ownership and speaks
|
|
// the table name again, without changing the user's row-reading option.
|
|
first.OriginPath = first.Source.Path
|
|
expect_speech(first, 8, "Reading settings, unchecked, Only speak displayed text")
|
|
expect_speech(second, 9, "checked, Break speech into chunks between pauses")
|
|
cancel()
|
|
<-completed
|
|
}
|