From 1dd2d9f38c620a2671e5d317472f8aa2b53b8067 Mon Sep 17 00:00:00 2001 From: Tig Date: Sat, 21 Sep 2024 14:29:51 -0600 Subject: [PATCH] Added Application.Mouse tests --- Terminal.Gui/Application/Application.Mouse.cs | 2 +- Terminal.Gui/View/View.Mouse.cs | 9 +- .../Application/Mouse/MouseEnterLeaveTests.cs | 337 ++++++++++++++++++ .../Application/{ => Mouse}/MouseTests.cs | 0 UnitTests/View/Mouse/MouseEnterLeaveTests.cs | 9 +- 5 files changed, 345 insertions(+), 12 deletions(-) create mode 100644 UnitTests/Application/Mouse/MouseEnterLeaveTests.cs rename UnitTests/Application/{ => Mouse}/MouseTests.cs (100%) diff --git a/Terminal.Gui/Application/Application.Mouse.cs b/Terminal.Gui/Application/Application.Mouse.cs index 4dac42643..3cce3e5da 100644 --- a/Terminal.Gui/Application/Application.Mouse.cs +++ b/Terminal.Gui/Application/Application.Mouse.cs @@ -128,7 +128,7 @@ public static partial class Application // Mouse handling /// public static event EventHandler? MouseEvent; - /// Called when a mouse event occurs. Raises the event. + /// Called when a mouse event is raised by the driver. /// This method can be used to simulate a mouse event, e.g. in unit tests. /// The mouse event with coordinates relative to the screen. internal static void OnMouseEvent (MouseEvent mouseEvent) diff --git a/Terminal.Gui/View/View.Mouse.cs b/Terminal.Gui/View/View.Mouse.cs index 8a5324695..b86922ac0 100644 --- a/Terminal.Gui/View/View.Mouse.cs +++ b/Terminal.Gui/View/View.Mouse.cs @@ -323,7 +323,7 @@ public partial class View // Mouse APIs /// /// /// - /// A view must be visible to receive Enter/Leave events. + /// A view must be visible to receive Enter events (Leave events are always received). /// /// /// This method calls to raise the event. @@ -376,7 +376,7 @@ public partial class View // Mouse APIs /// /// /// - /// A view must be visible to receive Enter/Leave events. + /// A view must be visible to receive Enter events (Leave events are always received). /// /// /// This method calls to raise the event. @@ -392,11 +392,6 @@ public partial class View // Mouse APIs /// if the event was handled, otherwise. internal bool? NewMouseLeaveEvent (MouseEvent mouseEvent) { - if (!CanBeVisible (this)) - { - return false; - } - if (OnMouseLeave (mouseEvent)) { return true; diff --git a/UnitTests/Application/Mouse/MouseEnterLeaveTests.cs b/UnitTests/Application/Mouse/MouseEnterLeaveTests.cs new file mode 100644 index 000000000..e6a85e91a --- /dev/null +++ b/UnitTests/Application/Mouse/MouseEnterLeaveTests.cs @@ -0,0 +1,337 @@ +namespace Terminal.Gui.ViewMouseTests; + +[Trait ("Category", "Input")] +public class ApplicationMouseEnterLeaveTests +{ + private class TestView : View + { + public TestView () + { + X = 1; + Y = 1; + Width = 1; + Height = 1; + } + + public bool HandleOnEnter { get; } + public bool HandleOnLeave { get; } + + public int OnMouseEnterCalled { get; private set; } + public int OnMouseLeaveCalled { get; private set; } + + protected internal override bool? OnMouseEnter (MouseEvent mouseEvent) + { + OnMouseEnterCalled++; + mouseEvent.Handled = HandleOnEnter; + + base.OnMouseEnter (mouseEvent); + + return mouseEvent.Handled; + } + + protected internal override bool OnMouseLeave (MouseEvent mouseEvent) + { + OnMouseLeaveCalled++; + mouseEvent.Handled = HandleOnLeave; + + base.OnMouseLeave (mouseEvent); + + return mouseEvent.Handled; + } + } + + [Fact] + public void RaiseMouseEnterLeaveEvents_MouseEntersView_CallsOnMouseEnter () + { + // Arrange + Application.Top = new () { Frame = new (0, 0, 10, 10) }; + var view = new TestView (); + Application.Top.Add (view); + var mousePosition = new Point (1, 1); + List currentViewsUnderMouse = new () { view }; + var mouseEvent = new MouseEvent () + { + Position = mousePosition, + ScreenPosition = mousePosition + }; + + Application._cachedViewsUnderMouse.Clear (); + + try + { + // Act + Application.RaiseMouseEnterLeaveEvents (mousePosition, currentViewsUnderMouse, mouseEvent); + + // Assert + Assert.Equal (1, view.OnMouseEnterCalled); + } + finally + { + // Cleanup + Application.Top?.Dispose (); + Application.ResetState (); + } + } + + [Fact] + public void RaiseMouseEnterLeaveEvents_MouseLeavesView_CallsOnMouseLeave () + { + // Arrange + Application.Top = new () { Frame = new (0, 0, 10, 10) }; + var view = new TestView (); + Application.Top.Add (view); + var mousePosition = new Point (0, 0); + List currentViewsUnderMouse = new (); + var mouseEvent = new MouseEvent (); + + Application._cachedViewsUnderMouse.Clear (); + Application._cachedViewsUnderMouse.Add (view); + + try + { + // Act + Application.RaiseMouseEnterLeaveEvents (mousePosition, currentViewsUnderMouse, mouseEvent); + + // Assert + Assert.Equal (0, view.OnMouseEnterCalled); + Assert.Equal (1, view.OnMouseLeaveCalled); + } + finally + { + // Cleanup + Application.Top?.Dispose (); + Application.ResetState (); + } + } + + [Fact] + public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenAdjacentViews_CallsOnMouseEnterAndLeave () + { + // Arrange + Application.Top = new () { Frame = new (0, 0, 10, 10) }; + var view1 = new TestView (); // at 1,1 to 2,2 + var view2 = new TestView () // at 2,2 to 3,3 + { + X = 2, + Y = 2, + }; + Application.Top.Add (view1); + Application.Top.Add (view2); + + + + Application._cachedViewsUnderMouse.Clear (); + + try + { + // Act + var mousePosition = new Point (0, 0); + Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent () + { + Position = mousePosition, + ScreenPosition = mousePosition + }); + + // Assert + Assert.Equal (0, view1.OnMouseEnterCalled); + Assert.Equal (0, view1.OnMouseLeaveCalled); + Assert.Equal (0, view2.OnMouseEnterCalled); + Assert.Equal (0, view2.OnMouseLeaveCalled); + + // Act + mousePosition = new Point (1, 1); + Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent () + { + Position = mousePosition, + ScreenPosition = mousePosition + }); + + // Assert + Assert.Equal (1, view1.OnMouseEnterCalled); + Assert.Equal (0, view1.OnMouseLeaveCalled); + Assert.Equal (0, view2.OnMouseEnterCalled); + Assert.Equal (0, view2.OnMouseLeaveCalled); + + // Act + mousePosition = new Point (2, 2); + Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent () + { + Position = mousePosition, + ScreenPosition = mousePosition + }); + + // Assert + Assert.Equal (1, view1.OnMouseEnterCalled); + Assert.Equal (1, view1.OnMouseLeaveCalled); + Assert.Equal (1, view2.OnMouseEnterCalled); + Assert.Equal (0, view2.OnMouseLeaveCalled); + + + // Act + mousePosition = new Point (3, 3); + Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent () + { + Position = mousePosition, + ScreenPosition = mousePosition + }); + + // Assert + Assert.Equal (1, view1.OnMouseEnterCalled); + Assert.Equal (1, view1.OnMouseLeaveCalled); + Assert.Equal (1, view2.OnMouseEnterCalled); + Assert.Equal (1, view2.OnMouseLeaveCalled); + + // Act + mousePosition = new Point (0, 0); + Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent () + { + Position = mousePosition, + ScreenPosition = mousePosition + }); + + // Assert + Assert.Equal (1, view1.OnMouseEnterCalled); + Assert.Equal (1, view1.OnMouseLeaveCalled); + Assert.Equal (1, view2.OnMouseEnterCalled); + Assert.Equal (1, view2.OnMouseLeaveCalled); + } + finally + { + // Cleanup + Application.Top?.Dispose (); + Application.ResetState (); + } + } + + [Fact] + public void RaiseMouseEnterLeaveEvents_NoViewsUnderMouse_DoesNotCallOnMouseEnterOrLeave () + { + // Arrange + Application.Top = new () { Frame = new (0, 0, 10, 10) }; + var view = new TestView (); + Application.Top.Add (view); + var mousePosition = new Point (0, 0); + List currentViewsUnderMouse = new (); + var mouseEvent = new MouseEvent (); + + Application._cachedViewsUnderMouse.Clear (); + + try + { + // Act + Application.RaiseMouseEnterLeaveEvents (mousePosition, currentViewsUnderMouse, mouseEvent); + + // Assert + Assert.Equal (0, view.OnMouseEnterCalled); + Assert.Equal (0, view.OnMouseLeaveCalled); + } + finally + { + // Cleanup + Application.Top?.Dispose (); + Application.ResetState (); + } + } + + + [Fact] + public void RaiseMouseEnterLeaveEvents_MouseMovesBetweenOverlappingViews_CallsOnMouseEnterAndLeave () + { + // Arrange + Application.Top = new () { Frame = new (0, 0, 10, 10) }; + var view1 = new TestView () + { + Width = 2 + }; // at 1,1 to 3,2 + var view2 = new TestView () // at 2,2 to 4,3 + { + Width = 2, + X = 2, + Y = 2, + }; + Application.Top.Add (view1); + Application.Top.Add (view2); + + Application._cachedViewsUnderMouse.Clear (); + + try + { + // Act + var mousePosition = new Point (0, 0); + Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent () + { + Position = mousePosition, + ScreenPosition = mousePosition + }); + + // Assert + Assert.Equal (0, view1.OnMouseEnterCalled); + Assert.Equal (0, view1.OnMouseLeaveCalled); + Assert.Equal (0, view2.OnMouseEnterCalled); + Assert.Equal (0, view2.OnMouseLeaveCalled); + + // Act + mousePosition = new Point (1, 1); + Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent () + { + Position = mousePosition, + ScreenPosition = mousePosition + }); + + // Assert + Assert.Equal (1, view1.OnMouseEnterCalled); + Assert.Equal (0, view1.OnMouseLeaveCalled); + Assert.Equal (0, view2.OnMouseEnterCalled); + Assert.Equal (0, view2.OnMouseLeaveCalled); + + // Act + mousePosition = new Point (2, 2); + Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent () + { + Position = mousePosition, + ScreenPosition = mousePosition + }); + + // Assert + Assert.Equal (1, view1.OnMouseEnterCalled); + Assert.Equal (1, view1.OnMouseLeaveCalled); + Assert.Equal (1, view2.OnMouseEnterCalled); + Assert.Equal (0, view2.OnMouseLeaveCalled); + + + // Act + mousePosition = new Point (3, 3); + Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent () + { + Position = mousePosition, + ScreenPosition = mousePosition + }); + + // Assert + Assert.Equal (1, view1.OnMouseEnterCalled); + Assert.Equal (1, view1.OnMouseLeaveCalled); + Assert.Equal (1, view2.OnMouseEnterCalled); + Assert.Equal (1, view2.OnMouseLeaveCalled); + + // Act + mousePosition = new Point (0, 0); + Application.RaiseMouseEnterLeaveEvents (mousePosition, View.GetViewsUnderMouse (mousePosition), new MouseEvent () + { + Position = mousePosition, + ScreenPosition = mousePosition + }); + + // Assert + Assert.Equal (1, view1.OnMouseEnterCalled); + Assert.Equal (1, view1.OnMouseLeaveCalled); + Assert.Equal (1, view2.OnMouseEnterCalled); + Assert.Equal (1, view2.OnMouseLeaveCalled); + } + finally + { + // Cleanup + Application.Top?.Dispose (); + Application.ResetState (); + } + } +} diff --git a/UnitTests/Application/MouseTests.cs b/UnitTests/Application/Mouse/MouseTests.cs similarity index 100% rename from UnitTests/Application/MouseTests.cs rename to UnitTests/Application/Mouse/MouseTests.cs diff --git a/UnitTests/View/Mouse/MouseEnterLeaveTests.cs b/UnitTests/View/Mouse/MouseEnterLeaveTests.cs index 75c92d4e9..1aed128d9 100644 --- a/UnitTests/View/Mouse/MouseEnterLeaveTests.cs +++ b/UnitTests/View/Mouse/MouseEnterLeaveTests.cs @@ -160,7 +160,7 @@ public class MouseEnterLeaveTests } [Fact] - public void NewMouseLeaveEvent_ViewIsNotVisible_DoesNotCallOnMouseLeave () + public void NewMouseLeaveEvent_ViewIsNotVisible_CallsOnMouseLeave () { // Arrange var view = new TestView @@ -175,7 +175,7 @@ public class MouseEnterLeaveTests bool? handled = view.NewMouseLeaveEvent (mouseEvent); // Assert - Assert.False (view.OnMouseLeaveCalled); + Assert.True (view.OnMouseLeaveCalled); Assert.False (handled); Assert.False (mouseEvent.Handled); @@ -263,7 +263,8 @@ public class MouseEnterLeaveTests // Arrange var view = new TestView { - Enabled = true, Visible = true + Enabled = true, + Visible = true }; var mouseEvent = new MouseEvent (); @@ -296,7 +297,7 @@ public class MouseEnterLeaveTests bool? handled = view.NewMouseLeaveEvent (mouseEvent); // Assert - Assert.False (view.MouseLeaveRaised); + Assert.True (view.MouseLeaveRaised); Assert.False (handled); Assert.False (mouseEvent.Handled);