| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604 |
- #nullable enable
- namespace ViewsTests;
- /// <summary>
- /// Tests for <see cref="SelectorBase"/> functionality that applies to all selector implementations.
- /// These tests use <see cref="OptionSelector"/> as a concrete implementation to test the base class.
- /// </summary>
- public class SelectorBaseTests
- {
- #region Initialization Tests
- [Fact]
- public void Constructor_SetsDefaults ()
- {
- var selector = new OptionSelector ();
- Assert.True (selector.CanFocus);
- Assert.Equal (Dim.Auto (DimAutoStyle.Content), selector.Width);
- Assert.Equal (Dim.Auto (DimAutoStyle.Content), selector.Height);
- Assert.Equal (Orientation.Vertical, selector.Orientation);
- Assert.Null (selector.Labels);
- Assert.Null (selector.Values);
- Assert.False (selector.AssignHotKeys);
- Assert.Empty (selector.UsedHotKeys);
- Assert.Equal (SelectorStyles.None, selector.Styles);
- Assert.True (selector.DoubleClickAccepts);
- Assert.Equal (2, selector.HorizontalSpace);
- }
- #endregion
- #region Value Property Tests
- [Fact]
- public void Value_Set_ValidValue_UpdatesValue ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- selector.Value = 1;
- Assert.Equal (1, selector.Value);
- }
- [Fact]
- public void Value_Set_InvalidValue_ThrowsArgumentOutOfRangeException ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- Assert.Throws<ArgumentOutOfRangeException> (() => selector.Value = 5);
- Assert.Throws<ArgumentOutOfRangeException> (() => selector.Value = -1);
- }
- [Fact]
- public void Value_Set_Null_Succeeds ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- selector.Value = null;
- Assert.Null (selector.Value);
- }
- [Fact]
- public void Value_Set_SameValue_DoesNotRaiseEvent ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- selector.Value = 1;
- var eventRaisedCount = 0;
- selector.ValueChanged += (s, e) => eventRaisedCount++;
- selector.Value = 1; // Set to same value
- Assert.Equal (0, eventRaisedCount);
- }
- [Fact]
- public void Value_Changed_RaisesValueChangedEvent ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- int? capturedValue = null;
- selector.ValueChanged += (s, e) => capturedValue = e.Value;
- selector.Value = 1;
- Assert.Equal (1, capturedValue);
- }
- #endregion
- #region Values Property Tests
- [Fact]
- public void Values_Get_WhenNull_ReturnsSequentialValues ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2", "Option3"];
- IReadOnlyList<int>? values = selector.Values;
- Assert.NotNull (values);
- Assert.Equal (3, values.Count);
- Assert.Equal ([0, 1, 2], values);
- }
- [Fact]
- public void Values_Set_UpdatesValues ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- selector.Values = [10, 20];
- Assert.Equal ([10, 20], selector.Values);
- }
- [Fact]
- public void Values_Set_SetsDefaultValue ()
- {
- var selector = new OptionSelector ();
- selector.Value = null;
- selector.Labels = ["Option1", "Option2"];
- selector.Values = [10, 20];
- Assert.Equal (10, selector.Value); // Should default to first value
- }
- #endregion
- #region Labels Property Tests
- [Fact]
- public void Labels_Set_CreatesSubViews ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- Assert.Equal (2, selector.SubViews.OfType<CheckBox> ().Count ());
- }
- [Fact]
- public void Labels_Set_Null_RemovesSubViews ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- selector.Labels = null;
- Assert.Empty (selector.SubViews.OfType<CheckBox> ());
- }
- [Fact]
- public void Labels_Values_CountMismatch_DoesNotCreateSubViews ()
- {
- var selector = new OptionSelector ();
- selector.Values = [0, 1, 2];
- selector.Labels = ["Option1", "Option2"]; // Mismatch
- Assert.Empty (selector.SubViews.OfType<CheckBox> ());
- }
- #endregion
- #region SetValuesAndLabels Tests
- [Fact]
- public void SetValuesAndLabels_FromEnum_SetsValuesAndLabels ()
- {
- var selector = new OptionSelector ();
- selector.SetValuesAndLabels<SelectorStyles> ();
- Assert.NotNull (selector.Values);
- Assert.NotNull (selector.Labels);
- Assert.Equal (Enum.GetValues<SelectorStyles> ().Length, selector.Values.Count);
- Assert.Equal (Enum.GetNames<SelectorStyles> (), selector.Labels);
- }
- [Fact]
- public void SetValuesAndLabels_SetsCorrectIntegerValues ()
- {
- var selector = new OptionSelector ();
- selector.SetValuesAndLabels<SelectorStyles> ();
- // Verify values match enum integer values
- var expectedValues = Enum.GetValues<SelectorStyles> ().Select (e => (int)e).ToList ();
- Assert.Equal (expectedValues, selector.Values);
- }
- #endregion
- #region Styles Property Tests
- [Fact]
- public void Styles_Set_None_NoExtraSubViews ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- selector.Styles = SelectorStyles.None;
- Assert.Equal (2, selector.SubViews.Count);
- Assert.Null (selector.SubViews.FirstOrDefault (v => v.Id == "valueField"));
- }
- [Fact]
- public void Styles_Set_ShowValue_AddsValueField ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- selector.Styles = SelectorStyles.ShowValue;
- View? valueField = selector.SubViews.FirstOrDefault (v => v.Id == "valueField");
- Assert.NotNull (valueField);
- Assert.IsType<TextField> (valueField);
- }
- [Fact]
- public void Styles_Set_ShowValue_ValueFieldDisplaysCurrentValue ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- selector.Value = 1;
- selector.Styles = SelectorStyles.ShowValue;
- var valueField = (TextField?)selector.SubViews.FirstOrDefault (v => v.Id == "valueField");
- Assert.NotNull (valueField);
- Assert.Equal ("1", valueField.Text);
- }
- [Fact]
- public void Styles_Set_ShowValue_ValueFieldUpdatesOnValueChange ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- selector.Styles = SelectorStyles.ShowValue;
- selector.Value = 1;
- var valueField = (TextField?)selector.SubViews.FirstOrDefault (v => v.Id == "valueField");
- Assert.NotNull (valueField);
- Assert.Equal ("1", valueField.Text);
- }
- [Fact]
- public void Styles_Set_SameValue_DoesNotRecreateSubViews ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- selector.Styles = SelectorStyles.ShowValue;
- CheckBox firstCheckBox = selector.SubViews.OfType<CheckBox> ().First ();
- selector.Styles = SelectorStyles.ShowValue; // Set to same value
- // Should be the same instance
- Assert.Same (firstCheckBox, selector.SubViews.OfType<CheckBox> ().First ());
- }
- #endregion
- #region AssignHotKeys Tests
- [Fact]
- public void AssignHotKeys_True_AssignsHotKeysToCheckBoxes ()
- {
- var selector = new OptionSelector { AssignHotKeys = true };
- selector.Labels = ["Option1", "Option2"];
- CheckBox [] checkBoxes = selector.SubViews.OfType<CheckBox> ().ToArray ();
- Assert.NotEqual (Key.Empty, checkBoxes [0].HotKey);
- Assert.NotEqual (Key.Empty, checkBoxes [1].HotKey);
- }
- [Fact]
- public void AssignHotKeys_True_AddsHotKeySpecifierToTitles ()
- {
- var selector = new OptionSelector { AssignHotKeys = true };
- selector.Labels = ["Option1", "Option2"];
- CheckBox [] checkBoxes = selector.SubViews.OfType<CheckBox> ().ToArray ();
- Assert.Contains ('_', checkBoxes [0].Title);
- Assert.Contains ('_', checkBoxes [1].Title);
- }
- [Fact]
- public void AssignHotKeys_True_AssignsUniqueHotKeys ()
- {
- var selector = new OptionSelector { AssignHotKeys = true };
- selector.Labels = ["Option1", "Option2", "Option3"];
- CheckBox [] checkBoxes = selector.SubViews.OfType<CheckBox> ().ToArray ();
- var hotKeys = checkBoxes.Select (cb => cb.HotKey).ToList ();
- Assert.Equal (3, hotKeys.Distinct ().Count ()); // All unique
- }
- [Fact]
- public void AssignHotKeys_False_DoesNotAssignHotKeys ()
- {
- var selector = new OptionSelector { AssignHotKeys = false };
- selector.Labels = ["Option1", "Option2"];
- CheckBox [] checkBoxes = selector.SubViews.OfType<CheckBox> ().ToArray ();
- Assert.Equal (Key.Empty, checkBoxes [0].HotKey);
- Assert.Equal (Key.Empty, checkBoxes [1].HotKey);
- }
- [Fact]
- public void AssignHotKeys_PreservesExistingHotKeys ()
- {
- var selector = new OptionSelector { AssignHotKeys = true };
- selector.Labels = ["_Alt Option", "Option2"];
- CheckBox [] checkBoxes = selector.SubViews.OfType<CheckBox> ().ToArray ();
- // Should use 'A' from "_Alt"
- Assert.Equal (Key.A, checkBoxes [0].HotKey);
- }
- #endregion
- #region UsedHotKeys Tests
- [Fact]
- public void UsedHotKeys_SkipsMarkedKeys ()
- {
- var selector = new OptionSelector { AssignHotKeys = true };
- selector.UsedHotKeys.Add (Key.O); // Mark 'O' as used
- selector.Labels = ["Option1", "Option2"];
- CheckBox [] checkBoxes = selector.SubViews.OfType<CheckBox> ().ToArray ();
- // Should skip 'O' and use next available character
- Assert.NotEqual (Key.O, checkBoxes [0].HotKey);
- }
- [Fact]
- public void UsedHotKeys_PopulatedWhenHotKeysAssigned ()
- {
- var selector = new OptionSelector { AssignHotKeys = true };
- selector.Labels = ["Option1", "Option2"];
- // UsedHotKeys should contain the assigned hotkeys
- Assert.NotEmpty (selector.UsedHotKeys);
- CheckBox [] checkBoxes = selector.SubViews.OfType<CheckBox> ().ToArray ();
- foreach (CheckBox cb in checkBoxes)
- {
- Assert.Contains (cb.HotKey, selector.UsedHotKeys);
- }
- }
- #endregion
- #region Orientation Tests
- [Fact]
- public void Orientation_Vertical_CheckBoxesStackedVertically ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- selector.Orientation = Orientation.Vertical;
- selector.Layout ();
- CheckBox [] checkBoxes = selector.SubViews.OfType<CheckBox> ().ToArray ();
- Assert.Equal (0, checkBoxes [0].Frame.Y);
- Assert.True (checkBoxes [1].Frame.Y > checkBoxes [0].Frame.Y);
- }
- [Fact]
- public void Orientation_Horizontal_CheckBoxesArrangedHorizontally ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- selector.Orientation = Orientation.Horizontal;
- selector.Layout ();
- CheckBox [] checkBoxes = selector.SubViews.OfType<CheckBox> ().ToArray ();
- Assert.Equal (0, checkBoxes [0].Frame.Y);
- Assert.Equal (0, checkBoxes [1].Frame.Y);
- Assert.True (checkBoxes [1].Frame.X > checkBoxes [0].Frame.X);
- }
- [Fact]
- public void Orientation_Change_TriggersLayout ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- selector.Layout ();
- CheckBox [] checkBoxes = selector.SubViews.OfType<CheckBox> ().ToArray ();
- int originalYDiff = checkBoxes [1].Frame.Y - checkBoxes [0].Frame.Y;
- selector.Orientation = Orientation.Horizontal;
- selector.Layout ();
- int newYDiff = checkBoxes [1].Frame.Y - checkBoxes [0].Frame.Y;
- Assert.NotEqual (originalYDiff, newYDiff);
- Assert.Equal (0, newYDiff); // Both should be at Y=0 now
- }
- #endregion
- #region HorizontalSpace Tests
- [Fact]
- public void HorizontalSpace_Default_Is2 ()
- {
- var selector = new OptionSelector ();
- Assert.Equal (2, selector.HorizontalSpace);
- }
- [Fact]
- public void HorizontalSpace_Set_UpdatesSpacing ()
- {
- var selector = new OptionSelector { Orientation = Orientation.Horizontal };
- selector.Labels = ["Option1", "Option2"];
- selector.HorizontalSpace = 2;
- selector.Layout ();
- CheckBox [] checkBoxes = selector.SubViews.OfType<CheckBox> ().ToArray ();
- // HorizontalSpace is applied via Margin.Thickness.Right
- int spacing2 = checkBoxes [0].Margin!.Thickness.Right;
- selector.HorizontalSpace = 5;
- selector.Layout ();
- int spacing5 = checkBoxes [0].Margin!.Thickness.Right;
- Assert.True (spacing5 > spacing2);
- Assert.Equal (2, spacing2);
- Assert.Equal (5, spacing5);
- }
- [Fact]
- public void HorizontalSpace_OnlyAppliesToHorizontalOrientation ()
- {
- var selector = new OptionSelector { Orientation = Orientation.Vertical };
- selector.Labels = ["Option1", "Option2"];
- selector.HorizontalSpace = 10;
- selector.Layout ();
- CheckBox [] checkBoxes = selector.SubViews.OfType<CheckBox> ().ToArray ();
- // In vertical mode, checkboxes should be at same X
- Assert.Equal (checkBoxes [0].Frame.X, checkBoxes [1].Frame.X);
- }
- #endregion
- #region DoubleClickAccepts Tests
- [Fact]
- public void DoubleClickAccepts_Default_IsTrue ()
- {
- var selector = new OptionSelector ();
- Assert.True (selector.DoubleClickAccepts);
- }
- [Fact]
- public void DoubleClickAccepts_True_AcceptOnDoubleClick ()
- {
- var selector = new OptionSelector { DoubleClickAccepts = true };
- selector.Labels = ["Option1", "Option2"];
- selector.Layout ();
- var acceptCount = 0;
- selector.Accepting += (s, e) => acceptCount++;
- CheckBox checkBox = selector.SubViews.OfType<CheckBox> ().First ();
- checkBox.NewMouseEvent (new () { Position = Point.Empty, Flags = MouseFlags.Button1Clicked });
- checkBox.NewMouseEvent (new () { Position = Point.Empty, Flags = MouseFlags.Button1DoubleClicked });
- Assert.Equal (1, acceptCount);
- }
- [Fact]
- public void DoubleClickAccepts_False_DoesNotAcceptOnDoubleClick ()
- {
- var selector = new OptionSelector { DoubleClickAccepts = false };
- selector.Labels = ["Option1", "Option2"];
- selector.Layout ();
- var acceptCount = 0;
- selector.Accepting += (s, e) => acceptCount++;
- CheckBox checkBox = selector.SubViews.OfType<CheckBox> ().First ();
- checkBox.NewMouseEvent (new () { Position = Point.Empty, Flags = MouseFlags.Button1Clicked });
- checkBox.NewMouseEvent (new () { Position = Point.Empty, Flags = MouseFlags.Button1DoubleClicked });
- Assert.Equal (0, acceptCount);
- }
- #endregion
- #region CreateSubViews Tests
- [Fact]
- public void CreateSubViews_RemovesOldSubViewsAndCreatesNew ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- int oldCount = selector.SubViews.Count;
- selector.Labels = ["New1", "New2", "New3"];
- Assert.NotEqual (oldCount, selector.SubViews.Count);
- Assert.Equal (3, selector.SubViews.OfType<CheckBox> ().Count ());
- Assert.Contains (selector.SubViews.OfType<CheckBox> (), cb => cb.Title == "New1");
- }
- [Fact]
- public void CreateSubViews_SetsCheckBoxProperties ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Test Option"];
- selector.Values = [42];
- CheckBox checkBox = selector.SubViews.OfType<CheckBox> ().First ();
- Assert.Equal ("Test Option", checkBox.Title);
- Assert.Equal ("Test Option", checkBox.Id);
- Assert.Equal (42, checkBox.Data);
- Assert.True (checkBox.CanFocus);
- }
- #endregion
- #region HotKey Command Tests
- [Fact]
- public void HotKey_Command_DoesNotFireAccept ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- var acceptCount = 0;
- selector.Accepting += (s, e) => acceptCount++;
- selector.InvokeCommand (Command.HotKey);
- Assert.Equal (0, acceptCount);
- }
- [Fact]
- public void Accept_Command_FiresAccept ()
- {
- var selector = new OptionSelector ();
- selector.Labels = ["Option1", "Option2"];
- var acceptCount = 0;
- selector.Accepting += (s, e) => acceptCount++;
- selector.InvokeCommand (Command.Accept);
- Assert.Equal (1, acceptCount);
- }
- #endregion
- #region Edge Cases
- [Fact]
- public void EmptyLabels_CreatesNoSubViews ()
- {
- var selector = new OptionSelector ();
- selector.Labels = [];
- Assert.Empty (selector.SubViews);
- }
- [Fact]
- public void Value_WithNoLabels_CanBeSet ()
- {
- var selector = new OptionSelector ();
- // This should work even without labels
- var exception = Record.Exception (() => selector.Value = null);
- Assert.Null (exception);
- Assert.Null (selector.Value);
- }
- #endregion
- }
|