// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package interaction import "testing" func secretForm(text string) Form { return Form{Title: "Connection", Elements: []Element{ {ID: "key", Label: "Channel key", Kind: Entry, Secret: true, Text: text, Required: true, MaxLength: 12}, {ID: "connect", Label: "Connect", Kind: Button}, }} } func TestSecretEntryEditingAndPresentation(t *testing.T) { session, err := NewSession(secretForm("猫é123")) if err != nil { t.Fatal(err) } if got := session.Opening(); got.Speech != "Connection. Channel key, password edit, 5 characters" || got.Braille != "Channel key, password edit: *****" || got.Cursor != 33 { t.Fatalf("unexpected masked opening: %+v", got) } steps := []struct { event Event speech, text, braille string }{ {Event{Key: Home}, "Hidden character", "猫é123", "Channel key, password edit: *****"}, {Event{Key: Right, Shift: true}, "Hidden character, selected", "猫é123", "Channel key, password edit: ***** (selected 1 characters)"}, {Event{Text: "密"}, "Inserted 1 character", "密é123", "Channel key, password edit: *****"}, {Event{Key: Delete}, "Deleted 1 character", "密123", "Channel key, password edit: ****"}, {Event{Key: Backspace}, "Deleted 1 character", "123", "Channel key, password edit: ***"}, {Event{Key: End}, "End of text", "123", "Channel key, password edit: ***"}, {Event{Text: "a", Control: true}, "Selected all", "123", "Channel key, password edit: *** (selected 3 characters)"}, {Event{Text: "c", Control: true}, "Copy and cut are unavailable for password fields", "123", "Channel key, password edit: *** (selected 3 characters)"}, {Event{Text: "x", Control: true}, "Copy and cut are unavailable for password fields", "123", "Channel key, password edit: *** (selected 3 characters)"}, {Event{Key: Backspace}, "Deleted 3 characters", "", "Channel key, password edit: "}, {Event{Key: Enter}, "Channel key is required", "", "Channel key is required"}, } for index, step := range steps { effect := session.Handle(step.event) if effect.Speech != step.speech || effect.Braille != step.braille || effect.Copy != nil || effect.Activated != "" || session.Values()["key"].Text != step.text { t.Fatalf("secret editing step %d failed", index) } } if !session.Handle(Event{Text: "v", Control: true}).Paste { t.Fatal("explicit paste not requested") } if effect := session.Insert("pasted猫"); effect.Speech != "Inserted 7 characters" || effect.Braille != "Channel key, password edit: *******" { t.Fatal("paste was not masked") } for _, text := range []string{"oversized content", "bad\ntext", string([]byte{0xff})} { session.Insert(text) if session.Values()["key"].Text != "pasted猫" { t.Fatal("invalid paste modified secret") } } session.Handle(Event{Key: Tab}) if got := session.Handle(Event{Key: Tab, Shift: true}); got.Speech != "Channel key, password edit, 7 characters" { t.Fatal("returning to password did not mask it") } session.Handle(Event{Key: Tab}) if effect := session.Handle(Event{Key: Enter}); effect.Activated != "connect" || session.Values()["key"].Text != "pasted猫" { t.Fatal("submission lost the actual secret") } } func TestSecretEntryCloseAndValidation(t *testing.T) { for _, escape := range []bool{false, true} { session, _ := NewSession(secretForm("fixture")) if escape { if !session.Handle(Event{Key: Escape}).Cancelled { t.Fatal("escape did not cancel") } } else { session.Close() } session.Close() if session.Values() != nil || session.elements[0].element.Text != "" || session.Presentation() != (Presentation{}) || session.Insert("more") != (Effect{}) { t.Fatal("closed editor retained or accepted a secret") } } for _, kind := range []Kind{List, CheckBox, Button} { form := secretForm("") form.Elements[0].Kind = kind if _, err := NewSession(form); err == nil { t.Fatal("secret accepted on non-entry") } } if err := ValidatePreferences(secretForm("fixture")); err == nil { t.Fatal("secret accepted in persistent preferences") } }