Files
cthulhu/internal/app/browser_lists_test.go

98 lines
3.0 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"context"
"fmt"
"testing"
"time"
"git.stormux.org/storm/cthulhu/internal/atspi"
"git.stormux.org/storm/cthulhu/internal/input"
"git.stormux.org/storm/cthulhu/internal/review"
)
type listTestSource struct {
keys *uiTestKeys
selected chan int
count int
}
func (source *listTestSource) ListDocument(context.Context, review.DocumentTarget, int) (review.DocumentListSnapshot, error) {
list := review.DocumentListSnapshot{Selected: source.count - 1}
for index := 0; index < source.count; index++ {
list.Entries = append(list.Entries, review.DocumentListEntry{DocumentPosition: review.DocumentPosition{Snapshot: atspi.Snapshot{Name: fmt.Sprintf("Example %03d", index), Role: "link"}}})
}
return list, nil
}
func (source *listTestSource) SelectDocumentList(_ review.DocumentListSnapshot, index int) error {
if source.keys.InternalUISession() != 0 {
index = -1
}
source.selected <- index
return nil
}
func TestBrowserListNavigationSelectionAndCancellation(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
keys := &uiTestKeys{}
source := &listTestSource{keys: keys, selected: make(chan int, 1), count: 300}
output := &ocrTestOutput{speech: make(chan speechRequest, 16)}
ui := &internalUI{keys: keys, documents: source, speech: output, gate: &focusGate{}, events: make(chan input.CommandEvent, 16), done: make(chan struct{})}
go ui.run(ctx)
defer func() { cancel(); <-ui.done }()
wait := func(want string) {
t.Helper()
select {
case message := <-output.speech:
if message.Text != want {
t.Fatalf("got %q want %q", message.Text, want)
}
case <-ctx.Done():
t.Fatal(ctx.Err())
}
}
send := func(symbol uint32) {
ui.Handle(input.CommandEvent{Command: input.InternalUIKey, UISession: 1, Chord: input.CommandChord{Symbol: symbol}})
}
ui.Handle(input.CommandEvent{Command: input.DocumentListLinks})
wait("Links: 300 items found. Example 299")
send(0xff50)
wait("Example 000")
send(0xff54)
wait("Example 001")
send(0xff0d)
select {
case index := <-source.selected:
if index != 1 {
t.Fatalf("selected %d or keyboard capture retained", index)
}
case <-ctx.Done():
t.Fatal("selection did not finish")
}
ui.Handle(input.CommandEvent{Command: input.DocumentListLinks})
wait("Links: 300 items found. Example 299")
send(0xff1b)
// A second request is processed after Escape, establishing cancellation
// without a timing assumption or a selection callback.
ui.Handle(input.CommandEvent{Command: input.DocumentListLinks})
wait("Links: 300 items found. Example 299")
select {
case <-source.selected:
t.Fatal("Escape selected an item")
default:
}
send(0xff1b)
}
func TestBrowserListCatalogMapsAllOrcaListTypes(t *testing.T) {
for _, binding := range input.DocumentListBindings() {
if documentListTarget(binding) == 0 {
t.Fatalf("unhandled %s", binding.Command)
}
}
}