// Cthulhu screen reader. // Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // // This program is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free // Software Foundation, either version 3 of the License, or (at your option) // any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // // You should have received a copy of the GNU General Public License along with // this program. If not, see . package presentation import ( "testing" "git.stormux.org/storm/cthulhu/internal/atspi" ) func TestEventTextAppliesPrintableKeyEchoSettings(t *testing.T) { tests := []struct { name string payload string update func(*Options) want string }{ {name: "alphabetic enabled", payload: "a", want: "a"}, {name: "alphabetic disabled", payload: "a", update: func(options *Options) { options.EnableAlphabeticKeys = false }}, {name: "numeric disabled", payload: "7", update: func(options *Options) { options.EnableNumericKeys = false }}, {name: "punctuation disabled", payload: "!", update: func(options *Options) { options.EnablePunctuationKeys = false }}, {name: "symbol uses punctuation setting", payload: "€", update: func(options *Options) { options.EnablePunctuationKeys = false }}, {name: "space enabled", payload: " ", want: "space"}, {name: "space disabled", payload: " ", update: func(options *Options) { options.EnableSpace = false }}, {name: "diacritical enabled", payload: "́", update: func(options *Options) { options.EnableDiacriticalKeys = true }, want: "́"}, {name: "character echo overrides category", payload: "a", update: func(options *Options) { options.EnableAlphabeticKeys = false options.EnableEchoByCharacter = true }, want: "a"}, {name: "key echo disables character echo", payload: "a", update: func(options *Options) { options.EnableKeyEcho = false options.EnableEchoByCharacter = true }}, {name: "multi-character insertion is not a key echo", payload: "pasted text", update: func(options *Options) { options.EnableKeyEcho = false }, want: "pasted text"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { options := DefaultOptions() if test.update != nil { test.update(&options) } event := atspi.NewEventWithPayload( atspi.TextInsertedEvent, atspi.NewSnapshot("/accessible/editor", "", "text", "", ""), test.payload, ) if got := EventTextWithOptions(event, options); got != test.want { t.Fatalf("EventTextWithOptions() = %q, want %q", got, test.want) } }) } } func TestSentenceEchoStopsAtPreviousLine(t *testing.T) { options := DefaultOptions() options.EnableEchoBySentence = true for _, context := range []string{"First line\nSecond line. ", "First line\rSecond line. "} { event := atspi.NewEvent(atspi.TextInsertedEvent, atspi.Snapshot{TextBeforeCaret: context}) event.Payload = " " if got := TypingEchoText(event, "", options); got != "Second line." { t.Fatalf("sentence echo = %q, want only the current sentence", got) } } } func TestTypingEchoTextAppliesWordAndSentenceSettings(t *testing.T) { options := DefaultOptions() options.EnableEchoByWord = true snapshot := atspi.NewSnapshot("/accessible/editor", "", "text", "", "hello ") snapshot.Editable = true snapshot.TextBeforeCaret = "hello " event := atspi.NewEventWithPayload(atspi.TextInsertedEvent, snapshot, " ") if got := TypingEchoText(event, "", options); got != "hello" { t.Fatalf("word echo = %q, want hello", got) } snapshot.TextBeforeCaret = "hello," event = atspi.NewEventWithPayload(atspi.TextInsertedEvent, snapshot, ",") if got := TypingEchoText(event, "", options); got != "hello" { t.Fatalf("punctuation word echo = %q, want hello", got) } snapshot.TextBeforeCaret = "hello " event = atspi.NewEventWithPayload(atspi.TextInsertedEvent, snapshot, " ") if got := TypingEchoText(event, "", options); got != "space" { t.Fatalf("repeated delimiter echo = %q, want space", got) } options.EnableEchoBySentence = true snapshot.TextBeforeCaret = "First. Second. " event = atspi.NewEventWithPayload(atspi.TextInsertedEvent, snapshot, " ") if got := TypingEchoText(event, "", options); got != "Second." { t.Fatalf("sentence echo = %q, want Second.", got) } snapshot.TextBeforeCaret = "" snapshot.Text = "" event = atspi.NewEventWithPayload(atspi.TextInsertedEvent, snapshot, "\n") if got := TypingEchoText(event, "Line without punctuation", options); got != "Line without punctuation" { t.Fatalf("newline sentence echo = %q", got) } options.EnableKeyEcho = false if got := TypingEchoText(event, "Line without punctuation", options); got != "" { t.Fatalf("disabled key echo sentence = %q", got) } }