namespace UnitTests_Parallelizable.ViewsTests; public class OptionSelectorTests { [Fact] public void Initialization_ShouldSetDefaults () { var optionSelector = new OptionSelector (); Assert.True (optionSelector.CanFocus); Assert.Equal (Dim.Auto (DimAutoStyle.Content), optionSelector.Width); Assert.Equal (Dim.Auto (DimAutoStyle.Content), optionSelector.Height); Assert.Equal (Orientation.Vertical, optionSelector.Orientation); Assert.Equal (0, optionSelector.Value); Assert.Null (optionSelector.Labels); } [Fact] public void Initialization_With_Options_Value_Is_First () { OptionSelector optionSelector = new OptionSelector (); List options = ["Option1", "Option2"]; optionSelector.Labels = options; Assert.Equal (0, optionSelector.Value); CheckBox checkBox = optionSelector.SubViews.OfType ().First (cb => cb.Title == "Option1"); Assert.Equal (CheckState.Checked, checkBox.CheckedState); } [Fact] public void SetOptions_ShouldCreateCheckBoxes () { var optionSelector = new OptionSelector (); List options = new () { "Option1", "Option2", "Option3" }; optionSelector.Labels = options; Assert.Equal (options, optionSelector.Labels); Assert.Equal (options.Count, optionSelector.SubViews.OfType ().Count ()); Assert.Contains (optionSelector.SubViews, sv => sv is CheckBox cb && cb.Title == "Option1"); Assert.Contains (optionSelector.SubViews, sv => sv is CheckBox cb && cb.Title == "Option2"); Assert.Contains (optionSelector.SubViews, sv => sv is CheckBox cb && cb.Title == "Option3"); } [Fact] public void Value_Set_ShouldUpdateCheckedState () { var optionSelector = new OptionSelector (); List options = new () { "Option1", "Option2" }; optionSelector.Labels = options; optionSelector.Value = 1; CheckBox selectedCheckBox = optionSelector.SubViews.OfType ().First (cb => (int)cb.Data == 1); Assert.Equal (CheckState.Checked, selectedCheckBox.CheckedState); CheckBox unselectedCheckBox = optionSelector.SubViews.OfType ().First (cb => (int)cb.Data == 0); Assert.Equal (CheckState.UnChecked, unselectedCheckBox.CheckedState); } [Fact] public void Value_Set_OutOfRange_ShouldThrow () { var optionSelector = new OptionSelector (); List options = ["Option1", "Option2"]; optionSelector.Labels = options; Assert.Throws (() => optionSelector.Value = -1); Assert.Throws (() => optionSelector.Value = 2); } [Fact] public void ValueChanged_Event_ShouldBeRaised () { var optionSelector = new OptionSelector (); List options = new () { "Option1", "Option2" }; optionSelector.Labels = options; var eventRaised = false; optionSelector.ValueChanged += (sender, args) => eventRaised = true; optionSelector.Value = 1; Assert.True (eventRaised); } [Fact] public void AssignHotKeys_ShouldAssignUniqueHotKeys () { var optionSelector = new OptionSelector { AssignHotKeys = true }; List options = new () { "Option1", "Option2" }; optionSelector.Labels = options; List checkBoxes = optionSelector.SubViews.OfType ().ToList (); Assert.Contains ('_', checkBoxes [0].Title); Assert.Contains ('_', checkBoxes [1].Title); } [Fact] public void Orientation_Set_ShouldUpdateLayout () { var optionSelector = new OptionSelector (); List options = new () { "Option1", "Option2" }; optionSelector.Labels = options; optionSelector.Orientation = Orientation.Horizontal; foreach (CheckBox checkBox in optionSelector.SubViews.OfType ()) { Assert.Equal (0, checkBox.Y); } } [Fact] public void HotKey_No_Value_Selects_First () { var superView = new View { CanFocus = true }; superView.Add (new View { CanFocus = true }); var selector = new OptionSelector { HotKey = Key.G.WithAlt, Labels = ["_Left", "_Right", "Cen_tered", "_Justified"] }; selector.Value = null; superView.Add (selector); Assert.False (selector.HasFocus); Assert.Null (selector.Value); selector.NewKeyDownEvent (Key.G.WithAlt); Assert.Equal (0, selector.Value); Assert.Equal (selector.SubViews.OfType ().First (), superView.MostFocused); } [Fact] public void Accept_Command_Fires_Accept () { var optionSelector = new OptionSelector (); optionSelector.Labels = new List { "Option1", "Option2" }; var accepted = false; optionSelector.Accepting += OnAccept; optionSelector.InvokeCommand (Command.Accept); Assert.True (accepted); return; void OnAccept (object sender, CommandEventArgs e) { accepted = true; } } [Fact] public void Mouse_Click_On_Activated_Does_Nothing () { OptionSelector optionSelector = new OptionSelector (); List options = ["Option1", "Option2"]; optionSelector.Labels = options; optionSelector.Layout (); CheckBox checkBox = optionSelector.SubViews.OfType ().First (cb => cb.Title == "Option1"); Assert.Equal (0, optionSelector.Value); Assert.Equal (CheckState.Checked, checkBox.CheckedState); var mouseEvent = new MouseEventArgs { Position = checkBox.Frame.Location, Flags = MouseFlags.Button1Clicked }; checkBox.NewMouseEvent (mouseEvent); Assert.Equal (0, optionSelector.Value); Assert.Equal (CheckState.Checked, checkBox.CheckedState); Assert.Equal (CheckState.UnChecked, optionSelector.SubViews.OfType ().First (cb => cb.Title == "Option2").CheckedState); } [Fact] public void Mouse_Click_On_NotActivated_Activates () { OptionSelector optionSelector = new OptionSelector (); List options = ["Option1", "Option2"]; optionSelector.Labels = options; optionSelector.Layout (); CheckBox checkBox = optionSelector.SubViews.OfType ().First (cb => cb.Title == "Option2"); Assert.Equal (0, optionSelector.Value); Assert.Equal (CheckState.Checked, optionSelector.SubViews.OfType ().First (cb => cb.Title == "Option1").CheckedState); Assert.Equal (CheckState.UnChecked, checkBox.CheckedState); var mouseEvent = new MouseEventArgs { Position = checkBox.Frame.Location, Flags = MouseFlags.Button1Clicked }; checkBox.NewMouseEvent (mouseEvent); Assert.Equal (1, optionSelector.Value); Assert.Equal (CheckState.Checked, checkBox.CheckedState); Assert.Equal (CheckState.UnChecked, optionSelector.SubViews.OfType ().First (cb => cb.Title == "Option1").CheckedState); } [Fact] public void Key_Space_On_Activated_Cycles () { OptionSelector optionSelector = new OptionSelector (); List options = ["Option1", "Option2"]; optionSelector.Labels = options; optionSelector.Layout (); CheckBox checkBox = optionSelector.SubViews.OfType ().First (cb => cb.Title == "Option1"); Assert.Equal (0, optionSelector.Value); Assert.Equal (CheckState.Checked, checkBox.CheckedState); checkBox.NewKeyDownEvent (Key.Space); Assert.Equal (1, optionSelector.Value); Assert.Equal (CheckState.UnChecked, checkBox.CheckedState); Assert.Equal (CheckState.Checked, optionSelector.SubViews.OfType ().First (cb => cb.Title == "Option2").CheckedState); } [Fact] public void Key_Space_On_NotActivated_Activates () { OptionSelector optionSelector = new OptionSelector (); List options = ["Option1", "Option2"]; optionSelector.Labels = options; optionSelector.Layout (); CheckBox checkBox = optionSelector.SubViews.OfType ().First (cb => cb.Title == "Option2"); Assert.Equal (0, optionSelector.Value); Assert.Equal (CheckState.Checked, optionSelector.SubViews.OfType ().First (cb => cb.Title == "Option1").CheckedState); Assert.Equal (CheckState.UnChecked, checkBox.CheckedState); checkBox.NewKeyDownEvent (Key.Space); Assert.Equal (1, optionSelector.Value); Assert.Equal (CheckState.Checked, checkBox.CheckedState); Assert.Equal (CheckState.UnChecked, optionSelector.SubViews.OfType ().First (cb => cb.Title == "Option1").CheckedState); } [Fact] public void Values_ShouldUseOptions_WhenValuesIsNull () { var optionSelector = new OptionSelector (); Assert.Null (optionSelector.Values); // Initially null List options = ["Option1", "Option2", "Option3"]; optionSelector.Labels = options; IReadOnlyList values = optionSelector.Values; Assert.NotNull (values); Assert.Equal (Enumerable.Range (0, options.Count).ToList (), values); } [Fact] public void Values_NonSequential_ShouldWorkCorrectly () { // Arrange OptionSelector optionSelector = new (); List options = new () { "Option _1", "Option _2", "Option _3" }; List values = new () { 0, 1, 5 }; optionSelector.Labels = options; optionSelector.Values = values; // Act & Assert Assert.Equal (values, optionSelector.Values); Assert.Equal (options, optionSelector.Labels); // Verify that the Value property updates correctly optionSelector.Value = 5; Assert.Equal (5, optionSelector.Value); // Verify that the CheckBox states align with the non-sequential Values CheckBox selectedCheckBox = optionSelector.SubViews.OfType () .First (cb => (int)cb.Data == 5); Assert.Equal (CheckState.Checked, selectedCheckBox.CheckedState); CheckBox unselectedCheckBox = optionSelector.SubViews.OfType () .First (cb => (int)cb.Data! == 0); // Index 0 corresponds to value 0 Assert.Equal (CheckState.UnChecked, unselectedCheckBox.CheckedState); } [Fact] public void Item_HotKey_Null_Value_Changes_Value_And_SetsFocus () { var superView = new View { CanFocus = true }; superView.Add (new View { Id = "otherView", CanFocus = true }); var selector = new OptionSelector (); selector.Labels = ["_One", "_Two"]; superView.Add (selector); superView.SetFocus (); Assert.False (selector.HasFocus); Assert.Equal (0, selector.Value); selector.Value = null; Assert.False (selector.HasFocus); selector.NewKeyDownEvent (Key.T); Assert.True (selector.HasFocus); Assert.Equal (1, selector.Value); } [Fact] public void Cursor_Get_ReturnsCorrectIndex () { var optionSelector = new OptionSelector (); List options = ["Option1", "Option2", "Option3"]; optionSelector.Labels = options; optionSelector.Layout (); // Set focus to second checkbox CheckBox secondCheckBox = optionSelector.SubViews.OfType ().ToArray () [1]; secondCheckBox.SetFocus (); Assert.Equal (1, optionSelector.Cursor); // Set focus to third checkbox CheckBox thirdCheckBox = optionSelector.SubViews.OfType ().ToArray () [2]; thirdCheckBox.SetFocus (); Assert.Equal (2, optionSelector.Cursor); } [Fact] public void Cursor_Get_WhenNotFocusable_ReturnsZero () { var optionSelector = new OptionSelector { CanFocus = false }; List options = ["Option1", "Option2", "Option3"]; optionSelector.Labels = options; optionSelector.Layout (); Assert.Equal (0, optionSelector.Cursor); } [Fact] public void Cursor_Set_ShouldMoveFocusToCorrectCheckBox () { var optionSelector = new OptionSelector (); List options = ["Option1", "Option2", "Option3"]; optionSelector.Labels = options; optionSelector.Layout (); // Set cursor to second checkbox optionSelector.Cursor = 1; CheckBox [] checkBoxes = optionSelector.SubViews.OfType ().ToArray (); Assert.True (checkBoxes [1].HasFocus); Assert.Equal (1, optionSelector.Cursor); // Set cursor to third checkbox optionSelector.Cursor = 2; Assert.True (checkBoxes [2].HasFocus); Assert.Equal (2, optionSelector.Cursor); } [Fact] public void Cursor_Set_OutOfRange_ShouldThrow () { var optionSelector = new OptionSelector (); List options = ["Option1", "Option2", "Option3"]; optionSelector.Labels = options; optionSelector.Layout (); Assert.Throws (() => optionSelector.Cursor = -1); Assert.Throws (() => optionSelector.Cursor = 3); } [Fact] public void Cursor_Set_WhenNotFocusable_DoesNothing () { var optionSelector = new OptionSelector { CanFocus = false }; List options = ["Option1", "Option2", "Option3"]; optionSelector.Labels = options; optionSelector.Layout (); // Should not throw optionSelector.Cursor = 1; // Verify nothing changed Assert.Equal (0, optionSelector.Cursor); Assert.False (optionSelector is { } && optionSelector.SubViews.OfType ().Any (cb => cb.HasFocus)); } [Fact] public void Cursor_DoesNotChangeValue () { var optionSelector = new OptionSelector (); List options = ["Option1", "Option2", "Option3"]; optionSelector.Labels = options; optionSelector.Value = 0; // First option is selected optionSelector.Layout (); // Move cursor to second checkbox optionSelector.Cursor = 1; // Value should not change, only focus moves Assert.Equal (0, optionSelector.Value); Assert.Equal (1, optionSelector.Cursor); CheckBox [] checkBoxes = optionSelector.SubViews.OfType ().ToArray (); Assert.Equal (CheckState.Checked, checkBoxes [0].CheckedState); Assert.Equal (CheckState.UnChecked, checkBoxes [1].CheckedState); Assert.True (checkBoxes [1].HasFocus); } }