From 13008225c0171777b5986fccfcddad52a2585205 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Mon, 15 Sep 2025 15:59:25 +0800 Subject: [PATCH] test: add basic unit test, fix a search text issue. --- .../MultiAutoCompleteBox.Properties.cs | 2 +- .../AutoCompleteBox/MultiAutoCompleteBox.cs | 7 - .../MultiAutoCompleteBoxTests.cs | 720 ++++++++++++++++++ 3 files changed, 721 insertions(+), 8 deletions(-) create mode 100644 tests/HeadlessTest.Ursa/Controls/AutoCompleteBoxTests/MultiAutoCompleteBoxTests.cs diff --git a/src/Ursa/Controls/AutoCompleteBox/MultiAutoCompleteBox.Properties.cs b/src/Ursa/Controls/AutoCompleteBox/MultiAutoCompleteBox.Properties.cs index 5e1b325..2736134 100644 --- a/src/Ursa/Controls/AutoCompleteBox/MultiAutoCompleteBox.Properties.cs +++ b/src/Ursa/Controls/AutoCompleteBox/MultiAutoCompleteBox.Properties.cs @@ -431,7 +431,7 @@ public partial class MultiAutoCompleteBox /// The search mode is automatically set to Custom if you set the /// TextFilter property. /// - public AutoCompleteFilterPredicate? TextFilter + public AutoCompleteFilterPredicate TextFilter { get => GetValue(TextFilterProperty); set => SetValue(TextFilterProperty, value); diff --git a/src/Ursa/Controls/AutoCompleteBox/MultiAutoCompleteBox.cs b/src/Ursa/Controls/AutoCompleteBox/MultiAutoCompleteBox.cs index 4b66808..ae53ca5 100644 --- a/src/Ursa/Controls/AutoCompleteBox/MultiAutoCompleteBox.cs +++ b/src/Ursa/Controls/AutoCompleteBox/MultiAutoCompleteBox.cs @@ -1257,15 +1257,8 @@ public partial class MultiAutoCompleteBox : TemplatedControl, IInnerContentContr } else { - // TODO implement selection. - /* SearchText = string.Empty; - if (SelectedItem != null) _skipSelectedItemTextUpdate = true; - - SetCurrentValue(SelectedItemProperty, null); - if (IsDropDownOpen) SetCurrentValue(IsDropDownOpenProperty, false); - */ } } diff --git a/tests/HeadlessTest.Ursa/Controls/AutoCompleteBoxTests/MultiAutoCompleteBoxTests.cs b/tests/HeadlessTest.Ursa/Controls/AutoCompleteBoxTests/MultiAutoCompleteBoxTests.cs new file mode 100644 index 0000000..1c46feb --- /dev/null +++ b/tests/HeadlessTest.Ursa/Controls/AutoCompleteBoxTests/MultiAutoCompleteBoxTests.cs @@ -0,0 +1,720 @@ +using System.Collections.ObjectModel; +using Avalonia.Controls; +using Avalonia.Headless.XUnit; +using Avalonia.Threading; +using Avalonia.VisualTree; +using HeadlessTest.Ursa.TestHelpers; +using Ursa.Controls; +using AutoCompleteBox = Avalonia.Controls.AutoCompleteBox; + +namespace HeadlessTest.Ursa.Controls.AutoCompleteBoxTests; + +public class MultiAutoCompleteBoxTests +{ + [AvaloniaFact] + public void Search_Filters() + { + Assert.True(GetFilter(AutoCompleteFilterMode.Contains)("am", "name")); + Assert.True(GetFilter(AutoCompleteFilterMode.Contains)("AME", "name")); + Assert.False(GetFilter(AutoCompleteFilterMode.Contains)("hello", "name")); + + Assert.True(GetFilter(AutoCompleteFilterMode.ContainsCaseSensitive)("na", "name")); + Assert.False(GetFilter(AutoCompleteFilterMode.ContainsCaseSensitive)("AME", "name")); + Assert.False(GetFilter(AutoCompleteFilterMode.ContainsCaseSensitive)("hello", "name")); + + Assert.Null(GetFilter(AutoCompleteFilterMode.Custom)); + Assert.Null(GetFilter(AutoCompleteFilterMode.None)); + + Assert.True(GetFilter(AutoCompleteFilterMode.Equals)("na", "na")); + Assert.True(GetFilter(AutoCompleteFilterMode.Equals)("na", "NA")); + Assert.False(GetFilter(AutoCompleteFilterMode.Equals)("hello", "name")); + + Assert.True(GetFilter(AutoCompleteFilterMode.EqualsCaseSensitive)("na", "na")); + Assert.False(GetFilter(AutoCompleteFilterMode.EqualsCaseSensitive)("na", "NA")); + Assert.False(GetFilter(AutoCompleteFilterMode.EqualsCaseSensitive)("hello", "name")); + + Assert.True(GetFilter(AutoCompleteFilterMode.StartsWith)("na", "name")); + Assert.True(GetFilter(AutoCompleteFilterMode.StartsWith)("NAM", "name")); + Assert.False(GetFilter(AutoCompleteFilterMode.StartsWith)("hello", "name")); + + Assert.True(GetFilter(AutoCompleteFilterMode.StartsWithCaseSensitive)("na", "name")); + Assert.False(GetFilter(AutoCompleteFilterMode.StartsWithCaseSensitive)("NAM", "name")); + Assert.False(GetFilter(AutoCompleteFilterMode.StartsWithCaseSensitive)("hello", "name")); + } + + [AvaloniaFact] + public void Ordinal_Search_Filters() + { + Assert.True(GetFilter(AutoCompleteFilterMode.ContainsOrdinal)("am", "name")); + Assert.True(GetFilter(AutoCompleteFilterMode.ContainsOrdinal)("AME", "name")); + Assert.False(GetFilter(AutoCompleteFilterMode.ContainsOrdinal)("hello", "name")); + + Assert.True(GetFilter(AutoCompleteFilterMode.ContainsOrdinalCaseSensitive)("na", "name")); + Assert.False(GetFilter(AutoCompleteFilterMode.ContainsOrdinalCaseSensitive)("AME", "name")); + Assert.False(GetFilter(AutoCompleteFilterMode.ContainsOrdinalCaseSensitive)("hello", "name")); + + Assert.True(GetFilter(AutoCompleteFilterMode.EqualsOrdinal)("na", "na")); + Assert.True(GetFilter(AutoCompleteFilterMode.EqualsOrdinal)("na", "NA")); + Assert.False(GetFilter(AutoCompleteFilterMode.EqualsOrdinal)("hello", "name")); + + Assert.True(GetFilter(AutoCompleteFilterMode.EqualsOrdinalCaseSensitive)("na", "na")); + Assert.False(GetFilter(AutoCompleteFilterMode.EqualsOrdinalCaseSensitive)("na", "NA")); + Assert.False(GetFilter(AutoCompleteFilterMode.EqualsOrdinalCaseSensitive)("hello", "name")); + + Assert.True(GetFilter(AutoCompleteFilterMode.StartsWithOrdinal)("na", "name")); + Assert.True(GetFilter(AutoCompleteFilterMode.StartsWithOrdinal)("NAM", "name")); + Assert.False(GetFilter(AutoCompleteFilterMode.StartsWithOrdinal)("hello", "name")); + + Assert.True(GetFilter(AutoCompleteFilterMode.StartsWithOrdinalCaseSensitive)("na", "name")); + Assert.False(GetFilter(AutoCompleteFilterMode.StartsWithOrdinalCaseSensitive)("NAM", "name")); + Assert.False(GetFilter(AutoCompleteFilterMode.StartsWithOrdinalCaseSensitive)("hello", "name")); + } + + private static AutoCompleteFilterPredicate GetFilter(AutoCompleteFilterMode mode) + { + return new MultiAutoCompleteBox { FilterMode = mode } + .TextFilter; + } + + [AvaloniaFact] + public void Fires_DropDown_Event() + { + Window window = new Window(); + var control = new MultiAutoCompleteBox(){ MinimumPrefixLength = 1 }; + + bool openEvent = false; + bool closeEvent = false; + control.DropDownOpened += (s, e) => openEvent = true; + control.DropDownClosed += (s, e) => closeEvent = true; + control.ItemsSource = CreateSimpleStringArray(); + + window.Content = control; + window.Show(); + Dispatcher.UIThread.RunJobs(); + + var textBox = control.FindDescendantOfType(); + + Assert.NotNull(textBox); + + textBox.Text = "a"; + Dispatcher.UIThread.RunJobs(); + Assert.True(control.SearchText == "a"); + Assert.True(control.IsDropDownOpen); + Assert.True(openEvent); + + textBox.Text = String.Empty; + Dispatcher.UIThread.RunJobs(); + Assert.True(control.SearchText == String.Empty); + Assert.False(control.IsDropDownOpen); + Assert.True(closeEvent); + } + + [AvaloniaFact] + public void Custom_FilterMode_Without_ItemFilter_Setting_Throws_Exception() + { + Window window = new Window(); + var control = new MultiAutoCompleteBox() + { + ItemsSource = CreateSimpleStringArray() + }; + window.Content = control; + window.Show(); + + control.FilterMode = AutoCompleteFilterMode.Custom; + Assert.Throws(() => { control.Text = "a"; }); + } + + private static IList CreateSimpleStringArray() + { + return new List + { + "a", + "abide", + "able", + "about", + "above", + "absence", + "absurd", + "accept", + "acceptance", + "accepted", + "accepting", + "access", + "accessed", + "accessible", + "accident", + "accidentally", + "accordance", + "account", + "accounting", + "accounts", + "accusation", + "accustomed", + "ache", + "across", + "act", + "active", + "actual", + "actually", + "ada", + "added", + "adding", + "addition", + "additional", + "additions", + "address", + "addressed", + "addresses", + "addressing", + "adjourn", + "adoption", + "advance", + "advantage", + "adventures", + "advice", + "advisable", + "advise", + "affair", + "affectionately", + "afford", + "afore", + "afraid", + "after", + "afterwards", + "again", + "against", + "age", + "aged", + "agent", + "ago", + "agony", + "agree", + "agreed", + "agreement", + "ah", + "ahem", + "air", + "airs", + "ak", + "alarm", + "alarmed", + "alas", + "alice", + "alive", + "all", + "allow", + "almost", + "alone", + "along", + "aloud", + "already", + "also", + "alteration", + "altered", + "alternate", + "alternately", + "altogether", + "always", + "am", + "ambition", + "among", + "an", + "ancient", + "and", + "anger", + "angrily", + "angry", + "animal", + "animals", + "ann", + "annoy", + "annoyed", + "another", + "answer", + "answered", + "answers", + "antipathies", + "anxious", + "anxiously", + "any", + "anyone", + "anything", + "anywhere", + "appealed", + "appear", + "appearance", + "appeared", + "appearing", + "appears", + "applause", + "apple", + "apples", + "applicable", + "apply", + "approach", + "arch", + "archbishop", + "arches", + "archive", + "are", + "argue", + "argued", + "argument", + "arguments", + "arise", + "arithmetic", + "arm", + "arms", + "around", + "arranged", + "array", + "arrived", + "arrow", + "arrum", + "as", + "ascii", + "ashamed", + "ask", + "askance", + "asked", + "asking", + "asleep", + "assembled", + "assistance", + "associated", + "at", + "ate", + "atheling", + "atom", + "attached", + "attempt", + "attempted", + "attempts", + "attended", + "attending", + "attends", + "audibly", + "australia", + "author", + "authority", + "available", + "avoid", + "away", + "awfully", + "axes", + "axis", + "b", + "baby", + "back", + "backs", + "bad", + "bag", + "baked", + "balanced", + "bank", + "banks", + "banquet", + "bark", + "barking", + "barley", + "barrowful", + "based", + "bat", + "bathing", + "bats", + "bawled", + "be", + "beak", + "bear", + "beast", + "beasts", + "beat", + "beating", + "beau", + "beauti", + "beautiful", + "beautifully", + "beautify", + "became", + "because", + "become", + "becoming", + "bed", + "beds", + "bee", + "been", + "before", + "beg", + "began", + "begged", + "begin", + "beginning", + "begins", + "begun", + "behead", + "beheaded", + "beheading", + "behind", + "being", + "believe", + "believed", + "bells", + "belong", + "belongs", + "beloved", + "below", + "belt", + "bend", + "bent", + "besides", + "best", + "better", + "between", + "bill", + "binary", + "bird", + "birds", + "birthday", + "bit", + "bite", + "bitter", + "blacking", + "blades", + "blame", + "blasts", + "bleeds", + "blew", + "blow", + "blown", + "blows", + "body", + "boldly", + "bone", + "bones", + "book", + "books", + "boon", + "boots", + "bore", + "both", + "bother", + "bottle", + "bottom", + "bough", + "bound", + "bowed", + "bowing", + "box", + "boxed", + "boy", + "brain", + "branch", + "branches", + "brandy", + "brass", + "brave", + "breach", + "bread", + "break", + "breath", + "breathe", + "breeze", + "bright", + "brightened", + "bring", + "bringing", + "bristling", + "broke", + "broken", + "brother", + "brought", + "brown", + "brush", + "brushing", + "burn", + "burning", + "burnt", + "burst", + "bursting", + "busily", + "business", + "business@pglaf", + "busy", + "but", + "butter", + "buttercup", + "buttered", + "butterfly", + "buttons", + "by", + "bye", + "c", + "cackled", + "cake", + "cakes", + "calculate", + "calculated", + "call", + "called", + "calling", + "calmly", + "came", + "camomile", + "can", + "canary", + "candle", + "cannot", + "canterbury", + "canvas", + "capering", + "capital", + "card", + "cardboard", + "cards", + "care", + "carefully", + "cares", + "carried", + "carrier", + "carroll", + "carry", + "carrying", + "cart", + "cartwheels", + "case", + "cat", + "catch", + "catching", + "caterpillar", + "cats", + "cattle", + "caucus", + "caught", + "cauldron", + "cause", + "caused", + "cautiously", + "cease", + "ceiling", + "centre", + "certain", + "certainly", + "chain", + "chains", + "chair", + "chance", + "chanced", + "change", + "changed", + "changes", + "changing", + "chapter", + "character", + "charge", + "charges", + "charitable", + "charities", + "chatte", + "cheap", + "cheated", + "check", + "checked", + "checks", + "cheeks", + "cheered", + "cheerfully", + "cherry", + "cheshire", + "chief", + "child", + "childhood", + "children", + "chimney", + "chimneys", + "chin", + "choice", + "choke", + "choked", + "choking", + "choose", + "choosing", + "chop", + "chorus", + "chose", + "christmas", + "chrysalis", + "chuckled", + "circle", + "circumstances", + "city", + "civil", + "claim", + "clamour", + "clapping", + "clasped", + "classics", + "claws", + "clean", + "clear", + "cleared", + "clearer", + "clearly", + "clever", + "climb", + "clinging", + "clock", + "close", + "closed", + "closely", + "closer", + "clubs", + "coast", + "coaxing", + "codes", + "coils", + "cold", + "collar", + "collected", + "collection", + "come", + "comes", + "comfits", + "comfort", + "comfortable", + "comfortably", + "coming", + "commercial", + "committed", + "common", + "commotion", + "company", + "compilation", + "complained", + "complaining", + "completely", + "compliance", + "comply", + "complying", + "compressed", + "computer", + "computers", + "concept", + "concerning", + "concert", + "concluded", + "conclusion", + "condemn", + "conduct", + "confirmation", + "confirmed", + "confused", + "confusing", + "confusion", + "conger", + "conqueror", + "conquest", + "consented", + "consequential", + "consider", + "considerable", + "considered", + "considering", + "constant", + "consultation", + "contact", + "contain", + "containing", + "contempt", + "contemptuous", + "contemptuously", + "content", + "continued", + "contract", + "contradicted", + "contributions", + "conversation", + "conversations", + "convert", + "cook", + "cool", + "copied", + "copies", + "copy", + "copying", + "copyright", + "corner", + "corners", + "corporation", + "corrupt", + "cost", + "costs", + "could", + "couldn", + "counting", + "countries", + "country", + "couple", + "couples", + "courage", + "course", + "court", + "courtiers", + "coward", + "crab", + "crash", + "crashed", + "crawled", + "crawling", + "crazy", + "created", + "creating", + "creation", + "creature", + "creatures", + "credit", + "creep", + "crept", + "cried", + "cries", + "crimson", + "critical", + "crocodile", + "croquet", + "croqueted", + "croqueting", + "cross", + "crossed", + "crossly", + "crouched", + "crowd", + "crowded", + "crown", + "crumbs", + "crust", + "cry", + "crying", + "cucumber", + "cunning", + "cup", + "cupboards", + "cur", + "curiosity", + "curious", + "curiouser", + "curled", + "curls", + "curly", + "currants", + "current", + "curtain", + "curtsey", + "curtseying", + "curving", + "cushion", + "custard", + "custody", + "cut", + "cutting", + }; + } +} \ No newline at end of file