// Fenrir console screen reader. // Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package emoji import ( "strings" "testing" "git.stormux.org/storm/fenrir/internal/i18n" "git.stormux.org/storm/fenrir/internal/interaction" ) func TestCategorySelectionAndIndependentSearches(t *testing.T) { session, err := New_session() if err != nil { t.Fatal(err) } if _, _, ok := Selection(session); ok { t.Fatal("tab strip has a selection") } session.Handle(interaction.Key{Name: "tab"}) session.Handle(interaction.Key{Text: "j"}) session.Handle(interaction.Key{Name: "right"}) if value, _, _ := Selection(session); value != "πŸ‡―πŸ‡΅" { t.Fatal("filtered All navigation", value) } session.Handle(interaction.Key{Name: "tab"}) session.Handle(interaction.Key{Name: "right"}) session.Handle(interaction.Key{Name: "right"}) session.Handle(interaction.Key{Name: "tab"}) session.Handle(interaction.Key{Text: "c"}) if value, _, _ := Selection(session); value != "🐱" { t.Fatal("Nature selection came from another tab", value) } session.Handle(interaction.Key{Name: "tab", Shift: true}) session.Handle(interaction.Key{Name: "left"}) session.Handle(interaction.Key{Name: "left"}) session.Handle(interaction.Key{Name: "tab"}) if value, _, _ := Selection(session); value != "πŸ‡―πŸ‡΅" { t.Fatal("All search/selection not retained", value) } other, err := New_session() if err != nil { t.Fatal(err) } other.Handle(interaction.Key{Name: "tab"}) control, controlOK := other.Focused_control() value, _, selectionOK := Selection(other) if !controlOK || control.Text != "" || !selectionOK || value == "πŸ‡―πŸ‡΅" { t.Fatal("new session inherited previous search") } } func TestBundledTranslationSearchPreservesEmoji(t *testing.T) { previous := i18n.Use(i18n.New(t.TempDir(), "de")) t.Cleanup(func() { i18n.Use(previous) }) session, err := New_session() if err != nil { t.Fatal(err) } if got := session.Open(); !strings.HasPrefix(got, "Emoji-MenΓΌ. Alle Registerkarte") { t.Fatal("untranslated picker", got) } session.Handle(interaction.Key{Name: "tab"}) session.Handle(interaction.Key{Text: "Schmetter"}) if value, label, ok := Selection(session); !ok || value != "πŸ¦‹" || label != "Butterfly" { t.Fatalf("localized search changed identity: %q %q %v", value, label, ok) } } func TestExpandedCatalogCategoriesAndUniqueEmojiIDs(t *testing.T) { expectedCategories := map[string]bool{ "activity": false, "travel": false, "objects": false, } emojiIDs := make(map[string]bool) presentationIDs := make(map[string]string) stripPresentation := strings.NewReplacer("\ufe0e", "", "\ufe0f", "") for _, category := range categories { if _, ok := expectedCategories[category.id]; ok { expectedCategories[category.id] = true } if category.id == "" || category.label == "" || len(category.choices) == 0 { t.Fatalf("invalid category %#v", category) } for _, choice := range category.choices { if choice.ID == "" || choice.Label == "" { t.Fatalf("invalid choice %#v", choice) } if emojiIDs[choice.ID] { t.Fatalf("duplicate emoji ID %q", choice.ID) } emojiIDs[choice.ID] = true // Text/emoji presentation selectors do not make a distinct emoji. presentationID := stripPresentation.Replace(choice.ID) if previous, ok := presentationIDs[presentationID]; ok { t.Fatalf("presentation-equivalent emoji IDs %q and %q", previous, choice.ID) } presentationIDs[presentationID] = choice.ID } } for id, found := range expectedCategories { if !found { t.Fatal("missing category", id) } } if len(emojiIDs) != 501 { t.Fatal("expanded catalog has the wrong size", len(emojiIDs)) } if !emojiIDs["🐐"] || !emojiIDs["πŸŽ‰"] { t.Fatal("goat or party popper emoji missing") } } func TestNewCategoryNavigation(t *testing.T) { session, err := New_session() if err != nil { t.Fatal(err) } for range 4 { session.Handle(interaction.Key{Name: "right"}) } session.Handle(interaction.Key{Name: "tab"}) if value, label, ok := Selection(session); !ok || value != "⚽" || label != "Soccer ball" { t.Fatalf("Activity navigation failed: %q %q %v", value, label, ok) } }