194 lines
7.1 KiB
Go
194 lines
7.1 KiB
Go
// Fenrir console screen reader.
|
|
// Copyright (c) 2026 Storm Dragon
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.stormux.org/storm/fenrir/internal/command"
|
|
"git.stormux.org/storm/fenrir/internal/config"
|
|
"git.stormux.org/storm/fenrir/internal/input"
|
|
"git.stormux.org/storm/fenrir/internal/punctuation"
|
|
"git.stormux.org/storm/fenrir/internal/screen"
|
|
)
|
|
|
|
func TestSharedSpeechPunctuationAndPreparedCharacterFeedback(t *testing.T) {
|
|
processor, _, err := punctuation.Load([]string{"../../config/punctuation/default.toml"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
voice := &fakeVoice{}
|
|
echo := &echoSpeaker{Speaker: voice, punctuation: processor, punctuationLevel: "some"}
|
|
echo.Say("a/b")
|
|
if echo.last != "a/b" {
|
|
t.Fatal("clipboard echo lost original text", echo.last)
|
|
}
|
|
echo.punctuationLevel = "none"
|
|
echo.say_character('?')
|
|
echo.Say("a/b")
|
|
echo.punctuationLevel = "all"
|
|
echo.say_character('?')
|
|
echo.punctuationLevel = "some"
|
|
echo.say_character('?', " first character in cell path/to")
|
|
if echo.last != "question? first character in cell path/to" {
|
|
t.Fatal("clipboard echo lost raw table header", echo.last)
|
|
}
|
|
got := strings.Join(voice.messages, "|")
|
|
if got != "a slash b|question?|a b|question?|question? first character in cell path slash to" {
|
|
t.Fatal(got)
|
|
}
|
|
}
|
|
|
|
func TestSpeechKeepsEmbeddedPunctuationAndOriginalClipboardText(t *testing.T) {
|
|
processor, _, err := punctuation.Load([]string{"../../config/punctuation/default.toml"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, substitutions := range []bool{false, true} {
|
|
settings := config.Defaults()
|
|
settings.Reading.Emoticons = substitutions
|
|
settings.Reading.RepeatedSymbols = substitutions
|
|
voice := &fakeVoice{}
|
|
echo := &echoSpeaker{Speaker: voice, punctuation: processor, punctuationLevel: "some", settings: &settings}
|
|
for _, trial := range []struct{ input, want string }{
|
|
{"https://stormux.org", "https colon slash slash stormux dot org"},
|
|
{"2026.10.31", "2026 dot 10 dot 31"},
|
|
{"3.14", "3.14"},
|
|
} {
|
|
voice.messages = nil
|
|
echo.Say(trial.input)
|
|
if !reflect.DeepEqual(voice.messages, []string{trial.want}) {
|
|
t.Fatal("speech lost punctuation", trial.input, substitutions, voice.messages)
|
|
}
|
|
if echo.last != trial.input {
|
|
t.Fatal("clipboard echo lost original text", echo.last)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDiffstatSpeechKeepsOriginalClipboardText(t *testing.T) {
|
|
processor, _, err := punctuation.Load([]string{"../../config/punctuation/default.toml"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
settings := config.Defaults()
|
|
settings.Reading.RepeatedSymbols = true
|
|
settings.Reading.RepeatedSymbolsMin = 4
|
|
voice := &fakeVoice{}
|
|
echo := &echoSpeaker{Speaker: voice, punctuation: processor, punctuationLevel: "some", settings: &settings}
|
|
text := "docs/user.md | 7 +++++--"
|
|
echo.Say(text)
|
|
if len(voice.messages) != 1 {
|
|
t.Fatal("missing diffstat speech", voice.messages)
|
|
}
|
|
if got := strings.Join(strings.Fields(voice.messages[0]), " "); got != "docs slash user dot md 7 5 plus dash dash" {
|
|
t.Fatal("diffstat did not summarize", got)
|
|
}
|
|
if echo.last != text {
|
|
t.Fatal("clipboard echo lost original diffstat", echo.last)
|
|
}
|
|
}
|
|
|
|
func TestPackageProgressBarSpeechKeepsOriginalClipboardText(t *testing.T) {
|
|
processor, _, err := punctuation.Load([]string{"../../config/punctuation/default.toml"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
settings := config.Defaults()
|
|
settings.Reading.RepeatedSymbols = true
|
|
voice := &fakeVoice{}
|
|
echo := &echoSpeaker{Speaker: voice, punctuation: processor, punctuationLevel: "some", settings: &settings}
|
|
for _, trial := range []struct{ bar, want string }{
|
|
{strings.Repeat("#", 120), "120 number"},
|
|
{strings.Repeat("#", 110) + strings.Repeat("-", 10), "110 number 10 dash"},
|
|
{strings.Repeat("#", 90) + strings.Repeat("-", 10), "90 number 10 dash"},
|
|
} {
|
|
voice.messages = nil
|
|
text := "(123/189) upgrading nodejs-lts-jod" + strings.Repeat(" ", 157) + "[" + trial.bar + "] 100%"
|
|
echo.Say(text)
|
|
if len(voice.messages) != 1 {
|
|
t.Fatal("missing progress speech", voice.messages)
|
|
}
|
|
want := "123 slash 189 upgrading nodejs dash lts dash jod " + trial.want + " 100 percent"
|
|
if got := strings.Join(strings.Fields(voice.messages[0]), " "); got != want {
|
|
t.Fatal("progress bar did not summarize", got)
|
|
}
|
|
if echo.last != text {
|
|
t.Fatal("clipboard echo lost original progress bar", echo.last)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReviewCharactersUseProfileRegardlessOfLevelAndKeepBoundaries(t *testing.T) {
|
|
processor, _, err := punctuation.Load([]string{"../../config/punctuation/fr.toml"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, level := range punctuation.Levels() {
|
|
source := &fakeSource{snapshot: screen.Snapshot{Terminal: 2, Active: true, Rows: 1, Cols: 2, Cells: []rune("?/")}}
|
|
voice := &fakeVoice{}
|
|
keys := &fakeKeys{poll: func(_ bool, handle func(input.Decision)) error {
|
|
voice.messages = nil // Inspect command feedback after startup.
|
|
for _, id := range []command.ID{command.CurrentChar, command.NextChar, command.NextChar, command.Quit} {
|
|
handle(input.Decision{Command: id, Interrupt: true})
|
|
}
|
|
return nil
|
|
}}
|
|
settings := config.Defaults()
|
|
settings.Reading.Automatic = false
|
|
settings.Reading.PunctuationLevel = level
|
|
settings.Presentation.Sound.Enabled = false
|
|
if err := run(context.Background(), source, keys, voice, nil, RunOptions{Settings: settings, Punctuation: processor}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := "point d'interrogation|barre oblique|end of screen"
|
|
if level == "all" {
|
|
want = "point d'interrogation|barre oblique|end espace of espace screen"
|
|
}
|
|
if got := strings.Join(voice.messages, "|"); got != want {
|
|
t.Fatal(level, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestQuickPunctuationAppliesLiveAndSavesWhileMuted(t *testing.T) {
|
|
processor, _, err := punctuation.Load([]string{"../../config/punctuation/default.toml"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path := filepath.Join(t.TempDir(), "settings.toml")
|
|
settings := config.Defaults()
|
|
settings.Reading.Automatic = false
|
|
voice := &fakeVoice{}
|
|
source := &fakeSource{snapshot: screen.Snapshot{Terminal: 2, Active: true, Rows: 1, Cols: 3, Cells: []rune("a/b")}}
|
|
keys := &fakeKeys{poll: func(_ bool, handle func(input.Decision)) error {
|
|
handle(input.Decision{Command: command.NextQuickCategory})
|
|
handle(input.Decision{Command: command.PreviousQuickValue})
|
|
voice.messages = nil
|
|
handle(input.Decision{Command: command.CurrentLine})
|
|
if !reflect.DeepEqual(voice.messages, []string{"a b"}) {
|
|
t.Fatal("punctuation did not apply live", voice.messages)
|
|
}
|
|
handle(input.Decision{Command: command.Mute})
|
|
handle(input.Decision{Command: command.NextQuickValue})
|
|
handle(input.Decision{Command: command.NextQuickValue})
|
|
handle(input.Decision{Command: command.SaveSettings})
|
|
handle(input.Decision{Command: command.Quit})
|
|
return nil
|
|
}}
|
|
if err := run(context.Background(), source, keys, voice, nil, RunOptions{Settings: settings, Punctuation: processor, ConfigPath: path}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
saved, _, err := config.Load(path, nil)
|
|
if err != nil || saved.Reading.PunctuationLevel != "most" || saved.Reading.Automatic || !saved.Presentation.Speech.Enabled {
|
|
t.Fatal(saved, err)
|
|
}
|
|
}
|