namespace ViewBaseTests.Navigation; /// /// Tests for navigation into and out of Adornments (Padding, Border, Margin). /// These tests prove that navigation to/from adornments is broken and need to be fixed. /// public class AdornmentNavigationTests { #region Padding Navigation Tests [Fact] [Trait ("Category", "Adornment")] [Trait ("Category", "Navigation")] public void AdvanceFocus_Into_Padding_With_Focusable_SubView () { // Setup: View with a focusable subview in Padding View view = new () { Id = "view", Width = 10, Height = 10, CanFocus = true }; view.Padding!.Thickness = new Thickness (1); View paddingButton = new () { Id = "paddingButton", CanFocus = true, TabStop = TabBehavior.TabStop, X = 0, Y = 0, Width = 5, Height = 1 }; view.Padding.Add (paddingButton); View contentButton = new () { Id = "contentButton", CanFocus = true, TabStop = TabBehavior.TabStop, X = 0, Y = 0, Width = 5, Height = 1 }; view.Add (contentButton); view.BeginInit (); view.EndInit (); // Test: Advance focus should navigate to content first view.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); // Expected: contentButton should have focus // This test documents the expected behavior for navigation into padding Assert.True (contentButton.HasFocus, "Content view should receive focus first"); Assert.False (paddingButton.HasFocus, "Padding subview should not have focus yet"); // Test: Advance focus again should go to padding view.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); // Expected: paddingButton should now have focus // This will likely FAIL, proving the bug exists Assert.True (paddingButton.HasFocus, "Padding subview should receive focus after content"); Assert.False (contentButton.HasFocus, "Content view should no longer have focus"); view.Dispose (); } [Fact] [Trait ("Category", "Adornment")] [Trait ("Category", "Navigation")] public void AdvanceFocus_Out_Of_Padding_To_Content () { // Setup: View with focusable padding that has focus View view = new () { Id = "view", Width = 10, Height = 10, CanFocus = true }; view.Padding!.Thickness = new Thickness (1); View paddingButton = new () { Id = "paddingButton", CanFocus = true, TabStop = TabBehavior.TabStop, X = 0, Y = 0, Width = 5, Height = 1 }; view.Padding.Add (paddingButton); View contentButton = new () { Id = "contentButton", CanFocus = true, TabStop = TabBehavior.TabStop, X = 0, Y = 0, Width = 5, Height = 1 }; view.Add (contentButton); view.BeginInit (); view.EndInit (); // Set focus to padding button paddingButton.SetFocus (); Assert.True (paddingButton.HasFocus, "Setup: Padding button should have focus"); // Test: Advance focus should navigate from padding to content view.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); // Expected: Should navigate to content // This will likely FAIL, proving the bug exists Assert.True (contentButton.HasFocus, "Content view should receive focus after padding"); Assert.False (paddingButton.HasFocus, "Padding button should no longer have focus"); view.Dispose (); } [Fact] [Trait ("Category", "Adornment")] [Trait ("Category", "Navigation")] public void AdvanceFocus_Backward_Into_Padding () { // Setup: View with focusable subviews in both content and padding View view = new () { Id = "view", Width = 10, Height = 10, CanFocus = true }; view.Padding!.Thickness = new Thickness (1); View paddingButton = new () { Id = "paddingButton", CanFocus = true, TabStop = TabBehavior.TabStop, X = 0, Y = 0, Width = 5, Height = 1 }; view.Padding.Add (paddingButton); View contentButton = new () { Id = "contentButton", CanFocus = true, TabStop = TabBehavior.TabStop, X = 0, Y = 0, Width = 5, Height = 1 }; view.Add (contentButton); view.BeginInit (); view.EndInit (); // Set focus to content contentButton.SetFocus (); Assert.True (contentButton.HasFocus, "Setup: Content button should have focus"); // Test: Advance focus backward should go to padding view.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop); // Expected: Should navigate to padding // This will likely FAIL, proving the bug exists Assert.True (paddingButton.HasFocus, "Padding button should receive focus when navigating backward"); Assert.False (contentButton.HasFocus, "Content button should no longer have focus"); view.Dispose (); } [Fact] [Trait ("Category", "Adornment")] [Trait ("Category", "Navigation")] public void Padding_CanFocus_True_TabStop_TabStop_Should_Be_In_FocusChain () { // Setup: View with focusable Padding View view = new () { Id = "view", Width = 10, Height = 10, CanFocus = true }; view.Padding!.Thickness = new (1); view.Padding.CanFocus = true; view.Padding.TabStop = TabBehavior.TabStop; view.BeginInit (); view.EndInit (); // Test: Get focus chain View [] focusChain = view.GetFocusChain (NavigationDirection.Forward, TabBehavior.TabStop); // Expected: Padding should be in the focus chain // This should pass based on the GetFocusChain code Assert.Contains (view.Padding, focusChain); view.Dispose (); } #endregion #region Border Navigation Tests [Fact] [Trait ("Category", "Adornment")] [Trait ("Category", "Navigation")] public void AdvanceFocus_Into_Border_With_Focusable_SubView () { // Setup: View with a focusable subview in Border View view = new () { Id = "view", Width = 10, Height = 10, CanFocus = true }; view.Border!.Thickness = new Thickness (1); View borderButton = new () { Id = "borderButton", CanFocus = true, TabStop = TabBehavior.TabGroup, X = 0, Y = 0, Width = 5, Height = 1 }; view.Border.Add (borderButton); View contentButton = new () { Id = "contentButton", CanFocus = true, TabStop = TabBehavior.TabGroup, X = 0, Y = 0, Width = 5, Height = 1 }; view.Add (contentButton); view.BeginInit (); view.EndInit (); // Test: Advance focus should navigate between content and border view.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup); // Expected: One of them should have focus var hasFocus = contentButton.HasFocus || borderButton.HasFocus; Assert.True (hasFocus, "Either content or border button should have focus"); // Advance again view.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup); // Expected: The other one should now have focus // This will likely FAIL, proving the bug exists if (contentButton.HasFocus) { // If content has focus now, border should have had it before Assert.False (borderButton.HasFocus, "Only one should have focus at a time"); } else { Assert.True (borderButton.HasFocus, "Border should have focus if content doesn't"); } view.Dispose (); } [Fact] [Trait ("Category", "Adornment")] [Trait ("Category", "Navigation")] public void Border_CanFocus_True_TabStop_TabGroup_Should_NOT_Be_In_FocusChain () { // Setup: View with focusable Border (default TabStop is TabGroup for Border) View view = new () { Id = "view", Width = 10, Height = 10, CanFocus = true }; view.Border!.Thickness = new Thickness (1); view.Border.CanFocus = true; view.BeginInit (); view.EndInit (); // Test: Get focus chain for TabGroup View [] focusChain = view.GetFocusChain (NavigationDirection.Forward, TabBehavior.TabGroup); // Expected: Border should be in the focus chain Assert.DoesNotContain (view.Border, focusChain); view.Dispose (); } #endregion #region Margin Navigation Tests [Fact] [Trait ("Category", "Adornment")] [Trait ("Category", "Navigation")] public void Margin_CanFocus_True_Should_NOT_Be_In_FocusChain () { // Setup: View with focusable Margin View view = new () { Id = "view", Width = 10, Height = 10, CanFocus = true }; view.Margin!.Thickness = new Thickness (1); view.Margin.CanFocus = true; view.Margin.TabStop = TabBehavior.TabStop; view.BeginInit (); view.EndInit (); // Test: Get focus chain View [] focusChain = view.GetFocusChain (NavigationDirection.Forward, TabBehavior.TabStop); // Expected: Margin should be in the focus chain Assert.DoesNotContain (view.Margin, focusChain); view.Dispose (); } #endregion #region Mixed Scenarios [Fact] [Trait ("Category", "Adornment")] [Trait ("Category", "Navigation")] public void AdvanceFocus_Nested_Views_With_Adornment_SubViews () { // Setup: Nested views where parent has adornment subviews View parent = new () { Id = "parent", Width = 30, Height = 30, CanFocus = true }; parent.Padding!.Thickness = new Thickness (2); View parentPaddingButton = new () { Id = "parentPaddingButton", CanFocus = true, TabStop = TabBehavior.TabStop, X = 0, Y = 0, Width = 8, Height = 1 }; parent.Padding.Add (parentPaddingButton); View child = new () { Id = "child", Width = 10, Height = 10, CanFocus = true, TabStop = TabBehavior.TabStop }; parent.Add (child); child.Padding!.Thickness = new Thickness (1); View childPaddingButton = new () { Id = "childPaddingButton", CanFocus = true, TabStop = TabBehavior.TabStop, X = 0, Y = 0, Width = 5, Height = 1 }; child.Padding.Add (childPaddingButton); parent.BeginInit (); parent.EndInit (); // Test: Advance focus should navigate through parent padding, child, and child padding parent.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); // Track which views receive focus List focusedIds = new (); // Navigate multiple times to test nested navigation (extra iteration to allow for wrapping) for (var i = 0; i < 5; i++) { if (parentPaddingButton.HasFocus) { focusedIds.Add ("parentPaddingButton"); } else if (child.HasFocus) { focusedIds.Add ("child"); } else if (childPaddingButton.HasFocus) { focusedIds.Add ("childPaddingButton"); } parent.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); } // Expected: Navigation should reach all elements including adornment subviews // This will likely show incomplete navigation, proving the bug exists Assert.True ( focusedIds.Count > 0, "At least some navigation should occur (this test documents current behavior)" ); parent.Dispose (); } #endregion #region TabGroup Behavior Tests #endregion #region Edge Cases [Fact] [Trait ("Category", "Adornment")] [Trait ("Category", "Navigation")] public void AdvanceFocus_Padding_With_No_Thickness_Should_Not_Participate () { // Setup: View with Padding that has no thickness but has subviews View view = new () { Id = "view", Width = 10, Height = 10, CanFocus = true }; // Padding has default Thickness.Empty View paddingButton = new () { Id = "paddingButton", CanFocus = true, TabStop = TabBehavior.TabStop, X = 0, Y = 0, Width = 5, Height = 1 }; view.Padding!.Add (paddingButton); View contentButton = new () { Id = "contentButton", CanFocus = true, TabStop = TabBehavior.TabStop, X = 0, Y = 0, Width = 5, Height = 1 }; view.Add (contentButton); view.BeginInit (); view.EndInit (); // Test: Navigate - should only focus content since Padding has no thickness view.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); Assert.True (contentButton.HasFocus, "Content should get focus"); view.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); // Expected: Should wrap back to content, not go to padding Assert.True (contentButton.HasFocus, "Should stay in content when Padding has no thickness"); Assert.False (paddingButton.HasFocus, "Padding button should not receive focus"); view.Dispose (); } [Fact] [Trait ("Category", "Adornment")] [Trait ("Category", "Navigation")] public void AdvanceFocus_Disabled_Adornment_SubView_Should_Be_Skipped () { // Setup: View with disabled subview in Padding View view = new () { Id = "view", Width = 10, Height = 10, CanFocus = true }; view.Padding!.Thickness = new Thickness (1); View paddingButton = new () { Id = "paddingButton", CanFocus = true, TabStop = TabBehavior.TabStop, Enabled = false, // Disabled X = 0, Y = 0, Width = 5, Height = 1 }; view.Padding.Add (paddingButton); View contentButton = new () { Id = "contentButton", CanFocus = true, TabStop = TabBehavior.TabStop, X = 0, Y = 0, Width = 5, Height = 1 }; view.Add (contentButton); view.BeginInit (); view.EndInit (); // Test: Navigate - disabled padding button should be skipped view.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); Assert.True (contentButton.HasFocus, "Content should get focus"); view.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); // Expected: Should wrap back to content, skipping disabled padding button Assert.True (contentButton.HasFocus, "Should skip disabled padding button"); Assert.False (paddingButton.HasFocus, "Disabled padding button should not receive focus"); view.Dispose (); } #endregion }