137 lines
4.0 KiB
Go
137 lines
4.0 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/input"
|
|
"git.stormux.org/storm/cthulhu/internal/interaction"
|
|
)
|
|
|
|
type emojiTestSource struct {
|
|
keys *uiTestKeys
|
|
calls chan string
|
|
err error
|
|
}
|
|
|
|
func (source *emojiTestSource) TypeText(_ context.Context, text string) error {
|
|
if source.keys.InternalUISession() != 0 {
|
|
source.calls <- "capture retained"
|
|
} else {
|
|
source.calls <- text
|
|
}
|
|
return source.err
|
|
}
|
|
func TestEmojiHostCancelCheckboxMemoryAndClipboard(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
defer cancel()
|
|
keys := &uiTestKeys{}
|
|
source := &emojiTestSource{keys: keys, calls: make(chan string, 4)}
|
|
copier := &ocrTestCopier{copied: make(chan string, 4)}
|
|
output := &ocrTestOutput{speech: make(chan speechRequest, 64)}
|
|
ui := &internalUI{keys: keys, emojiSource: source, emojiCopier: copier, speech: output, gate: &focusGate{}, events: make(chan input.CommandEvent, 32), done: make(chan struct{})}
|
|
go ui.run(ctx)
|
|
defer func() { cancel(); <-ui.done }()
|
|
wait := func(prefix string) {
|
|
t.Helper()
|
|
for {
|
|
select {
|
|
case message := <-output.speech:
|
|
if len(message.Text) >= len(prefix) && message.Text[:len(prefix)] == prefix {
|
|
return
|
|
}
|
|
case <-ctx.Done():
|
|
t.Fatalf("missing %q", prefix)
|
|
}
|
|
}
|
|
}
|
|
open := func() {
|
|
t.Helper()
|
|
ui.Handle(input.CommandEvent{Command: input.EmojiMenu})
|
|
wait("Emoji Menu. All tab")
|
|
}
|
|
key := func(symbol uint32, text string) {
|
|
ui.Handle(input.CommandEvent{Command: input.InternalUIKey, UISession: 1, Chord: input.CommandChord{Symbol: symbol}, Text: text})
|
|
}
|
|
open()
|
|
key(0xff09, "")
|
|
key(0, "goat")
|
|
key(0xff0d, "")
|
|
wait("Inserted Goat")
|
|
if got := <-source.calls; got != "🐐" {
|
|
t.Fatal(got)
|
|
}
|
|
select {
|
|
case got := <-copier.copied:
|
|
t.Fatalf("default copied: %q", got)
|
|
default:
|
|
}
|
|
|
|
// Toggle copying on, cancel, and verify the next invocation retains the choice.
|
|
open()
|
|
key(0xff09, "")
|
|
key(0xff09, "")
|
|
key(0, " ")
|
|
key(0xff1b, "")
|
|
for keys.InternalUISession() != 0 {
|
|
if ctx.Err() != nil {
|
|
t.Fatal("cancel not processed")
|
|
}
|
|
time.Sleep(time.Millisecond)
|
|
}
|
|
open()
|
|
key(0xff09, "")
|
|
key(0, "astronaut")
|
|
key(0xff0d, "")
|
|
wait("Copied Astronaut to clipboard")
|
|
if got := <-copier.copied; got != "🧑🚀" {
|
|
t.Fatal(got)
|
|
}
|
|
select {
|
|
case got := <-source.calls:
|
|
t.Fatalf("copy mode typed: %q", got)
|
|
default:
|
|
}
|
|
}
|
|
|
|
func TestEmojiTypingFailure(t *testing.T) {
|
|
source := &emojiTestSource{keys: &uiTestKeys{}, calls: make(chan string, 1), err: errors.New("unsupported")}
|
|
output := &ocrTestOutput{speech: make(chan speechRequest, 1)}
|
|
ui := &internalUI{keys: source.keys, emojiSource: source, speech: output, gate: &focusGate{}}
|
|
ui.select_emoji(context.Background(), interaction.Value{Selected: "🐐", Text: "Goat"}, false)
|
|
if got := (<-output.speech).Text; got != "Emoji insertion unavailable" {
|
|
t.Fatal(got)
|
|
}
|
|
}
|
|
|
|
type emojiBrowseKeys struct{ uiTestKeys }
|
|
|
|
func (*emojiBrowseKeys) BrowseNavigationEnabled() bool { return true }
|
|
func TestEmojiBrowseModeBlocksTypingAndAllowsCopy(t *testing.T) {
|
|
keys := &emojiBrowseKeys{}
|
|
source := &emojiTestSource{keys: &keys.uiTestKeys, calls: make(chan string, 1)}
|
|
copier := &ocrTestCopier{copied: make(chan string, 1)}
|
|
output := &ocrTestOutput{speech: make(chan speechRequest, 2)}
|
|
ui := &internalUI{keys: keys, emojiSource: source, emojiCopier: copier, speech: output, gate: &focusGate{}}
|
|
ui.select_emoji(context.Background(), interaction.Value{Selected: "🐐", Text: "Goat"}, false)
|
|
if got := (<-output.speech).Text; got != "Emoji insertion unavailable in browse mode" {
|
|
t.Fatal(got)
|
|
}
|
|
ui.select_emoji(context.Background(), interaction.Value{Selected: "🐐", Text: "Goat"}, true)
|
|
if got := (<-output.speech).Text; got != "Copied Goat to clipboard" {
|
|
t.Fatal(got)
|
|
}
|
|
select {
|
|
case got := <-source.calls:
|
|
t.Fatalf("typed in browse mode: %q", got)
|
|
default:
|
|
}
|
|
if got := <-copier.copied; got != "🐐" {
|
|
t.Fatal(got)
|
|
}
|
|
}
|