Browse Source

Add ScrollBar unit tests and code cleanup.

BDisp 11 months ago
parent
commit
d3ab81eb19
2 changed files with 972 additions and 3 deletions
  1. 968 0
      UnitTests/Views/ScrollBarTests.cs
  2. 4 3
      UnitTests/Views/ScrollTests.cs

+ 968 - 0
UnitTests/Views/ScrollBarTests.cs

@@ -0,0 +1,968 @@
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.ViewsTests;
+
+public class ScrollBarTests
+{
+    public ScrollBarTests (ITestOutputHelper output) { _output = output; }
+    private readonly ITestOutputHelper _output;
+
+    [Theory]
+    [AutoInitShutdown]
+    [InlineData (
+                    20,
+                    @"
+▲
+█
+█
+█
+█
+░
+░
+░
+░
+▼",
+                    @"
+▲
+░
+░
+█
+█
+█
+█
+░
+░
+▼",
+                    @"
+▲
+░
+░
+░
+░
+█
+█
+█
+█
+▼",
+                    @"
+▲
+░
+░
+█
+█
+░
+░
+░
+░
+▼",
+                    @"
+◄████░░░░►",
+                    @"
+◄░░████░░►",
+                    @"
+◄░░░░████►",
+                    @"
+◄░░██░░░░►")]
+    [InlineData (
+                    40,
+                    @"
+▲
+█
+█
+░
+░
+░
+░
+░
+░
+▼",
+                    @"
+▲
+░
+█
+█
+░
+░
+░
+░
+░
+▼",
+                    @"
+▲
+░
+░
+█
+█
+░
+░
+░
+░
+▼",
+                    @"
+▲
+░
+█
+░
+░
+░
+░
+░
+░
+▼",
+                    @"
+◄██░░░░░░►",
+                    @"
+◄░██░░░░░►",
+                    @"
+◄░░██░░░░►",
+                    @"
+◄░█░░░░░░►")]
+    public void Changing_Position_Size_Orientation_Draws_Correctly (
+        int size,
+        string firstVertExpected,
+        string middleVertExpected,
+        string endVertExpected,
+        string sizeVertExpected,
+        string firstHoriExpected,
+        string middleHoriExpected,
+        string endHoriExpected,
+        string sizeHoriExpected
+    )
+    {
+        var scrollBar = new ScrollBar
+        {
+            Orientation = Orientation.Vertical,
+            Size = size,
+            Height = 10
+        };
+        var top = new Toplevel ();
+        top.Add (scrollBar);
+        Application.Begin (top);
+
+        _ = TestHelpers.AssertDriverContentsWithFrameAre (firstVertExpected, _output);
+
+        scrollBar.Position = 4;
+        Application.Refresh ();
+
+        _ = TestHelpers.AssertDriverContentsWithFrameAre (middleVertExpected, _output);
+
+        scrollBar.Position = 10;
+        Application.Refresh ();
+
+        _ = TestHelpers.AssertDriverContentsWithFrameAre (endVertExpected, _output);
+
+        scrollBar.Size = size * 2;
+        Application.Refresh ();
+
+        _ = TestHelpers.AssertDriverContentsWithFrameAre (sizeVertExpected, _output);
+
+        scrollBar.Orientation = Orientation.Horizontal;
+        scrollBar.Width = 10;
+        scrollBar.Height = 1;
+        scrollBar.Position = 0;
+        scrollBar.Size = size;
+        Application.Refresh ();
+
+        _ = TestHelpers.AssertDriverContentsWithFrameAre (firstHoriExpected, _output);
+
+        scrollBar.Position = 4;
+        Application.Refresh ();
+
+        _ = TestHelpers.AssertDriverContentsWithFrameAre (middleHoriExpected, _output);
+
+        scrollBar.Position = 10;
+        Application.Refresh ();
+
+        _ = TestHelpers.AssertDriverContentsWithFrameAre (endHoriExpected, _output);
+
+        scrollBar.Size = size * 2;
+        Application.Refresh ();
+
+        _ = TestHelpers.AssertDriverContentsWithFrameAre (sizeHoriExpected, _output);
+    }
+
+    [Fact]
+    public void Constructor_Defaults ()
+    {
+        var scrollBar = new ScrollBar ();
+        Assert.False (scrollBar.CanFocus);
+        Assert.Equal (Orientation.Vertical, scrollBar.Orientation);
+        Assert.Equal (0, scrollBar.Size);
+        Assert.Equal (0, scrollBar.Position);
+    }
+
+    [Theory]
+    [AutoInitShutdown]
+    [InlineData (
+                    Orientation.Vertical,
+                    20,
+                    10,
+                    4,
+                    @"
+▲
+░
+░
+░
+░
+█
+█
+█
+█
+▼",
+                    2,
+                    @"
+▲
+░
+█
+█
+█
+█
+░
+░
+░
+▼")]
+    [InlineData (
+                    Orientation.Vertical,
+                    40,
+                    10,
+                    5,
+                    @"
+▲
+░
+░
+█
+█
+░
+░
+░
+░
+▼",
+                    18,
+                    @"
+▲
+░
+░
+░
+░
+█
+█
+░
+░
+▼")]
+    [InlineData (
+                    Orientation.Horizontal,
+                    20,
+                    10,
+                    4,
+                    @"
+◄░░░░████►",
+                    2,
+                    @"
+◄░████░░░►")]
+    [InlineData (
+                    Orientation.Horizontal,
+                    40,
+                    10,
+                    5,
+                    @"
+◄░░██░░░░►",
+                    18,
+                    @"
+◄░░░░██░░►")]
+    public void Mouse_On_The_Container (Orientation orientation, int size, int position, int location, string output, int expectedPos, string expectedOut)
+    {
+        var scrollBar = new ScrollBar
+        {
+            Width = orientation == Orientation.Vertical ? 1 : 10,
+            Height = orientation == Orientation.Vertical ? 10 : 1,
+            Orientation = orientation, Size = size,
+            Position = position
+        };
+        var top = new Toplevel ();
+        top.Add (scrollBar);
+        Application.Begin (top);
+
+        _ = TestHelpers.AssertDriverContentsWithFrameAre (output, _output);
+
+        Application.OnMouseEvent (
+                                  new ()
+                                  {
+                                      Position = orientation == Orientation.Vertical ? new (0, location) : new Point (location, 0),
+                                      Flags = MouseFlags.Button1Pressed
+                                  });
+        Assert.Equal (expectedPos, scrollBar.Position);
+
+        Application.Refresh ();
+        _ = TestHelpers.AssertDriverContentsWithFrameAre (expectedOut, _output);
+    }
+
+    [Theory]
+    [AutoInitShutdown]
+    [InlineData (
+                    Orientation.Vertical,
+                    20,
+                    10,
+                    5,
+                    5,
+                    @"
+▲
+░
+░
+░
+░
+█
+█
+█
+█
+▼",
+                    MouseFlags.Button1Pressed,
+                    10,
+                    @"
+▲
+░
+░
+░
+░
+█
+█
+█
+█
+▼")]
+    [InlineData (
+                    Orientation.Vertical,
+                    40,
+                    10,
+                    3,
+                    3,
+                    @"
+▲
+░
+░
+█
+█
+░
+░
+░
+░
+▼",
+                    MouseFlags.Button1Pressed,
+                    10,
+                    @"
+▲
+░
+░
+█
+█
+░
+░
+░
+░
+▼")]
+    [InlineData (
+                    Orientation.Horizontal,
+                    20,
+                    10,
+                    5,
+                    5,
+                    @"
+◄░░░░████►",
+                    MouseFlags.Button1Pressed,
+                    10,
+                    @"
+◄░░░░████►")]
+    [InlineData (
+                    Orientation.Horizontal,
+                    40,
+                    10,
+                    3,
+                    3,
+                    @"
+◄░░██░░░░►",
+                    MouseFlags.Button1Pressed,
+                    10,
+                    @"
+◄░░██░░░░►")]
+    [InlineData (
+                    Orientation.Vertical,
+                    20,
+                    10,
+                    5,
+                    7,
+                    @"
+▲
+░
+░
+░
+░
+█
+█
+█
+█
+▼",
+                    MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
+                    12,
+                    @"
+▲
+░
+░
+░
+░
+█
+█
+█
+█
+▼")]
+    [InlineData (
+                    Orientation.Horizontal,
+                    20,
+                    10,
+                    5,
+                    4,
+                    @"
+◄░░░░████►",
+                    MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
+                    8,
+                    @"
+◄░░░████░►")]
+    [InlineData (
+                    Orientation.Vertical,
+                    20,
+                    10,
+                    5,
+                    6,
+                    @"
+▲
+░
+░
+░
+░
+█
+█
+█
+█
+▼",
+                    MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
+                    12,
+                    @"
+▲
+░
+░
+░
+░
+█
+█
+█
+█
+▼")]
+    [InlineData (
+                    Orientation.Horizontal,
+                    20,
+                    10,
+                    5,
+                    6,
+                    @"
+◄░░░░████►",
+                    MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
+                    12,
+                    @"
+◄░░░░████►")]
+    [InlineData (
+                    Orientation.Vertical,
+                    40,
+                    10,
+                    2,
+                    1,
+                    @"
+▲
+░
+░
+█
+█
+░
+░
+░
+░
+▼",
+                    MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
+                    2,
+                    @"
+▲
+█
+█
+░
+░
+░
+░
+░
+░
+▼")]
+    [InlineData (
+                    Orientation.Horizontal,
+                    40,
+                    10,
+                    2,
+                    1,
+                    @"
+◄░░██░░░░►",
+                    MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
+                    2,
+                    @"
+◄██░░░░░░►")]
+    [InlineData (
+                    Orientation.Vertical,
+                    40,
+                    10,
+                    3,
+                    4,
+                    @"
+▲
+░
+░
+█
+█
+░
+░
+░
+░
+▼",
+                    MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
+                    15,
+                    @"
+▲
+░
+░
+░
+█
+█
+░
+░
+░
+▼")]
+    [InlineData (
+                    Orientation.Horizontal,
+                    40,
+                    10,
+                    3,
+                    4,
+                    @"
+◄░░██░░░░►",
+                    MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
+                    15,
+                    @"
+◄░░░██░░░►")]
+    [InlineData (
+                    Orientation.Vertical,
+                    40,
+                    10,
+                    3,
+                    3,
+                    @"
+▲
+░
+░
+█
+█
+░
+░
+░
+░
+▼",
+                    MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
+                    10,
+                    @"
+▲
+░
+░
+█
+█
+░
+░
+░
+░
+▼")]
+    [InlineData (
+                    Orientation.Horizontal,
+                    40,
+                    10,
+                    3,
+                    3,
+                    @"
+◄░░██░░░░►",
+                    MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
+                    10,
+                    @"
+◄░░██░░░░►")]
+    [InlineData (
+                    Orientation.Vertical,
+                    40,
+                    10,
+                    3,
+                    5,
+                    @"
+▲
+░
+░
+█
+█
+░
+░
+░
+░
+▼",
+                    MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
+                    20,
+                    @"
+▲
+░
+░
+░
+░
+█
+█
+░
+░
+▼")]
+    [InlineData (
+                    Orientation.Horizontal,
+                    40,
+                    10,
+                    3,
+                    5,
+                    @"
+◄░░██░░░░►",
+                    MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
+                    20,
+                    @"
+◄░░░░██░░►")]
+    public void Mouse_On_The_Slider (
+        Orientation orientation,
+        int size,
+        int position,
+        int startLocation,
+        int endLocation,
+        string output,
+        MouseFlags mouseFlags,
+        int expectedPos,
+        string expectedOut
+    )
+    {
+        var scrollBar = new ScrollBar
+        {
+            Width = orientation == Orientation.Vertical ? 1 : 10,
+            Height = orientation == Orientation.Vertical ? 10 : 1,
+            Orientation = orientation,
+            Size = size, Position = position
+        };
+        var top = new Toplevel ();
+        top.Add (scrollBar);
+        Application.Begin (top);
+
+        _ = TestHelpers.AssertDriverContentsWithFrameAre (output, _output);
+
+        Assert.Null (Application.MouseGrabView);
+
+        if (mouseFlags.HasFlag (MouseFlags.ReportMousePosition))
+        {
+            MouseFlags mf = mouseFlags & ~MouseFlags.ReportMousePosition;
+
+            Application.OnMouseEvent (
+                                      new ()
+                                      {
+                                          Position = orientation == Orientation.Vertical ? new (0, startLocation) : new (startLocation, 0),
+                                          Flags = mf
+                                      });
+
+            Application.OnMouseEvent (
+                                      new ()
+                                      {
+                                          Position = orientation == Orientation.Vertical ? new (0, endLocation) : new (endLocation, 0),
+                                          Flags = mouseFlags
+                                      });
+        }
+        else
+        {
+            Assert.Equal (startLocation, endLocation);
+
+            Application.OnMouseEvent (
+                                      new ()
+                                      {
+                                          Position = orientation == Orientation.Vertical ? new (0, startLocation) : new (startLocation, 0),
+                                          Flags = mouseFlags
+                                      });
+        }
+
+        Assert.Equal ("scrollSlider", Application.MouseGrabView?.Id);
+        Assert.Equal (expectedPos, scrollBar.Position);
+
+        Application.Refresh ();
+        _ = TestHelpers.AssertDriverContentsWithFrameAre (expectedOut, _output);
+
+        Application.OnMouseEvent (
+                                  new ()
+                                  {
+                                      Position = orientation == Orientation.Vertical ? new (0, startLocation) : new (startLocation, 0),
+                                      Flags = MouseFlags.Button1Released
+                                  });
+        Assert.Null (Application.MouseGrabView);
+    }
+
+    [Theory]
+    [AutoInitShutdown]
+    [InlineData (Orientation.Vertical)]
+    [InlineData (Orientation.Horizontal)]
+    public void Moving_Mouse_Outside_Host_Ensures_Correct_Location (Orientation orientation)
+    {
+        var scrollBar = new ScrollBar
+        {
+            X = 10, Y = 10, Width = orientation == Orientation.Vertical ? 1 : 10, Height = orientation == Orientation.Vertical ? 10 : 1, Size = 20,
+            Position = 5, Orientation = orientation
+        };
+        var top = new Toplevel ();
+        top.Add (scrollBar);
+        Application.Begin (top);
+
+        var scroll = (Scroll)scrollBar.Subviews.FirstOrDefault (x => x is Scroll);
+        Rectangle scrollSliderFrame = scroll!.Subviews.FirstOrDefault (x => x.Id == "scrollSlider")!.Frame;
+        Assert.Equal (scrollSliderFrame, orientation == Orientation.Vertical ? new (0, 2, 1, 4) : new (2, 0, 4, 1));
+
+        Application.OnMouseEvent (new () { Position = orientation == Orientation.Vertical ? new (10, 14) : new (14, 10), Flags = MouseFlags.Button1Pressed });
+
+        Application.OnMouseEvent (
+                                  new ()
+                                  {
+                                      Position = orientation == Orientation.Vertical ? new (10, 0) : new (0, 10),
+                                      Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+                                  });
+        Assert.Equal (new (0, 0), scroll.Subviews.FirstOrDefault (x => x.Id == "scrollSlider")!.Frame.Location);
+
+        Application.OnMouseEvent (
+                                  new ()
+                                  {
+                                      Position = orientation == Orientation.Vertical ? new (0, 25) : new (80, 0),
+                                      Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+                                  });
+
+        Assert.Equal (
+                      orientation == Orientation.Vertical ? new (0, 4) : new (4, 0),
+                      scroll.Subviews.FirstOrDefault (x => x.Id == "scrollSlider")!.Frame.Location);
+    }
+
+    [Theory]
+    [InlineData (Orientation.Vertical, 20, 10)]
+    [InlineData (Orientation.Vertical, 40, 30)]
+    public void Position_Cannot_Be_Negative_Nor_Greater_Than_Size_Minus_Frame_Length (Orientation orientation, int size, int expectedPos)
+    {
+        var scrollBar = new ScrollBar { Orientation = orientation, Height = 10, Size = size };
+        Assert.Equal (0, scrollBar.Position);
+
+        scrollBar.Position = -1;
+        Assert.Equal (0, scrollBar.Position);
+
+        scrollBar.Position = size;
+        Assert.Equal (0, scrollBar.Position);
+
+        scrollBar.Position = expectedPos;
+        Assert.Equal (expectedPos, scrollBar.Position);
+    }
+
+    [Fact]
+    public void PositionChanging_Cancelable_And_PositionChanged_Events ()
+    {
+        var changingCount = 0;
+        var changedCount = 0;
+        var scrollBar = new ScrollBar { Size = 10 };
+
+        scrollBar.PositionChanging += (s, e) =>
+                                      {
+                                          if (changingCount == 0)
+                                          {
+                                              e.Cancel = true;
+                                          }
+
+                                          changingCount++;
+                                      };
+        scrollBar.PositionChanged += (s, e) => changedCount++;
+
+        scrollBar.Position = 1;
+        Assert.Equal (0, scrollBar.Position);
+        Assert.Equal (1, changingCount);
+        Assert.Equal (0, changedCount);
+
+        scrollBar.Position = 1;
+        Assert.Equal (1, scrollBar.Position);
+        Assert.Equal (2, changingCount);
+        Assert.Equal (1, changedCount);
+    }
+
+    [Fact]
+    public void PositionChanging_PositionChanged_Events_Only_Raises_Once_If_Position_Was_Really_Changed ()
+    {
+        var changing = 0;
+        var cancel = false;
+        var changed = 0;
+        var scrollBar = new ScrollBar { Height = 10, Size = 20 };
+        scrollBar.PositionChanging += Scroll_PositionChanging;
+        scrollBar.PositionChanged += Scroll_PositionChanged;
+
+        Assert.Equal (Orientation.Vertical, scrollBar.Orientation);
+        Assert.Equal (new (0, 0, 1, 10), scrollBar.Viewport);
+        Assert.Equal (0, scrollBar.Position);
+        Assert.Equal (0, changing);
+        Assert.Equal (0, changed);
+
+        scrollBar.Position = 0;
+        Assert.Equal (0, scrollBar.Position);
+        Assert.Equal (0, changing);
+        Assert.Equal (0, changed);
+
+        scrollBar.Position = 1;
+        Assert.Equal (1, scrollBar.Position);
+        Assert.Equal (1, changing);
+        Assert.Equal (1, changed);
+
+        Reset ();
+        cancel = true;
+        scrollBar.Position = 2;
+        Assert.Equal (1, scrollBar.Position);
+        Assert.Equal (1, changing);
+        Assert.Equal (0, changed);
+
+        Reset ();
+        scrollBar.Position = 10;
+        Assert.Equal (10, scrollBar.Position);
+        Assert.Equal (1, changing);
+        Assert.Equal (1, changed);
+
+        Reset ();
+        scrollBar.Position = 11;
+        Assert.Equal (11, scrollBar.Position);
+        Assert.Equal (1, changing);
+        Assert.Equal (1, changed);
+
+        Reset ();
+        scrollBar.Position = 12;
+        Assert.Equal (12, scrollBar.Position);
+        Assert.Equal (1, changing);
+        Assert.Equal (1, changed);
+
+        Reset ();
+        scrollBar.Position = 13;
+        Assert.Equal (12, scrollBar.Position);
+        Assert.Equal (0, changing);
+        Assert.Equal (0, changed);
+
+        Reset ();
+        scrollBar.Position = 0;
+        Assert.Equal (0, scrollBar.Position);
+        Assert.Equal (1, changing);
+        Assert.Equal (1, changed);
+
+        scrollBar.PositionChanging -= Scroll_PositionChanging;
+        scrollBar.PositionChanged -= Scroll_PositionChanged;
+
+        void Scroll_PositionChanging (object sender, CancelEventArgs<int> e)
+        {
+            changing++;
+            e.Cancel = cancel;
+        }
+
+        void Scroll_PositionChanged (object sender, EventArgs<int> e) { changed++; }
+
+        void Reset ()
+        {
+            changing = 0;
+            cancel = false;
+            changed = 0;
+        }
+    }
+
+    [Fact]
+    public void SizeChanged_Event ()
+    {
+        var count = 0;
+        var scrollBar = new ScrollBar ();
+        scrollBar.SizeChanged += (s, e) => count++;
+
+        scrollBar.Size = 10;
+        Assert.Equal (10, scrollBar.Size);
+        Assert.Equal (1, count);
+    }
+
+    [Theory]
+    [AutoInitShutdown]
+    [InlineData (
+                    3,
+                    10,
+                    1,
+                    Orientation.Vertical,
+                    @"
+┌─┐
+│▲│
+│█│
+│█│
+│░│
+│░│
+│░│
+│░│
+│▼│
+└─┘")]
+    [InlineData (
+                    10,
+                    3,
+                    1,
+                    Orientation.Horizontal,
+                    @"
+┌────────┐
+│◄██░░░░►│
+└────────┘")]
+    [InlineData (
+                    3,
+                    10,
+                    3,
+                    Orientation.Vertical,
+                    @"
+┌───┐
+│ ▲ │
+│███│
+│███│
+│░░░│
+│░░░│
+│░░░│
+│░░░│
+│ ▼ │
+└───┘")]
+    [InlineData (
+                    10,
+                    3,
+                    3,
+                    Orientation.Horizontal,
+                    @"
+┌────────┐
+│ ██░░░░ │
+│◄██░░░░►│
+│ ██░░░░ │
+└────────┘")]
+    public void Vertical_Horizontal_Draws_Correctly (int sizeWidth, int sizeHeight, int widthHeight, Orientation orientation, string expected)
+    {
+        var super = new Window { Id = "super", Width = Dim.Fill (), Height = Dim.Fill () };
+        var top = new Toplevel ();
+        top.Add (super);
+
+        var scrollBar = new ScrollBar
+        {
+            Orientation = orientation,
+            Size = orientation == Orientation.Vertical ? sizeHeight * 2 : sizeWidth * 2,
+            Width = orientation == Orientation.Vertical ? widthHeight : Dim.Fill (),
+            Height = orientation == Orientation.Vertical ? Dim.Fill () : widthHeight
+        };
+        super.Add (scrollBar);
+
+        Application.Begin (top);
+
+        ((FakeDriver)Application.Driver)!.SetBufferSize (
+                                                         sizeWidth + (orientation == Orientation.Vertical ? widthHeight - 1 : 0),
+                                                         sizeHeight + (orientation == Orientation.Vertical ? 0 : widthHeight - 1));
+
+        _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
+    }
+}

+ 4 - 3
UnitTests/Views/ScrollTests.cs

@@ -710,6 +710,7 @@ public class ScrollTests
     [Theory]
     [AutoInitShutdown]
     [InlineData (Orientation.Vertical)]
+    [InlineData (Orientation.Horizontal)]
     public void Moving_Mouse_Outside_Host_Ensures_Correct_Location (Orientation orientation)
     {
         var scroll = new Scroll
@@ -946,9 +947,9 @@ public class ScrollTests
 
         Application.Begin (top);
 
-        ((FakeDriver)Application.Driver).SetBufferSize (
-                                                        sizeWidth + (orientation == Orientation.Vertical ? widthHeight - 1 : 0),
-                                                        sizeHeight + (orientation == Orientation.Vertical ? 0 : widthHeight - 1));
+        ((FakeDriver)Application.Driver)!.SetBufferSize (
+                                                         sizeWidth + (orientation == Orientation.Vertical ? widthHeight - 1 : 0),
+                                                         sizeHeight + (orientation == Orientation.Vertical ? 0 : widthHeight - 1));
 
         _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
     }