// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package presentation import ( "fmt" "testing" "git.stormux.org/storm/cthulhu/internal/atspi" ) func TestTypingEchoModeCombinations(t *testing.T) { for mask := 0; mask < 8; mask++ { for _, enabled := range []bool{false, true} { t.Run(fmt.Sprintf("modes=%03b/enabled=%v", mask, enabled), func(t *testing.T) { options := DefaultOptions() options.EnableKeyEcho = enabled options.EnableEchoByCharacter = mask&1 != 0 options.EnableEchoByWord = mask&2 != 0 options.EnableEchoBySentence = mask&4 != 0 // With printable key categories disabled, each mode must still // work independently. Sentence completion takes precedence. options.EnableAlphabeticKeys = false options.EnableNumericKeys = false options.EnablePunctuationKeys = false options.EnableSpace = false for _, tc := range []struct { payload, context, word, sentence string }{ {"é", "Café", "", ""}, {"7", "7", "", ""}, {"😀", "😀", "", ""}, {" ", "Café ", "Café", ""}, {",", "Café,", "Café", ""}, {" ", "First. Second. ", "", "Second."}, {"\n", "First. Last line\n", "line", "Last line"}, {" ", "Café ", "", ""}, } { want, wantKind := "", "key" if options.EnableEchoByCharacter { wantKind = "character" if tc.payload != "\n" { want = tc.payload if want == " " { want = "space" } } } if options.EnableEchoByWord && tc.word != "" { want, wantKind = tc.word, "word" } if options.EnableEchoBySentence && tc.sentence != "" { want, wantKind = tc.sentence, "sentence" } if !enabled { want = "" } event := atspi.Event{Type: atspi.TextInsertedEvent, Payload: tc.payload, Source: atspi.Snapshot{Role: "entry", Editable: true, TextBeforeCaret: tc.context}} text, kind := TypingEcho(event, "", options) if text != want || text != "" && kind != wantKind { t.Errorf("payload=%q context=%q: (%q, %q), want (%q, %q)", tc.payload, tc.context, text, kind, want, wantKind) } } }) } } } func TestTypingEchoLineBreakBeforeExistingText(t *testing.T) { for _, mode := range []string{"word", "sentence"} { for _, tc := range []struct { name, payload, previous, following, want string }{ {"split line", "\n", "Hello", "following text", "Hello"}, {"split line with tab", "\t", "Hello", "\tfollowing text", "Hello"}, {"start of line", "\n", "", "following text", ""}, {"blank line", "\n", "", "", ""}, {"unicode", "\n", "Café", "following text", "Café"}, } { t.Run(mode+"/"+tc.name, func(t *testing.T) { options := DefaultOptions() options.EnableEchoByWord = mode == "word" options.EnableEchoBySentence = mode == "sentence" event := atspi.Event{Type: atspi.TextInsertedEvent, Payload: tc.payload, Source: atspi.Snapshot{Role: "entry", Editable: true, TextCaretKnown: true, Text: tc.following}} want := tc.want if mode == "sentence" && tc.payload == "\t" { want = "" // A tab only completes a sentence after punctuation. } if got := TypingEchoText(event, tc.previous, options); got != want { t.Fatalf("echo = %q, want %q", got, want) } }) } } }