diff --git a/src/MenuTracker.cs b/src/MenuTracker.cs
index ffe7d670..109c3b07 100644
--- a/src/MenuTracker.cs
+++ b/src/MenuTracker.cs
@@ -47,6 +47,22 @@ public void Register(MenuBar mb)
this.bars.Add(mb);
}
+ ///
+ /// Unregisters listeners for .
+ ///
+ /// to stop tracking.
+ public void UnregisterMenuBar( MenuBar? mb )
+ {
+ if ( !bars.TryTake( out mb ) )
+ {
+ return;
+ }
+
+ mb.MenuAllClosed -= MenuAllClosed;
+ mb.MenuOpened -= MenuOpened;
+ mb.MenuClosing -= MenuClosing;
+ }
+
///
///
/// Searches child items of all MenuBars tracked by this class
@@ -63,7 +79,7 @@ public void Register(MenuBar mb)
/// The immediate parent of .
/// Result may be a top level menu (e.g. File, View)
/// or a sub-menu parent (e.g. View=>Windows).
- public MenuBarItem? GetParent( MenuItem item, out MenuBar? hostBar )
+ private MenuBarItem? GetParent( MenuItem item, out MenuBar? hostBar )
{
foreach (var bar in this.bars)
{
@@ -83,7 +99,23 @@ public void Register(MenuBar mb)
return null;
}
- public bool TryGetParent(MenuItem item, [NotNullWhen(true)]out MenuBar? hostBar, [NotNullWhen(true)] out MenuBarItem? parentItem)
+ ///
+ /// Searches child items of all MenuBars tracked by this class to try and find the parent of the item passed.
+ ///
+ /// The item whose parent you want to find.
+ ///
+ /// When this method returns true, the that owns . Otherwise, if
+ /// not found or parent not registered (see ).
+ ///
+ ///
+ /// When this method returns , the immediate parent of . Otherwise,
+ ///
+ ///
+ ///
+ /// Search is recursive and dips into sub-menus. For sub-menus it is the immediate parent that is returned.
+ ///
+ /// A indicating if the search was successful or not.
+ public bool TryGetParent( MenuItem item, [NotNullWhen( true )] out MenuBar? hostBar, [NotNullWhen( true )] out MenuBarItem? parentItem )
{
var parentCandidate = GetParent( item, out hostBar );
if ( parentCandidate is null )
@@ -106,22 +138,21 @@ public bool TryGetParent(MenuItem item, [NotNullWhen(true)]out MenuBar? hostBar,
/// the substitution object ( ). See
///
/// for more information.
- public Dictionary ConvertEmptyMenus()
+ public Dictionary ConvertEmptyMenus( )
{
- var toReturn = new Dictionary();
-
+ Dictionary dictionary = [];
foreach (var b in this.bars)
{
foreach (var bi in b.Menus)
{
- foreach (var converted in this.ConvertEmptyMenus(b, bi))
+ foreach ( ( MenuBarItem? convertedMenuBarItem, MenuItem? convertedMenuItem ) in this.ConvertEmptyMenus( dictionary, b, bi ) )
{
- toReturn.Add(converted.Key, converted.Value);
+ dictionary.TryAdd( convertedMenuBarItem, convertedMenuItem );
}
}
}
- return toReturn;
+ return dictionary;
}
///
@@ -137,12 +168,12 @@ public Dictionary ConvertEmptyMenus()
/// The result of the conversion (same text, same index etc but
/// instead of ).
/// if conversion was possible (menu was empty and belonged to tracked menu).
- internal static bool ConvertMenuBarItemToRegularItemIfEmpty(MenuBarItem bar, out MenuItem? added)
+ internal static bool ConvertMenuBarItemToRegularItemIfEmpty( MenuBarItem bar, [NotNullWhen( true )] out MenuItem? added )
{
added = null;
// bar still has more children so don't convert
- if (bar.Children.Any())
+ if ( bar.Children.Length != 0 )
{
return false;
}
@@ -152,8 +183,7 @@ internal static bool ConvertMenuBarItemToRegularItemIfEmpty(MenuBarItem bar, out
return false;
}
- var children = parent.Children.ToList();
- var idx = children.IndexOf(bar);
+ int idx = Array.IndexOf( parent.Children, bar );
if (idx < 0)
{
@@ -161,39 +191,32 @@ internal static bool ConvertMenuBarItemToRegularItemIfEmpty(MenuBarItem bar, out
}
// bar has no children so convert to MenuItem
- added = new MenuItem { Title = bar.Title };
- added.Data = bar.Data;
- added.Shortcut = bar.Shortcut;
-
- children.RemoveAt(idx);
- children.Insert(idx, added);
-
- parent.Children = children.ToArray();
+ parent.Children[ idx ] = added = new( )
+ {
+ Title = bar.Title,
+ Data = bar.Data,
+ Shortcut = bar.Shortcut
+ };
return true;
}
///
- private Dictionary ConvertEmptyMenus(MenuBar bar, MenuBarItem mbi)
+ private Dictionary ConvertEmptyMenus(Dictionary dictionary, MenuBar bar, MenuBarItem mbi)
{
- var toReturn = new Dictionary();
-
foreach (var c in mbi.Children.OfType())
{
- this.ConvertEmptyMenus(bar, c);
- if ( ConvertMenuBarItemToRegularItemIfEmpty( c, out var added))
+ this.ConvertEmptyMenus(dictionary,bar, c);
+ if ( ConvertMenuBarItemToRegularItemIfEmpty( c, out MenuItem? added))
{
- if (added != null)
- {
- toReturn.Add(c, added);
- }
+ dictionary.TryAdd( c, added );
bar.CloseMenu();
bar.OpenMenu();
}
}
- return toReturn;
+ return dictionary;
}
private void MenuClosing(object? sender, MenuClosingEventArgs obj)
@@ -204,7 +227,7 @@ private void MenuClosing(object? sender, MenuClosingEventArgs obj)
private void MenuOpened(object? sender, MenuOpenedEventArgs obj)
{
this.CurrentlyOpenMenuItem = obj.MenuItem;
- this.ConvertEmptyMenus();
+ this.ConvertEmptyMenus( );
}
private void MenuAllClosed(object? sender, EventArgs e)
diff --git a/src/Operations/MenuOperations/RemoveMenuItemOperation.cs b/src/Operations/MenuOperations/RemoveMenuItemOperation.cs
index 9bc1304c..35d696b2 100644
--- a/src/Operations/MenuOperations/RemoveMenuItemOperation.cs
+++ b/src/Operations/MenuOperations/RemoveMenuItemOperation.cs
@@ -64,10 +64,12 @@ public override void Undo()
return;
}
- var children = this.Parent.Children.ToList();
-
- children.Insert(this.removedAtIdx, this.OperateOn);
- this.Parent.Children = children.ToArray();
+ this.Parent.Children =
+ [
+ .. Parent.Children[ .. removedAtIdx ],
+ this.OperateOn,
+ .. Parent.Children[ removedAtIdx .. ]
+ ];
this.Bar?.SetNeedsDisplay();
// if any MenuBarItem were converted to vanilla MenuItem
@@ -123,12 +125,12 @@ protected override bool DoImpl()
return false;
}
- var children = this.Parent.Children.ToList();
-
- this.removedAtIdx = Math.Max(0, children.IndexOf(this.OperateOn));
-
- children.Remove(this.OperateOn);
- this.Parent.Children = children.ToArray();
+ this.removedAtIdx = Math.Max( 0, Array.IndexOf( Parent.Children, OperateOn ) );
+ this.Parent.Children =
+ [
+ .. Parent.Children[ ..removedAtIdx ],
+ .. Parent.Children[ ( removedAtIdx + 1 ).. ]
+ ];
this.Bar?.SetNeedsDisplay();
if (this.Bar != null)
@@ -137,27 +139,24 @@ protected override bool DoImpl()
}
// if a top level menu now has no children
- if (this.Bar != null)
+ var empty = this.Bar?.Menus.Where(bi => bi.Children.Length == 0).ToArray();
+ if (empty?.Any() == true)
{
- var empty = this.Bar.Menus.Where(bi => bi.Children.Length == 0).ToArray();
- if (empty.Any())
- {
- // remember where they were
- this.prunedEmptyTopLevelMenus = empty.ToDictionary(e => Array.IndexOf(this.Bar.Menus, e), v => v);
+ // remember where they were
+ this.prunedEmptyTopLevelMenus = empty.ToDictionary(e => Array.IndexOf(this.Bar.Menus, e), v => v);
- // and remove them
- this.Bar.Menus = this.Bar.Menus.Except(this.prunedEmptyTopLevelMenus.Values).ToArray();
- }
+ // and remove them
+ this.Bar.Menus = this.Bar.Menus.Except(this.prunedEmptyTopLevelMenus.Values).ToArray();
+ }
- // if we just removed the last menu header
- // leaving a completely blank menu bar
- if (this.Bar.Menus.Length == 0 && this.Bar.SuperView != null)
- {
- // remove the bar completely
- this.Bar.CloseMenu();
- this.barRemovedFrom = this.Bar.SuperView;
- this.barRemovedFrom.Remove(this.Bar);
- }
+ // if we just removed the last menu header
+ // leaving a completely blank menu bar
+ if (this.Bar?.Menus.Length == 0 && this.Bar.SuperView != null)
+ {
+ // remove the bar completely
+ this.Bar.CloseMenu();
+ this.barRemovedFrom = this.Bar.SuperView;
+ this.barRemovedFrom.Remove(this.Bar);
}
return true;
diff --git a/src/TerminalGuiDesigner.csproj b/src/TerminalGuiDesigner.csproj
index dc993dbb..09c80a37 100644
--- a/src/TerminalGuiDesigner.csproj
+++ b/src/TerminalGuiDesigner.csproj
@@ -1,4 +1,4 @@
-
+
True
@@ -145,8 +145,8 @@
-
+
diff --git a/src/TerminalGuiDesigner.sln.DotSettings b/src/TerminalGuiDesigner.sln.DotSettings
new file mode 100644
index 00000000..98557a3a
--- /dev/null
+++ b/src/TerminalGuiDesigner.sln.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file
diff --git a/src/ViewFactory.cs b/src/ViewFactory.cs
index 2ed8d221..3d1898c2 100644
--- a/src/ViewFactory.cs
+++ b/src/ViewFactory.cs
@@ -28,7 +28,7 @@ public static class ViewFactory
internal const string DefaultMenuItemText = "Edit Me";
internal static readonly Type[] KnownUnsupportedTypes =
- {
+ [
typeof( Toplevel ),
typeof( Dialog ),
typeof( FileDialog ),
@@ -41,7 +41,7 @@ public static class ViewFactory
// BUG These seem to cause stack overflows in CreateSubControlDesigns (see TestAddView_RoundTrip)
typeof( Wizard ),
typeof( WizardStep )
- };
+ ];
///
/// Gets a new instance of a default [], to include as the default initial
@@ -56,12 +56,11 @@ internal static MenuBarItem[] DefaultMenuBarItems
{
get
{
- return new[]
- {
- new MenuBarItem(
- "_File (F9)",
- new[] { new MenuItem( DefaultMenuItemText, string.Empty, ( ) => { } ) } )
- };
+ return
+ [
+ new( "_File (F9)",
+ [ new MenuItem( DefaultMenuItemText, string.Empty, static ( ) => { } ) ] )
+ ];
}
}
diff --git a/tests/ListViewTests.cs b/tests/ListViewTests.cs
index 961450f8..4d42721a 100644
--- a/tests/ListViewTests.cs
+++ b/tests/ListViewTests.cs
@@ -78,7 +78,7 @@ public void TestIListSourceProperty_Rhs( )
Assert.That( lv.Source, Has.Count.EqualTo( 2 ) );
- var code = PropertyTests.ExpressionToCode( prop.GetRhs( ) );
+ var code = Helpers.ExpressionToCode( prop.GetRhs( ) );
Assert.That(
code, Is.EqualTo( "new Terminal.Gui.ListWrapper(new string[] {\n \"hi there\",\n \"my friend\"})".Replace( "\n", Environment.NewLine ) ) );
diff --git a/tests/MenuBarTests.cs b/tests/MenuBarTests.cs
index a2a2e467..6eea6ee3 100644
--- a/tests/MenuBarTests.cs
+++ b/tests/MenuBarTests.cs
@@ -3,10 +3,394 @@
namespace UnitTests;
[TestFixture]
-[TestOf(typeof(OperationManager))]
-[Category("UI")]
+[TestOf( typeof( OperationManager ) )]
+[Category( "UI" )]
internal class MenuBarTests : Tests
{
+ [Test]
+ [TestOf( typeof( RemoveMenuItemOperation ) )]
+ public void DeletingLastMenuItem_ShouldRemoveWholeBar( )
+ {
+ using MenuBar bar = GetMenuBar( out Design root );
+
+ MenuItem mi = bar.Menus[ 0 ].Children[ 0 ];
+
+ RemoveMenuItemOperation? removeMenuItemOperation = null;
+ Assert.That( ( ) => removeMenuItemOperation = new( mi ), Throws.Nothing );
+ Assert.That( removeMenuItemOperation, Is.Not.Null.And.InstanceOf( ) );
+
+ bool removeMenuItemOperationSucceeded = false;
+ Assert.That( ( ) => removeMenuItemOperationSucceeded = OperationManager.Instance.Do( removeMenuItemOperation! ), Throws.Nothing );
+ Assert.That( removeMenuItemOperationSucceeded );
+ Assert.Multiple( static ( ) =>
+ {
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ } );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( bar.Menus, Is.Empty );
+ Assert.That( root.View.Subviews, Has.None.InstanceOf( ) );
+ } );
+
+ Assert.That( OperationManager.Instance.Undo, Throws.Nothing );
+ Assert.Multiple( static ( ) =>
+ {
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.EqualTo( 1 ) );
+ // TODO: This needs to clean up after itself in a safe fashion
+ } );
+
+ // Same conditions as at the start
+ // The MenuBar should be back in the root view...
+ Assert.That( root.View.Subviews, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.That( root.View.Subviews[ 0 ], Is.Not.Null.And.SameAs( bar ) );
+
+ // ...And the original MenuBar should be back as it was at the start.
+ Assert.That( bar.Menus, Is.Not.Null );
+ Assert.That( bar.Menus, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.That( bar.Menus[ 0 ], Is.Not.Null.And.InstanceOf( ) );
+ Assert.That( bar.Menus[ 0 ].Children, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.That( bar.Menus[ 0 ].Children[ 0 ], Is.Not.Null.And.InstanceOf( ) );
+ }
+
+ [Test]
+ [TestOf( typeof( RemoveMenuItemOperation ) )]
+ public void DeletingMenuItemFromSubmenu_AllSubmenuChild( )
+ {
+ using MenuBarWithSubmenuItems m = GetMenuBarWithSubmenuItems( );
+
+ MenuItem? bottomChild = m.Head2.Children[ 1 ];
+
+ Assume.That( m.Bar.Menus[ 0 ].Children, Has.Exactly( 3 ).InstanceOf( ) );
+ Assume.That( m.Bar.Menus[ 0 ].Children[ 1 ], Is.Not.Null.And.InstanceOf( ) );
+ Assume.That( m.Head2.Children, Has.Exactly( 2 ).InstanceOf( ) );
+ Assume.That( m.Head2.Children[ 0 ], Is.SameAs( m.TopChild ) );
+
+ RemoveMenuItemOperation cmd1 = new( m.TopChild );
+ Assert.That( cmd1.Do, Throws.Nothing );
+
+ RemoveMenuItemOperation cmd2 = new( bottomChild );
+ Assert.That( cmd2.Do, Throws.Nothing );
+
+ // Deleting both children should convert us from
+ // a dropdown submenu to just a regular MenuItem
+ Assert.That( m.Bar.Menus[ 0 ].Children, Has.Exactly( 3 ).InstanceOf( ) );
+ Assert.That( m.Bar.Menus[ 0 ].Children[ 1 ], Is.Not.Null.And.InstanceOf( ) );
+
+ Assert.That( cmd2.Undo, Throws.Nothing );
+
+ // should bring the bottom one back
+ Assert.That( m.Bar.Menus[ 0 ].Children, Has.Exactly( 3 ).InstanceOf( ) );
+ Assert.That( m.Bar.Menus[ 0 ].Children[ 1 ], Is.Not.Null.And.InstanceOf( ) );
+ Assert.That( ( (MenuBarItem)m.Bar.Menus[ 0 ].Children[ 1 ] ).Children[ 0 ], Is.SameAs( bottomChild ) );
+
+ Assert.That( cmd1.Undo, Throws.Nothing );
+
+ // Both submenu items should now be back
+ Assert.That( m.Bar.Menus[ 0 ].Children, Has.Exactly( 3 ).InstanceOf( ) );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( m.Bar.Menus[ 0 ].Children[ 1 ], Is.Not.Null.And.InstanceOf( ) );
+ Assert.That( m.Head2.Children, Has.Exactly( 2 ).InstanceOf( ) );
+ } );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( m.Head2.Children[ 0 ], Is.SameAs( m.TopChild ) );
+ Assert.That( ( (MenuBarItem)m.Bar.Menus[ 0 ].Children[ 1 ] ).Children[ 0 ], Is.SameAs( m.TopChild ) );
+ Assert.That( ( (MenuBarItem)m.Bar.Menus[ 0 ].Children[ 1 ] ).Children[ 1 ], Is.SameAs( bottomChild ) );
+ } );
+ }
+
+ [Test]
+ [TestOf( typeof( RemoveMenuItemOperation ) )]
+ public void DeletingMenuItemFromSubmenu_TopChild( )
+ {
+ using MenuBarWithSubmenuItems m = GetMenuBarWithSubmenuItems( );
+
+ RemoveMenuItemOperation cmd = new ( m.TopChild );
+ bool cmdSucceeded = false;
+ Assert.That( ( ) => cmdSucceeded = cmd.Do( ), Throws.Nothing );
+ Assert.That( cmdSucceeded );
+
+ // Delete the top child should leave only 1 in submenu
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( m.Bar.Menus[ 0 ].Children, Has.Exactly( 3 ).InstanceOf( ) );
+ Assert.That( m.Head2.Children, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.That( m.Head2.Children[ 0 ], Is.Not.SameAs( m.TopChild ) );
+ } );
+
+ Assert.That( cmd.Undo, Throws.Nothing );
+
+ Assert.Multiple( ( ) =>
+ {
+ // should come back now
+ Assert.That( m.Bar.Menus[ 0 ].Children, Has.Exactly( 3 ).InstanceOf( ) );
+ Assert.That( m.Head2.Children, Has.Exactly( 2 ).InstanceOf( ) );
+ Assert.That( m.Head2.Children[ 0 ], Is.SameAs( m.TopChild ) );
+ } );
+ }
+
+ [Test]
+ [TestOf( typeof( MenuBarTests ) )]
+ [Category( "Change Control" )]
+ [Order( 1 )]
+ [Repeat( 10 )]
+ [Description( "Ensures that the GetMenuBar helper method returns the expected objects and doesn't fail even if used multiple times." )]
+ public void GetMenuBar_BehavesAsExpected( )
+ {
+ using MenuBar bar = GetMenuBar( out Design root );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( bar, Is.Not.Null.And.InstanceOf( ) );
+ Assert.That( root, Is.Not.Null.And.InstanceOf( ) );
+ } );
+ Assert.That( root.View.Subviews, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( root.View.Subviews[ 0 ], Is.Not.Null.And.SameAs( bar ) );
+ Assert.That( bar.Menus, Is.Not.Null );
+ } );
+ Assert.That( bar.Menus, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.That( bar.Menus[ 0 ], Is.Not.Null.And.InstanceOf( ) );
+ Assert.That( bar.Menus[ 0 ].Children, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( bar.Menus[ 0 ].Children[ 0 ], Is.Not.Null.And.InstanceOf( ) );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ } );
+ }
+
+ [Test]
+ [TestOf( typeof( MenuBarWithSubmenuItems ) )]
+ [Category( "Change Control" )]
+ [Order( 2 )]
+ [Repeat( 10 )]
+ [Description( "Ensures that the GetMenuBarWithSubmenuItems helper method returns the expected objects and doesn't fail even if used multiple times." )]
+ public void GetMenuBarWithSubmenuItems_BehavesAsExpected( )
+ {
+ using MenuBarWithSubmenuItems m = GetMenuBarWithSubmenuItems( );
+
+ Assert.That( m.Bar.Menus, Has.Exactly( 1 ).InstanceOf( ) );
+
+ MenuBarItem menu0 = m.Bar.Menus[ 0 ];
+ Assert.That( menu0.Children, Has.Exactly( 3 ).InstanceOf( ) );
+
+ // First item
+ MenuItem menu0Child0 = menu0.Children[ 0 ];
+ Assert.That( menu0Child0.Title, Is.EqualTo( "Head1" ) );
+
+ // Second item and its children
+ Assert.That( menu0.Children[ 1 ], Is.Not.Null.And.InstanceOf( ) );
+ MenuBarItem menu0Child1 = (MenuBarItem)menu0.Children[ 1 ];
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( menu0Child1.Title, Is.EqualTo( "Head2" ) );
+ Assert.That( menu0Child1.Children, Has.Exactly( 2 ).InstanceOf( ) );
+ } );
+ MenuItem menu0Child1Leaf0 = menu0Child1.Children[ 0 ];
+ MenuItem menu0Child1Leaf1 = menu0Child1.Children[ 1 ];
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( menu0Child1Leaf0.Title, Is.EqualTo( "Child1" ) );
+ Assert.That( menu0Child1Leaf0.Shortcut, Is.EqualTo( Key.J.WithCtrl.KeyCode ) );
+ Assert.That( menu0Child1Leaf1.Title, Is.EqualTo( "Child2" ) );
+ Assert.That( menu0Child1Leaf1.Shortcut, Is.EqualTo( Key.F.WithCtrl.KeyCode ) );
+ } );
+
+ // Third item
+ Assert.That( menu0.Children[ 2 ], Is.Not.Null.And.InstanceOf( ) );
+ MenuItem menu0Child2 = menu0.Children[ 2 ];
+ Assert.That( menu0Child2.Title, Is.EqualTo( "Head3" ) );
+
+ //Now just make sure the record properties were set to the right references
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( m.Head2, Is.SameAs( menu0Child1 ) );
+ Assert.That( m.TopChild, Is.SameAs( menu0Child1Leaf0 ) );
+ } );
+ }
+
+ [Test]
+ [TestOf( typeof( MoveMenuItemLeftOperation ) )]
+ public void MoveMenuItemLeft_CannotMoveRootItems( )
+ {
+ using MenuBar bar = GetMenuBar( );
+
+ // cannot move a root item
+ MoveMenuItemLeftOperation moveMenuItemLeftOperation = new( bar.Menus[ 0 ].Children[ 0 ] );
+ Assert.That( moveMenuItemLeftOperation.IsImpossible );
+ bool moveMenuItemLeftOperationSucceeded = false;
+ Assert.That( ( ) => moveMenuItemLeftOperationSucceeded = moveMenuItemLeftOperation.Do( ), Throws.Nothing );
+ Assert.That( moveMenuItemLeftOperationSucceeded, Is.False );
+ }
+
+ [Test]
+ public void MoveMenuItemLeft_MoveTopChild( )
+ {
+ using MenuBarWithSubmenuItems m = GetMenuBarWithSubmenuItems( );
+
+ MoveMenuItemLeftOperation moveMenuItemLeftOperation = new ( m.TopChild );
+ Assert.That( moveMenuItemLeftOperation.IsImpossible, Is.False );
+ bool moveMenuItemLeftOperationSucceeded = false;
+ Assert.That( ( ) => moveMenuItemLeftOperationSucceeded = moveMenuItemLeftOperation.Do( ), Throws.Nothing );
+ Assert.That( moveMenuItemLeftOperationSucceeded );
+
+ // move the top child left should pull
+ // it out of the submenu and onto the root
+ Assert.That( m.Bar.Menus[ 0 ].Children, Has.Exactly( 4 ).InstanceOf( ) );
+ Assert.That( m.Head2.Children, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.That( m.Head2.Children[ 0 ], Is.Not.SameAs( m.TopChild ) );
+
+ // it should be pulled out underneath its parent
+ Assert.That( m.Bar.Menus[ 0 ].Children[ 2 ], Is.SameAs( m.TopChild ) );
+
+ // undoing command should return us to previous state
+ Assert.That( moveMenuItemLeftOperation.Undo, Throws.Nothing );
+
+ Assert.That( m.Bar.Menus[ 0 ].Children, Has.Exactly( 3 ).InstanceOf( ) );
+ Assert.That( m.Head2.Children, Has.Exactly( 2 ).InstanceOf( ) );
+ Assert.That( m.Head2.Children[ 0 ], Is.SameAs( m.TopChild ) );
+ }
+
+ [Test]
+ [TestOf( typeof( AddMenuItemOperation ) )]
+ public void MoveMenuItemRight_CannotMoveElementZero( )
+ {
+ using MenuBar bar = GetMenuBar( );
+
+ MenuItem? mi = bar.Menus[ 0 ].Children[ 0 ];
+ mi.Data = "yarg";
+ mi.Shortcut = Key.Y.WithCtrl.KeyCode;
+
+ AddMenuItemOperation addAnother = new( mi );
+ Assert.That( addAnother.IsImpossible, Is.False );
+ bool addAnotherSucceeded = false;
+ Assert.That( ( ) => addAnotherSucceeded = addAnother.Do( ), Throws.Nothing );
+ Assert.That( addAnotherSucceeded );
+
+ // should have added below us
+ Assert.That( bar.Menus[ 0 ].Children, Has.Exactly( 2 ).InstanceOf( ) );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( bar.Menus[ 0 ].Children[ 0 ], Is.SameAs( mi ) );
+ Assert.That( bar.Menus[ 0 ].Children[ 1 ], Is.Not.SameAs( mi ) );
+ } );
+
+ // cannot move element 0
+ MoveMenuItemRightOperation impossibleMoveRightOp = new( bar.Menus[ 0 ].Children[ 0 ] );
+ Assert.That( impossibleMoveRightOp.IsImpossible );
+ bool impossibleMoveRightOpSucceeded = false;
+ Assert.That( ( ) => impossibleMoveRightOpSucceeded = impossibleMoveRightOp.Do( ), Throws.Nothing );
+ Assert.That( impossibleMoveRightOpSucceeded, Is.False );
+
+ // can move element 1
+ // This is a destructive action, so references will change.
+ MoveMenuItemRightOperation validMoveRightOp = new( bar.Menus[ 0 ].Children[ 1 ] );
+ Assert.That( validMoveRightOp.IsImpossible, Is.False );
+ bool validMoveRightOpSucceeded = false;
+ Assert.That( ( ) => validMoveRightOpSucceeded = validMoveRightOp.Do( ), Throws.Nothing );
+ Assert.That( validMoveRightOpSucceeded );
+
+ // We will have changed from a MenuItem to a MenuBarItem
+ // so element 0 will not be us. In Terminal.Gui there is
+ // a different class for a menu item and one with submenus.
+ Assert.That( bar.Menus[ 0 ].Children[ 0 ], Is.Not.Null.And.InstanceOf( ) );
+ MenuBarItem miConvertedToMenuBarItem = (MenuBarItem)bar.Menus[ 0 ].Children[ 0 ];
+
+ // Check that the references are unequal but values are equal
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( miConvertedToMenuBarItem, Is.Not.SameAs( mi ) );
+ Assert.That( miConvertedToMenuBarItem.Title, Is.EqualTo( mi.Title ) );
+ Assert.That( miConvertedToMenuBarItem.Data, Is.EqualTo( mi.Data ) );
+ Assert.That( miConvertedToMenuBarItem.Children, Has.Exactly( 1 ).InstanceOf( ) );
+ } );
+
+ // Now undo it.
+ // This is destructive as well.
+ Assert.That( validMoveRightOp.Undo, Throws.Nothing );
+
+ Assert.That( bar.Menus[ 0 ].Children, Has.Exactly( 2 ).InstanceOf( ) );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( bar.Menus[ 0 ].Children[ 0 ], Is.Not.Null.And.InstanceOf( ) );
+ Assert.That( bar.Menus[ 0 ].Children[ 1 ], Is.Not.Null.And.InstanceOf( ) );
+ } );
+ MenuItem firstChildAfterUndo = bar.Menus[ 0 ].Children[ 0 ];
+ MenuItem secondChildAfterUndo = bar.Menus[ 0 ].Children[ 1 ];
+
+ Assert.Multiple( ( ) =>
+ {
+ // All the previous references are gone forever through this process.
+ // So, neither element should be mi.
+ Assert.That( firstChildAfterUndo, Is.Not.SameAs( mi ) );
+ Assert.That( secondChildAfterUndo, Is.Not.SameAs( mi ) );
+
+ // Neither element should be miConvertedToMenuBarItem either
+ Assert.That( firstChildAfterUndo, Is.Not.SameAs( miConvertedToMenuBarItem ) );
+ Assert.That( secondChildAfterUndo, Is.Not.SameAs( miConvertedToMenuBarItem ) );
+
+ // And mi still should not be miConvertedToMenuBarItem
+ Assert.That( mi, Is.Not.SameAs( miConvertedToMenuBarItem ) );
+
+ // But the values need to be preserved
+ Assert.That( firstChildAfterUndo.Title, Is.EqualTo( mi.Title ) );
+ Assert.That( firstChildAfterUndo.Data, Is.EqualTo( mi.Data ) );
+ Assert.That( firstChildAfterUndo.Shortcut, Is.EqualTo( mi.Shortcut ) );
+ } );
+ }
+
+ ///
+ /// Tests that when there is only one menu item
+ /// that it cannot be moved into a submenu
+ ///
+ [Test]
+ [TestOf( typeof( MoveMenuItemRightOperation ) )]
+ public void MoveMenuItemRight_CannotMoveLast( )
+ {
+ MenuBar bar = GetMenuBar( );
+
+ MenuItem? mi = bar.Menus[ 0 ].Children[ 0 ];
+ MoveMenuItemRightOperation cmd = new( mi );
+ Assert.That( cmd.IsImpossible );
+ Assert.That( cmd.Do, Is.False );
+ }
+
+ ///
+ /// Tests removing the last menu item (i.e. 'Do Something')
+ /// under the only remaining menu header (e.g. 'File F9')
+ /// should result in a completely empty menu bar and be undoable
+ ///
+ [Test]
+ [TestOf( typeof( RemoveMenuItemOperation ) )]
+ public void RemoveFinalMenuItemOnBar( )
+ {
+ using MenuBar bar = GetMenuBar( );
+
+ MenuBarItem? fileMenu = bar.Menus[ 0 ];
+ MenuItem? placeholderMenuItem = fileMenu.Children[ 0 ];
+
+ RemoveMenuItemOperation removeOp = new( placeholderMenuItem );
+
+ // we are able to remove the last one
+ Assert.That( removeOp.IsImpossible, Is.False );
+ bool removeOpSucceeded = false;
+ Assert.That( ( ) => removeOpSucceeded = removeOp.Do( ), Throws.Nothing );
+ Assert.That( removeOpSucceeded );
+ Assert.That( bar.Menus, Is.Empty );
+
+ Assert.That( removeOp.Undo, Throws.Nothing );
+
+ // should be back to where we started
+ Assert.That( bar.Menus, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.That( bar.Menus[ 0 ].Children, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.That( bar.Menus[ 0 ].Children[ 0 ], Is.SameAs( placeholderMenuItem ) );
+ }
+
[Test]
public void RoundTrip_PreserveMenuItems()
{
@@ -31,7 +415,7 @@ public void RoundTrip_PreserveMenuItems()
Assume.That( addViewOperation, Is.Not.Null.And.InstanceOf( ) );
bool addViewOperationSucceeded = false;
- Assert.That( ( ) => addViewOperationSucceeded = OperationManager.Instance.Do( addViewOperation ), Throws.Nothing );
+ Assert.That( ( ) => addViewOperationSucceeded = OperationManager.Instance.Do( addViewOperation! ), Throws.Nothing );
Assert.That( addViewOperationSucceeded );
Assume.That( ( ) => viewToCode.GenerateDesignerCs( designOut, typeof( Dialog ) ), Throws.Nothing );
@@ -41,19 +425,19 @@ public void RoundTrip_PreserveMenuItems()
Assert.That( codeToView, Is.Not.Null.And.InstanceOf( ) );
Design? designBackIn = null;
- Assert.That( ( ) => designBackIn = codeToView.CreateInstance( ), Throws.Nothing );
+ Assert.That( ( ) => designBackIn = codeToView!.CreateInstance( ), Throws.Nothing );
Assert.That( designBackIn, Is.Not.Null.And.InstanceOf( ) );
// 1 visible root menu (e.g. File)
MenuBar? mbIn = null;
- Assert.That( designBackIn.View, Is.Not.Null.And.InstanceOf( ) );
+ Assert.That( designBackIn!.View, Is.Not.Null.And.InstanceOf( ) );
IList actualSubviews = designBackIn.View.GetActualSubviews();
Assert.That( actualSubviews, Has.Exactly( 1 ).InstanceOf( ) );
Assert.That( ( ) => mbIn = actualSubviews.OfType( ).Single( ), Throws.Nothing );
Assert.That( mbIn, Is.Not.Null.And.InstanceOf( ) );
// 1 child menu item (e.g. Open)
- Assert.That( mbIn.Menus, Is.Not.Null.And.Not.Empty );
+ Assert.That( mbIn!.Menus, Is.Not.Null.And.Not.Empty );
Assert.That( mbIn.Menus, Has.Exactly( 1 ).InstanceOf( ) );
Assert.That( mbIn.Menus[ 0 ].Children, Has.Exactly( 1 ).InstanceOf( ) );
Assert.That( mbIn.Menus[ 0 ].Children[ 0 ].Title, Is.EqualTo( mbOut.Menus[ 0 ].Children[ 0 ].Title ) );
@@ -91,11 +475,11 @@ public void RoundTrip_PreserveMenuItems_WithSubmenus( )
bool addChildMenuOperation1Succeeded = false;
bool addChildMenuOperation2Succeeded = false;
bool addChildMenuOperation3Succeeded = false;
- Assert.That( ( ) => addChildMenuOperation1Succeeded = addChildMenuOperation1.Do( ), Throws.Nothing );
+ Assert.That( ( ) => addChildMenuOperation1Succeeded = addChildMenuOperation1!.Do( ), Throws.Nothing );
Assert.That( addChildMenuOperation1Succeeded );
- Assert.That( ( ) => addChildMenuOperation2Succeeded = addChildMenuOperation2.Do( ), Throws.Nothing );
+ Assert.That( ( ) => addChildMenuOperation2Succeeded = addChildMenuOperation2!.Do( ), Throws.Nothing );
Assert.That( addChildMenuOperation2Succeeded );
- Assert.That( ( ) => addChildMenuOperation3Succeeded = addChildMenuOperation3.Do( ), Throws.Nothing );
+ Assert.That( ( ) => addChildMenuOperation3Succeeded = addChildMenuOperation3!.Do( ), Throws.Nothing );
Assert.That( addChildMenuOperation3Succeeded );
} );
@@ -122,7 +506,7 @@ public void RoundTrip_PreserveMenuItems_WithSubmenus( )
Design? designBackIn = null;
- Assume.That( ( ) => designBackIn = codeToView.CreateInstance( ), Throws.Nothing );
+ Assume.That( ( ) => designBackIn = codeToView!.CreateInstance( ), Throws.Nothing );
Assume.That( designBackIn, Is.Not.Null.And.InstanceOf( ) );
MenuBar? mbIn = null;
@@ -134,7 +518,7 @@ public void RoundTrip_PreserveMenuItems_WithSubmenus( )
Assert.That( mbIn, Is.Not.Null.And.InstanceOf( ) );
// 1 visible root menu (e.g. File)
- Assert.That( mbIn.Menus, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.That( mbIn!.Menus, Has.Exactly( 1 ).InstanceOf( ) );
// 3 child menu item (original one + 3 we added -1 because we moved it to submenu)
Assert.That( mbIn.Menus[ 0 ].Children, Has.Exactly( 3 ).InstanceOf( ) );
Assert.That( mbIn.Menus[ 0 ].Children, Has.All.Not.Null );
@@ -147,18 +531,21 @@ public void RoundTrip_PreserveMenuItems_WithSubmenus( )
}
[Test]
- [TestOf(typeof(MenuTracker))]
- public void TestMenuOperations()
+ [TestOf( typeof( MenuTracker ) )]
+ // TODO: Break this one up into smaller units at some point.
+ public void TestMenuOperations( )
{
- ViewToCode viewToCode = new ();
+ ViewToCode viewToCode = new( );
- FileInfo file = new ($"{nameof(TestMenuOperations)}.cs");
+ FileInfo file = new( $"{nameof( TestMenuOperations )}.cs" );
Design designOut = viewToCode.GenerateNewView( file, "YourNamespace", typeof( Dialog ) );
Assume.That( designOut, Is.Not.Null.And.InstanceOf( ) );
Assume.That( designOut.View, Is.Not.Null.And.InstanceOf( ) );
- MenuBar mbOut = ViewFactory.Create( );
+ using MenuBar mbOut = ViewFactory.Create( );
Assume.That( mbOut, Is.Not.Null.And.InstanceOf( ) );
+ Assume.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ Assume.That( OperationManager.Instance.RedoStackSize, Is.Zero );
Assert.Warn( "MenuTracker.Instance.CurrentlyOpenMenuItem cannot be guaranteed null at this time. See https://github.com/gui-cs/TerminalGuiDesigner/issues/270" );
// TODO: Enable this pre-condition once MenuTracker changes are implemented.
@@ -168,192 +555,196 @@ public void TestMenuOperations()
MenuTracker.Instance.Register( mbOut );
// 1 visible root menu (e.g. File)
- ClassicAssert.AreEqual(1, mbOut.Menus.Length);
+ Assert.That( mbOut.Menus, Has.Exactly( 1 ).InstanceOf( ) );
// 1 child menu item (e.g. Open)
- ClassicAssert.AreEqual(1, mbOut.Menus[0].Children.Length);
+ Assert.That( mbOut.Menus[ 0 ].Children, Has.Exactly( 1 ).InstanceOf( ) );
- MenuItem? orig = mbOut.Menus[0].Children[0];
+ MenuItem? orig = mbOut.Menus[ 0 ].Children[ 0 ];
+ Assert.That( orig, Is.Not.Null.And.InstanceOf( ) );
- OperationManager.Instance.Do(
- new AddMenuItemOperation(mbOut.Menus[0].Children[0]));
+ AddMenuItemOperation? addMenuItemOperation = null;
+ Assert.That( ( ) => addMenuItemOperation = new( mbOut.Menus[ 0 ].Children[ 0 ] ), Throws.Nothing );
+ Assert.That( addMenuItemOperation, Is.Not.Null.And.InstanceOf( ) );
+
+ bool addMenuItemOperationSucceeded = false;
+ Assert.That( ( ) => addMenuItemOperationSucceeded = OperationManager.Instance.Do( addMenuItemOperation! ), Throws.Nothing );
+ Assert.That( addMenuItemOperationSucceeded );
// Now 2 child menu item
- ClassicAssert.AreEqual(2, mbOut.Menus[0].Children.Length);
- ClassicAssert.AreSame(orig, mbOut.Menus[0].Children[0]); // original is still at top
+ Assert.That( mbOut.Menus[ 0 ].Children, Has.Exactly( 2 ).InstanceOf( ) );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( mbOut.Menus[ 0 ].Children[ 0 ], Is.Not.Null.And.SameAs( orig ) ); // original is still at top
+ Assert.That( mbOut.Menus[ 0 ].Children[ 1 ], Is.Not.Null.And.Not.SameAs( orig ) );
+ } );
+
+ Assert.Multiple( static ( ) =>
+ {
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ } );
OperationManager.Instance.Undo();
+ Assert.Multiple( static ( ) =>
+ {
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.EqualTo( 1 ) );
+ } );
+
// Now only 1 child menu item
- ClassicAssert.AreEqual(1, mbOut.Menus[0].Children.Length);
- ClassicAssert.AreSame(orig, mbOut.Menus[0].Children[0]); // original is still at top
+ Assert.That( mbOut.Menus[ 0 ].Children, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.That( mbOut.Menus[ 0 ].Children[ 0 ], Is.Not.Null.And.SameAs( orig ) ); // original is still at top
OperationManager.Instance.Redo();
- // Now 2 child menu item
- ClassicAssert.AreEqual(2, mbOut.Menus[0].Children.Length);
- ClassicAssert.AreSame(orig, mbOut.Menus[0].Children[0]); // original is still at top
+ Assert.Multiple( static ( ) =>
+ {
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ } );
+
+ // Now 2 child menu items again
+ Assert.That( mbOut.Menus[ 0 ].Children, Has.Exactly( 2 ).InstanceOf( ) );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( mbOut.Menus[ 0 ].Children[ 0 ], Is.Not.Null.And.SameAs( orig ) ); // original is still at top
+ Assert.That( mbOut.Menus[ 0 ].Children[ 1 ], Is.Not.Null.And.Not.SameAs( orig ) ); // original is still at top
+ } );
// Now test moving an item around
- MenuItem? toMove = mbOut.Menus[0].Children[1];
+ MenuItem? toMove = mbOut.Menus[ 0 ].Children[ 1 ];
+ Assume.That( toMove, Is.Not.Null.And.InstanceOf( ) );
// Move second menu item up
- MoveMenuItemOperation up = new MoveMenuItemOperation(toMove, true);
- ClassicAssert.IsFalse(up.IsImpossible);
- OperationManager.Instance.Do(up);
+ MoveMenuItemOperation? up = null;
+ Assert.That( ( ) => up = new( toMove, true ), Throws.Nothing );
+ Assert.That( up, Is.Not.Null.And.InstanceOf( ) );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( up!.Bar, Is.SameAs( mbOut ) );
+ Assert.That( up.IsImpossible, Is.False );
+ } );
+
+ bool moveUpSucceeded = false;
+ Assert.That( ( ) => moveUpSucceeded = OperationManager.Instance.Do( up! ), Throws.Nothing );
+ Assert.That( moveUpSucceeded );
+
+ Assert.Multiple( static ( ) =>
+ {
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 2 ) );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ } );
// Original one should now be bottom
- ClassicAssert.AreSame(orig, mbOut.Menus[0].Children[1]);
+ Assume.That( orig, Is.Not.SameAs( toMove ) );
+ Assert.That( mbOut.Menus[ 0 ].Children, Has.Exactly( 2 ).InstanceOf( ) );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( mbOut.Menus[ 0 ].Children[ 0 ], Is.Not.Null.And.Not.SameAs( orig ) );
+ Assert.That( mbOut.Menus[ 0 ].Children[ 0 ], Is.Not.Null.And.SameAs( toMove ) );
+ Assert.That( mbOut.Menus[ 0 ].Children[ 1 ], Is.SameAs( orig ) );
+ } );
// can't move top one up
- ClassicAssert.IsTrue(new MoveMenuItemOperation(toMove, true).IsImpossible);
+ MoveMenuItemOperation? impossibleMoveUpOperation = null;
+ Assert.That( ( ) => impossibleMoveUpOperation = new( toMove, true ), Throws.Nothing );
+ Assert.That( impossibleMoveUpOperation, Is.Not.Null.And.InstanceOf( ) );
+ Assert.That( impossibleMoveUpOperation!.IsImpossible );
+
// cant move bottom one down
- ClassicAssert.IsTrue(new MoveMenuItemOperation(mbOut.Menus[0].Children[1], false).IsImpossible);
+ MoveMenuItemOperation? impossibleMoveDownOperation = null;
+ Assert.That( ( ) => impossibleMoveDownOperation = new( mbOut.Menus[ 0 ].Children[ 1 ], false ), Throws.Nothing );
+ Assert.That( impossibleMoveDownOperation, Is.Not.Null.And.InstanceOf( ) );
+ Assert.That( impossibleMoveDownOperation!.IsImpossible );
- OperationManager.Instance.Undo();
+ Assert.That( static ( ) => OperationManager.Instance.Undo( ), Throws.Nothing );
// Original one should be back on top
- ClassicAssert.AreSame(orig, mbOut.Menus[0].Children[0]);
+ Assert.That( mbOut.Menus[ 0 ].Children, Has.Exactly( 2 ).InstanceOf( ) );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( mbOut.Menus[ 0 ].Children[ 0 ], Is.SameAs( orig ) );
+ Assert.That( mbOut.Menus[ 0 ].Children[ 0 ], Is.Not.SameAs( toMove ) );
+ Assert.That( mbOut.Menus[ 0 ].Children[ 1 ], Is.SameAs( toMove ) );
+ } );
// test moving the top one down
- MenuItem? toMove2 = mbOut.Menus[0].Children[1];
+ MenuItem? toMove2 = mbOut.Menus[ 0 ].Children[ 1 ];
// Move first menu item down
- MoveMenuItemOperation down = new MoveMenuItemOperation(toMove2, true);
- ClassicAssert.IsFalse(down.IsImpossible);
- OperationManager.Instance.Do(down);
-
- // Original one should now be bottom
- ClassicAssert.AreSame(orig, mbOut.Menus[0].Children[1]);
- ClassicAssert.AreNotSame(orig, mbOut.Menus[0].Children[0]);
-
- OperationManager.Instance.Undo();
-
- // should be back to how we started now
- ClassicAssert.AreSame(orig, mbOut.Menus[0].Children[0]);
- ClassicAssert.AreNotSame(orig, mbOut.Menus[0].Children[1]);
- }
-
- private MenuBar GetMenuBar()
- {
- return this.GetMenuBar(out _);
- }
-
- private MenuBar GetMenuBar(out Design root)
- {
- root = Get10By10View();
-
- var bar = ViewFactory.Create( );
- var addBarCmd = new AddViewOperation(bar, root, "mb");
- ClassicAssert.IsTrue(addBarCmd.Do());
+ MoveMenuItemOperation? down = null;
+ Assert.That( ( ) => down = new( toMove2, true ), Throws.Nothing );
+ Assert.That( down, Is.Not.Null.And.InstanceOf( ) );
+ Assert.That( down!.IsImpossible, Is.False );
- // Expect ViewFactory to have created a single
- // placeholder menu item
- ClassicAssert.AreEqual(1, bar.Menus.Length);
- ClassicAssert.AreEqual(1, bar.Menus[0].Children.Length);
-
- return bar;
- }
-
- ///
- /// Tests removing the last menu item (i.e. 'Do Something')
- /// under the only remaining menu header (e.g. 'File F9')
- /// should result in a completely empty menu bar and be undoable
- ///
- [Test]
- public void TestRemoveFinalMenuItemOnBar()
- {
- var bar = this.GetMenuBar();
+ Assert.Multiple( static ( ) =>
+ {
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.EqualTo( 1 ) );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ } );
- var fileMenu = bar.Menus[0];
- var placeholderMenuItem = fileMenu.Children[0];
+ bool moveDownSucceeded = false;
+ Assert.That( ( ) => moveDownSucceeded = OperationManager.Instance.Do( down ), Throws.Nothing );
+ Assert.That( moveDownSucceeded );
+ Assert.Multiple( static ( ) =>
+ {
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 2 ) );
+ } );
- var remove = new RemoveMenuItemOperation(placeholderMenuItem);
+ // Original one should now be bottom
+ Assert.That( mbOut.Menus[ 0 ].Children, Has.Exactly( 2 ).InstanceOf( ) );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( mbOut.Menus[ 0 ].Children[ 0 ], Is.SameAs( toMove2 ) );
+ Assert.That( mbOut.Menus[ 0 ].Children[ 0 ], Is.Not.SameAs( orig ) );
+ Assert.That( mbOut.Menus[ 0 ].Children[ 1 ], Is.SameAs( orig ) );
+ } );
- // we are able to remove the last one
- ClassicAssert.IsTrue(remove.Do());
- ClassicAssert.IsEmpty(bar.Menus, "menu bar should now be completely empty");
+ Assert.That( static ( ) => OperationManager.Instance.Undo( ), Throws.Nothing );
- remove.Undo();
+ Assert.Multiple( static ( ) =>
+ {
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.EqualTo( 1 ) );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ } );
- // should be back to where we started
- ClassicAssert.AreEqual(1, bar.Menus.Length);
- ClassicAssert.AreEqual(1, bar.Menus[0].Children.Length);
- ClassicAssert.AreSame(placeholderMenuItem, bar.Menus[0].Children[0]);
- }
- ///
- /// Tests that when there is only one menu item
- /// that it cannot be moved into a submenu
- ///
- [Test]
- public void TestMoveMenuItemRight_CannotMoveLast()
- {
- var bar = this.GetMenuBar();
+ // should be back to how we started now
+ Assert.That( mbOut.Menus[ 0 ].Children, Has.Exactly( 2 ).InstanceOf( ) );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( mbOut.Menus[ 0 ].Children[ 0 ], Is.SameAs( orig ) );
+ Assert.That( mbOut.Menus[ 0 ].Children[ 0 ], Is.Not.SameAs( toMove2 ) );
+ Assert.That( mbOut.Menus[ 0 ].Children[ 1 ], Is.SameAs( toMove2 ) );
+ } );
- var mi = bar.Menus[0].Children[0];
- var cmd = new MoveMenuItemRightOperation(mi);
- ClassicAssert.IsFalse(cmd.Do());
+ MenuTracker.Instance.UnregisterMenuBar( mbOut );
}
- [Test]
- public void TestMoveMenuItemRight_CannotMoveElementZero()
+ private static MenuBar GetMenuBar( )
{
- var bar = this.GetMenuBar();
-
- var mi = bar.Menus[0].Children[0];
- mi.Data = "yarg";
- mi.Shortcut = Key.Y.WithCtrl.KeyCode;
- var addAnother = new AddMenuItemOperation(mi);
- ClassicAssert.True(addAnother.Do());
-
- // should have added below us
- ClassicAssert.AreSame(mi, bar.Menus[0].Children[0]);
- ClassicAssert.AreNotSame(mi, bar.Menus[0].Children[1]);
- ClassicAssert.AreEqual(2, bar.Menus[0].Children.Length);
-
- // cannot move element 0
- ClassicAssert.IsFalse(new MoveMenuItemRightOperation(
- bar.Menus[0].Children[0])
- .Do());
-
- var cmd = new MoveMenuItemRightOperation(
- bar.Menus[0].Children[1]);
-
- // can move element 1
- ClassicAssert.IsTrue(cmd.Do());
-
- // We will have changed from a MenuItem to a MenuBarItem
- // so element 0 will not be us. In Terminal.Gui there is
- // a different class for a menu item and one with submenus
- ClassicAssert.AreNotSame(mi, bar.Menus[0].Children[0]);
- ClassicAssert.AreEqual(mi.Title, bar.Menus[0].Children[0].Title);
- ClassicAssert.AreEqual(mi.Data, bar.Menus[0].Children[0].Data);
- ClassicAssert.AreEqual(1, bar.Menus[0].Children.Length);
-
- cmd.Undo();
-
- ClassicAssert.AreEqual(mi.Title, bar.Menus[0].Children[0].Title);
- ClassicAssert.AreEqual(mi.Data, bar.Menus[0].Children[0].Data);
- ClassicAssert.AreEqual(mi.Shortcut, bar.Menus[0].Children[0].Shortcut);
- ClassicAssert.AreNotSame(mi, bar.Menus[0].Children[1]);
+ return GetMenuBar( out _ );
}
- [Test]
- public void TestMoveMenuItemLeft_CannotMoveRootItems()
+ private static MenuBar GetMenuBar( out Design root )
{
- var bar = this.GetMenuBar();
+ root = Get10By10View( );
- var mi = bar.Menus[0].Children[0];
+ var bar = ViewFactory.Create( );
+ var addBarCmd = new AddViewOperation( bar, root, "mb" );
+ addBarCmd.Do( );
- // cannot move a root item
- ClassicAssert.IsFalse(new MoveMenuItemLeftOperation(
- bar.Menus[0].Children[0])
- .Do());
+ return bar;
}
- private MenuBar GetMenuBarWithSubmenuItems(out MenuBarItem head2, out MenuItem topChild)
+ private static MenuBarWithSubmenuItems GetMenuBarWithSubmenuItems( )
{
- var bar = this.GetMenuBar();
+ MenuBarWithSubmenuItems toReturn = new( GetMenuBar( ), null!, null! )
+ {
+ Bar = GetMenuBar( )
+ };
// Set up a menu like:
/*
@@ -363,156 +754,54 @@ private MenuBar GetMenuBarWithSubmenuItems(out MenuBarItem head2, out MenuItem t
Head3 Child2
*/
- var mi = bar.Menus[0].Children[0];
+ var mi = toReturn.Bar.Menus[ 0 ].Children[ 0 ];
mi.Title = "Head1";
- bar.Menus[0].Children = new[]
+ toReturn.Bar.Menus[ 0 ].Children =
+ [
+ toReturn.Bar.Menus[ 0 ].Children[ 0 ],
+ toReturn.Head2 = CreateHead2Item( ),
+ new( "Head3", null, static ( ) => { } ),
+ ];
+
+ return toReturn;
+
+ MenuBarItem CreateHead2Item( )
{
- bar.Menus[0].Children[0],
- head2 = new MenuBarItem(new[]
+ return new( [toReturn.TopChild = CreateHead2Child1Item( ), CreateHead2Child2Item( )] )
{
- topChild = new MenuItem("Child1", null, () => { })
+ Title = "Head2",
+ };
+
+ static MenuItem CreateHead2Child1Item( )
+ {
+ return new( "Child1", null, static ( ) => { } )
{
Data = "Child1",
Shortcut = Key.J.WithCtrl.KeyCode,
- },
- new MenuItem("Child2", null, () => { })
+ };
+ }
+
+ static MenuItem CreateHead2Child2Item( )
+ {
+ return new( "Child2", null, static ( ) => { } )
{
Data = "Child2",
Shortcut = Key.F.WithCtrl.KeyCode,
- },
- })
- {
- Title = "Head2",
- },
- new MenuItem("Head3", null, () => { }),
- };
-
- return bar;
+ };
+ }
+ }
}
- [Test]
- public void TestMoveMenuItemLeft_MoveTopChild()
+ private sealed record MenuBarWithSubmenuItems( MenuBar Bar, MenuBarItem Head2, MenuItem TopChild ) : IDisposable
{
- var bar = this.GetMenuBarWithSubmenuItems(out var head2, out var topChild);
-
- ClassicAssert.AreEqual(3, bar.Menus[0].Children.Length);
- ClassicAssert.AreEqual(2, head2.Children.Length);
- ClassicAssert.AreSame(topChild, head2.Children[0]);
-
- var cmd = new MoveMenuItemLeftOperation(topChild);
- ClassicAssert.IsTrue(cmd.Do());
+ public MenuBarItem Head2 { get; set; } = Head2;
+ public MenuItem TopChild { get; set; } = TopChild;
- // move the top child left should pull
- // it out of the submenu and onto the root
- ClassicAssert.AreEqual(4, bar.Menus[0].Children.Length);
- ClassicAssert.AreEqual(1, head2.Children.Length);
-
- // it should be pulled out underneath its parent
- // and preserve its (Name) and Shortcuts
- ClassicAssert.AreEqual(topChild.Title, bar.Menus[0].Children[2].Title);
- ClassicAssert.AreEqual(topChild.Data, bar.Menus[0].Children[2].Data);
- ClassicAssert.AreEqual(topChild.Shortcut, bar.Menus[0].Children[2].Shortcut);
- ClassicAssert.AreSame(topChild, bar.Menus[0].Children[2]);
-
- // undoing command should return us to
- // previous state
- cmd.Undo();
-
- ClassicAssert.AreEqual(3, bar.Menus[0].Children.Length);
- ClassicAssert.AreEqual(2, head2.Children.Length);
-
- ClassicAssert.AreEqual(topChild.Title, head2.Children[0].Title);
- ClassicAssert.AreEqual(topChild.Data, head2.Children[0].Data);
- ClassicAssert.AreEqual(topChild.Shortcut, head2.Children[0].Shortcut);
- ClassicAssert.AreSame(topChild, head2.Children[0]);
- }
-
- [Test]
- public void TestDeletingMenuItemFromSubmenu_TopChild()
- {
- var bar = this.GetMenuBarWithSubmenuItems(out var head2, out var topChild);
-
- ClassicAssert.AreEqual(3, bar.Menus[0].Children.Length);
- ClassicAssert.AreEqual(2, head2.Children.Length);
- ClassicAssert.AreSame(topChild, head2.Children[0]);
-
- var cmd = new RemoveMenuItemOperation(topChild);
- ClassicAssert.IsTrue(cmd.Do());
-
- // Delete the top child should leave only 1 in submenu
- ClassicAssert.AreEqual(3, bar.Menus[0].Children.Length);
- ClassicAssert.AreEqual(1, head2.Children.Length);
- ClassicAssert.AreNotSame(topChild, head2.Children[0]);
-
- cmd.Undo();
-
- // should come back now
- ClassicAssert.AreEqual(3, bar.Menus[0].Children.Length);
- ClassicAssert.AreEqual(2, head2.Children.Length);
- ClassicAssert.AreSame(topChild, head2.Children[0]);
- }
-
- [Test]
- public void TestDeletingMenuItemFromSubmenu_AllSubmenuChild()
- {
- var bar = this.GetMenuBarWithSubmenuItems(out var head2, out var topChild);
- var bottomChild = head2.Children[1];
-
- ClassicAssert.AreEqual(3, bar.Menus[0].Children.Length);
- ClassicAssert.AreEqual(typeof(MenuBarItem), bar.Menus[0].Children[1].GetType());
- ClassicAssert.AreEqual(2, head2.Children.Length);
- ClassicAssert.AreSame(topChild, head2.Children[0]);
-
- var cmd1 = new RemoveMenuItemOperation(topChild);
- ClassicAssert.IsTrue(cmd1.Do());
-
- var cmd2 = new RemoveMenuItemOperation(bottomChild);
- ClassicAssert.IsTrue(cmd2.Do());
-
- // Deleting both children should convert us from
- // a dropdown submenu to just a regular MenuItem
- ClassicAssert.AreEqual(3, bar.Menus[0].Children.Length);
- ClassicAssert.AreEqual(typeof(MenuItem), bar.Menus[0].Children[1].GetType());
-
- cmd2.Undo();
-
- // should bring the bottom one back
- ClassicAssert.AreEqual(3, bar.Menus[0].Children.Length);
- ClassicAssert.AreEqual(typeof(MenuBarItem), bar.Menus[0].Children[1].GetType());
- ClassicAssert.AreSame(bottomChild, ((MenuBarItem)bar.Menus[0].Children[1]).Children[0]);
-
- cmd1.Undo();
-
- // Both submenu items should now be back
- ClassicAssert.AreEqual(3, bar.Menus[0].Children.Length);
- ClassicAssert.AreEqual(typeof(MenuBarItem), bar.Menus[0].Children[1].GetType());
- ClassicAssert.AreSame(topChild, ((MenuBarItem)bar.Menus[0].Children[1]).Children[0]);
- ClassicAssert.AreSame(bottomChild, ((MenuBarItem)bar.Menus[0].Children[1]).Children[1]);
- }
-
- [Test]
- public void TestDeletingLastMenuItem_ShouldRemoveWholeBar()
- {
- var bar = this.GetMenuBar(out Design root);
-
- var mi = bar.Menus[0].Children[0];
-
- ClassicAssert.Contains(bar, root.View.Subviews.ToArray(),
- "The MenuBar should be on the main view being edited");
-
- var cmd = new RemoveMenuItemOperation(mi);
- ClassicAssert.IsTrue(cmd.Do());
-
- ClassicAssert.IsEmpty(bar.Menus, "Expected menu bar header (File) to be removed along with it's last (only) child");
-
- ClassicAssert.IsFalse(
- root.View.Subviews.Contains(bar),
- "Now that the MenuBar is completely empty it should be automatically removed");
-
- cmd.Undo();
-
- ClassicAssert.Contains(bar, root.View.Subviews.ToArray(),
- "Undo should put the MenuBar back on the view again");
+ ///
+ public void Dispose( )
+ {
+ Bar.Dispose( );
+ }
}
}
\ No newline at end of file
diff --git a/tests/MouseManagerTests.cs b/tests/MouseManagerTests.cs
deleted file mode 100644
index e9303386..00000000
--- a/tests/MouseManagerTests.cs
+++ /dev/null
@@ -1,370 +0,0 @@
-using System;
-using System.Linq;
-using Terminal.Gui;
-using TerminalGuiDesigner;
-using TerminalGuiDesigner.Operations;
-using TerminalGuiDesigner.UI;
-
-namespace UnitTests;
-
-internal class MouseManagerTests : Tests
-{
- [Test]
- public void TestDragLabel()
- {
- var d = Get10By10View();
-
- var lbl = new Label(0, 0, "Hi there buddy");
- var lblDesign = new Design(d.SourceCode, "mylabel", lbl);
- lbl.Data = lblDesign;
- d.View.Add(lbl);
-
- var mgr = new MouseManager();
-
- // we haven't done anything yet
- ClassicAssert.AreEqual(0, OperationManager.Instance.UndoStackSize);
- ClassicAssert.AreEqual(0, lbl.Bounds.Y);
-
- // user presses down over the control
- var e = new MouseEvent
- {
- X = 1,
- Y = 0,
- Flags = MouseFlags.Button1Pressed,
- };
-
- mgr.HandleMouse(e, d);
-
- ClassicAssert.AreEqual(0, lbl.Bounds.Y);
-
- // we still haven't committed to anything
- ClassicAssert.AreEqual(0, OperationManager.Instance.UndoStackSize);
-
- // user pulled view down but still has mouse down
- e = new MouseEvent
- {
- X = 1,
- Y = 1,
- Flags = MouseFlags.Button1Pressed,
- };
- mgr.HandleMouse(e, d);
-
- ClassicAssert.AreEqual((Pos)1, lbl.Y);
-
- // we still haven't committed to anything
- ClassicAssert.AreEqual(0, OperationManager.Instance.UndoStackSize);
-
- // user releases mouse (in place)
- e = new MouseEvent
- {
- X = 1,
- Y = 1,
- };
- mgr.HandleMouse(e, d);
-
- ClassicAssert.AreEqual((Pos)1, lbl.Y);
-
- // we have now committed the drag so could undo
- ClassicAssert.AreEqual(1, OperationManager.Instance.UndoStackSize);
- }
-
- [TestCase(typeof(Button))]
- [TestCase(typeof(TabView))]
- [TestCase(typeof(TableView))]
- [TestCase(typeof(View))]
- public void TestDragResizeView(Type t)
- {
- var d = Get10By10View();
-
- var view = ViewFactory.Create(t);
- view.Width = 8;
- view.Height = 1;
-
- var design = new Design(d.SourceCode, "myView", view);
- view.Data = design;
- d.View.Add(view);
-
- ClassicAssert.AreEqual(8, view.Bounds.Width);
- var mgr = new MouseManager();
-
- // we haven't done anything yet
- ClassicAssert.AreEqual(0, OperationManager.Instance.UndoStackSize);
- ClassicAssert.AreEqual(0, view.Bounds.X);
- ClassicAssert.AreEqual(0, view.Bounds.Y);
- ClassicAssert.AreEqual(8, view.Bounds.Width);
- ClassicAssert.AreEqual(1, view.Bounds.Height);
-
- // user presses down in the lower right of control
- var e = new MouseEvent
- {
- X = 6,
- Y = 0,
- Flags = MouseFlags.Button1Pressed,
- };
-
- mgr.HandleMouse(e, d);
-
- ClassicAssert.AreEqual(0, view.Bounds.Y);
-
- // we still haven't committed to anything
- ClassicAssert.AreEqual(0, OperationManager.Instance.UndoStackSize);
-
- // user pulled view size +1 width and +1 height
- e = new MouseEvent
- {
- X = 9,
- Y = 0,
- Flags = MouseFlags.Button1Pressed,
- };
- mgr.HandleMouse(e, d);
-
- ClassicAssert.AreEqual(0, view.Bounds.X);
- ClassicAssert.AreEqual(0, view.Bounds.Y);
- ClassicAssert.AreEqual(10, view.Bounds.Width, "Expected resize to increase Width when dragging");
- ClassicAssert.AreEqual(1, view.Bounds.Height, "Expected resize of button to ignore Y component");
-
- // we still haven't committed to anything
- ClassicAssert.AreEqual(0, OperationManager.Instance.UndoStackSize);
-
- // user releases mouse (in place)
- e = new MouseEvent
- {
- X = 9,
- Y = 0,
- };
- mgr.HandleMouse(e, d);
-
- ClassicAssert.AreEqual(0, view.Bounds.X);
- ClassicAssert.AreEqual(0, view.Bounds.Y);
- ClassicAssert.AreEqual(10, view.Bounds.Width, "Expected resize to increase Width when dragging");
- ClassicAssert.AreEqual(1, view.Bounds.Height);
-
- // we have now committed the drag so could undo
- ClassicAssert.AreEqual(1, OperationManager.Instance.UndoStackSize);
- }
-
- [TestCase(typeof(View))]
- public void TestDragResizeView_CannotResize_DimFill(Type t)
- {
- var d = Get10By10View();
-
- var view = ViewFactory.Create(t);
- view.Width = Dim.Fill();
- view.Height = 1;
-
- var design = new Design(d.SourceCode, "myView", view);
- view.Data = design;
- d.View.Add(view);
-
- var mgr = new MouseManager();
-
- // we haven't done anything yet
- ClassicAssert.AreEqual(0, OperationManager.Instance.UndoStackSize);
- ClassicAssert.AreEqual(0, view.Bounds.X);
- ClassicAssert.AreEqual(0, view.Bounds.Y);
- ClassicAssert.AreEqual(10, view.Bounds.Width);
- ClassicAssert.AreEqual(1, view.Bounds.Height);
-
- // user presses down in the lower right of control
- var e = new MouseEvent
- {
- X = 9,
- Y = 0,
- Flags = MouseFlags.Button1Pressed,
- };
-
- mgr.HandleMouse(e, d);
-
- ClassicAssert.AreEqual(0, view.Bounds.Y);
-
- // we still haven't committed to anything
- ClassicAssert.AreEqual(0, OperationManager.Instance.UndoStackSize);
-
- // user pulled view size down and left
- e = new MouseEvent
- {
- X = 6,
- Y = 3,
- Flags = MouseFlags.Button1Pressed,
- };
- mgr.HandleMouse(e, d);
-
- ClassicAssert.AreEqual(0, view.Bounds.X);
- ClassicAssert.AreEqual(0, view.Bounds.Y);
- ClassicAssert.AreEqual(10, view.Bounds.Width, "Expected Width to remain constant because it is Dim.Fill()");
- ClassicAssert.AreEqual(4, view.Bounds.Height, "Expected resize to update Y component");
- ClassicAssert.AreEqual(Dim.Fill(),view.Width);
- ClassicAssert.AreEqual(Dim.Sized(4), view.Height);
-
- // we still haven't committed to anything
- ClassicAssert.AreEqual(0, OperationManager.Instance.UndoStackSize);
-
- // user releases mouse (in place)
- e = new MouseEvent
- {
- X = 6,
- Y = 3,
- };
- mgr.HandleMouse(e, d);
-
- ClassicAssert.AreEqual(0, view.Bounds.X);
- ClassicAssert.AreEqual(0, view.Bounds.Y);
- ClassicAssert.AreEqual(10, view.Bounds.Width, "Expected Width to remain constant because it is Dim.Fill()");
- ClassicAssert.AreEqual(4, view.Bounds.Height, "Expected resize to update Y component");
- ClassicAssert.AreEqual(Dim.Fill(), view.Width);
- ClassicAssert.AreEqual(Dim.Sized(4), view.Height);
-
- // we have now committed the drag so could undo
- ClassicAssert.AreEqual(1, OperationManager.Instance.UndoStackSize);
-
- OperationManager.Instance.Undo();
-
- // Should reset us to the initial state
- ClassicAssert.AreEqual(0, OperationManager.Instance.UndoStackSize);
- ClassicAssert.AreEqual(0, view.Bounds.X);
- ClassicAssert.AreEqual(0, view.Bounds.Y);
- ClassicAssert.AreEqual(10, view.Bounds.Width);
- ClassicAssert.AreEqual(1, view.Bounds.Height);
- ClassicAssert.AreEqual(Dim.Fill(), view.Width);
- ClassicAssert.AreEqual(Dim.Sized(1), view.Height);
-
- }
-
- [TestCase(0,0)]
- [TestCase(3, 3)]
- public void TestDragResizeView_CannotResize_1By1View(int locationOfViewX, int locationOfViewY)
- {
- var d = Get10By10View();
-
- var view = ViewFactory.Create(typeof(View));
- view.Width = Dim.Fill();
- view.X = locationOfViewX;
- view.Y = locationOfViewY;
- view.Width = 1;
- view.Height = 1;
-
- var design = new Design(d.SourceCode, "myView", view);
- view.Data = design;
- d.View.Add(view);
-
- d.View.LayoutSubviews();
-
- var mgr = new MouseManager();
-
- // we haven't done anything yet
- ClassicAssert.AreEqual(0, OperationManager.Instance.UndoStackSize);
- ClassicAssert.AreEqual(locationOfViewX, view.Frame.X);
- ClassicAssert.AreEqual(locationOfViewY, view.Frame.Y);
- ClassicAssert.AreEqual(1, view.Bounds.Width);
- ClassicAssert.AreEqual(1, view.Bounds.Height);
-
- // user presses down in the lower right of control
- var e = new MouseEvent
- {
- X = locationOfViewX,
- Y = locationOfViewY,
- Flags = MouseFlags.Button1Pressed,
- };
-
- var hit = view.HitTest(e, out var isBorder, out var isLowerRight);
- ClassicAssert.AreSame(view, hit);
- ClassicAssert.IsTrue(isBorder);
- ClassicAssert.IsFalse(isLowerRight,"Upper left should never be considered lower right even if view is 1x1");
-
- mgr.HandleMouse(e, d);
-
- ClassicAssert.AreEqual(locationOfViewY, view.Frame.Y);
-
- // we still haven't committed to anything
- ClassicAssert.AreEqual(0, OperationManager.Instance.UndoStackSize);
-
- // user pulled view size down and left
- e = new MouseEvent
- {
- X = 6,
- Y = 3,
- Flags = MouseFlags.Button1Pressed,
- };
- mgr.HandleMouse(e, d);
-
- ClassicAssert.AreEqual(1, view.Bounds.Width);
- ClassicAssert.AreEqual(1, view.Bounds.Height);
- }
-
- [TestCase(1, 1, 4, 6, new[] { 0, 2 })] // drag from 1,1 to 4,6 and expect labels 0 and 2 to be selected
- [TestCase(1, 1, 10, 10, new[] { 0, 1, 2 })] // drag over all
- public void TestDragSelectionBox(int xStart, int yStart, int xEnd, int yEnd, int[] expectSelected)
- {
- var d = Get10By10View();
-
- /*
- Hi
- Hi
- Hi
- */
-
- var lbl1 = new Label(2, 1, "Hi");
- var lbl2 = new Label(4, 2, "Hi");
- var lbl3 = new Label(2, 3, "Hi");
-
- var lbl1Design = new Design(d.SourceCode, "lbl1", lbl1);
- var lbl2Design = new Design(d.SourceCode, "lbl2", lbl2);
- var lbl3Design = new Design(d.SourceCode, "lbl3", lbl3);
-
- lbl1.Data = lbl1Design;
- lbl2.Data = lbl2Design;
- lbl3.Data = lbl3Design;
-
- var labels = new[] { lbl1Design, lbl2Design, lbl3Design };
-
- d.View.Add(lbl1);
- d.View.Add(lbl2);
- d.View.Add(lbl3);
-
- var selection = SelectionManager.Instance;
- selection.Clear();
- var mgr = new MouseManager();
-
- // user presses down
- var e = new MouseEvent
- {
- X = xStart,
- Y = yStart,
- Flags = MouseFlags.Button1Pressed,
- };
-
- mgr.HandleMouse(e, d);
-
- // user pulled selection box to destination
- e = new MouseEvent
- {
- X = xEnd,
- Y = yEnd,
- Flags = MouseFlags.Button1Pressed,
- };
- mgr.HandleMouse(e, d);
-
- // user releases mouse (in place)
- e = new MouseEvent
- {
- X = xEnd,
- Y = yEnd,
- };
- mgr.HandleMouse(e, d);
-
- // do not expect dragging selection box to change anything
- // or be undoable
- ClassicAssert.AreEqual(0, OperationManager.Instance.UndoStackSize);
-
- // for each selectable thing
- for (int i = 0; i < labels.Length; i++)
- {
- // its selection status should match the expectation
- // passed in the test case
- ClassicAssert.AreEqual(
- expectSelected.Contains(i),
- selection.Selected.ToList().Contains(labels[i]),
- $"Expectation wrong for label index {i} (indexes are 0 based)");
- }
- }
-}
diff --git a/tests/PosTests.cs b/tests/PosTests.cs
index 82094333..c1ae6461 100644
--- a/tests/PosTests.cs
+++ b/tests/PosTests.cs
@@ -1,9 +1,3 @@
-using System.IO;
-using System.Linq;
-using TerminalGuiDesigner.FromCode;
-using TerminalGuiDesigner.Operations;
-using TerminalGuiDesigner.ToCode;
-
namespace UnitTests;
[TestFixture]
@@ -196,7 +190,7 @@ private static IEnumerable IsAnchorEnd_Cases
};
}
}
-
+
private static IEnumerable IsCenter_Cases
{
get
@@ -336,14 +330,14 @@ private static IEnumerable IsRelative_WithOutParams_Cases
[TestCaseSource( nameof( GetCode_Cases ) )]
public void GetCode( Pos testPos, string expectedCodeString, Design d, View v )
{
- Assert.That( testPos.ToCode( new( ) { d } ), Is.EqualTo( expectedCodeString ) );
+ Assert.That( testPos.ToCode( [d] ), Is.EqualTo( expectedCodeString ) );
}
[Test]
[TestCaseSource( nameof( GetPosType_OutputsCorrectOffset_Cases ) )]
public bool GetPosType_OutputsCorrectOffset( Pos testValue, int expectedOffset, Design? d )
{
- List knownDesigns = new( );
+ List knownDesigns = [];
if ( d is not null )
{
knownDesigns.Add( d );
@@ -358,7 +352,7 @@ public bool GetPosType_OutputsCorrectOffset( Pos testValue, int expectedOffset,
[TestCaseSource( nameof( GetPosType_OutputsCorrectType_Cases ) )]
public bool GetPosType_OutputsCorrectType( Pos testValue, PosType expectedPosType, Design? d )
{
- List knownDesigns = new( );
+ List knownDesigns = [];
if ( d is not null )
{
knownDesigns.Add( d );
@@ -373,7 +367,7 @@ public bool GetPosType_OutputsCorrectType( Pos testValue, PosType expectedPosTyp
[TestCaseSource( nameof( GetPosType_OutputsCorrectValue_Cases ) )]
public bool GetPosType_OutputsCorrectValue( Pos testValue, float expectedValue, Design? d )
{
- List knownDesigns = new( );
+ List knownDesigns = [];
if ( d is not null )
{
knownDesigns.Add( d );
@@ -411,6 +405,7 @@ public bool IsAnchorEnd( Pos? testValue )
{
Assert.Ignore( "BUG: Null returns true for this, when it shouldn't" );
}
+
return testValue.IsAnchorEnd( );
}
@@ -433,6 +428,7 @@ public bool IsCenter( Pos? testValue )
{
Assert.Warn( "BUG: Null returns true for this, when it shouldn't" );
}
+
return testValue.IsCenter( );
}
@@ -472,7 +468,7 @@ public bool IsRelative( Pos? testValue )
[NonParallelizable]
public bool IsRelative_WithOutParams( Pos testValue, Design? expectedOutDesign, Side expectedOutSide )
{
- List knownDesigns = new( );
+ List knownDesigns = [];
if ( expectedOutDesign is not null )
{
knownDesigns.Add( expectedOutDesign );
@@ -499,7 +495,7 @@ public void CreatePosRelative( [Values] Side side, [Values( -10, -5, 0, 5, 10 )]
Assert.Multiple( ( ) =>
{
Assert.That( p.IsRelative );
- Assert.That( p.IsRelative( new List { d }, out Design outDesign, out Side outSide ) );
+ Assert.That( p.IsRelative( new List { d }, out Design? outDesign, out Side outSide ) );
Assert.That( outDesign, Is.SameAs( d ) );
Assert.That( outSide, Is.EqualTo( side ) );
} );
@@ -524,121 +520,43 @@ public void NullPos( )
}
[Test]
- [Ignore( "Code generation is tested in other tests here" )]
- public void TestRoundTrip_PosAnchorEnd( )
- {
- var viewToCode = new ViewToCode( );
-
- var file = new FileInfo( "TestRoundTrip_PosAnchorEnd.cs" );
- var designOut = viewToCode.GenerateNewView( file, "YourNamespace", typeof( Window ) );
-
- designOut.View.Width = 100;
- designOut.View.Height = 100;
-
- var lbl = ViewFactory.Create( );
- lbl.X = Pos.AnchorEnd( 1 );
- lbl.Y = Pos.AnchorEnd( 4 ); // length of "Heya"
-
- new AddViewOperation( lbl, designOut, "label1" ).Do( );
-
- viewToCode.GenerateDesignerCs( designOut, typeof( Window ) );
-
- var codeToView = new CodeToView( designOut.SourceCode );
- var designBackIn = codeToView.CreateInstance( );
-
- var lblIn = designBackIn.View.GetActualSubviews( ).OfType( ).Single( );
-
- lblIn.X.GetPosType( designBackIn.GetAllDesigns( ).ToList( ), out var backInType, out var backInValue, out _, out _, out var backInOffset );
- Assert.That( backInOffset, Is.Zero );
- Assert.That( backInType, Is.EqualTo( PosType.AnchorEnd ) );
- Assert.That( backInValue, Is.EqualTo( 1 ) );
-
- lblIn.Y.GetPosType( designBackIn.GetAllDesigns( ).ToList( ), out backInType, out backInValue, out _, out _, out backInOffset );
- Assert.That( backInOffset, Is.Zero );
- Assert.That( backInType, Is.EqualTo( PosType.AnchorEnd ) );
- Assert.That( backInValue, Is.EqualTo( 14 ) );
- }
-
- [Test]
- [Ignore( "Code generation is tested in other tests here" )]
- public void TestRoundTrip_PosAnchorEnd_WithOffset( )
- {
- var viewToCode = new ViewToCode( );
-
- var file = new FileInfo( "TestRoundTrip_PosAnchorEnd.cs" );
- var designOut = viewToCode.GenerateNewView( file, "YourNamespace", typeof( Window ) );
-
- designOut.View.Width = 100;
- designOut.View.Height = 100;
-
- var lbl = ViewFactory.Create( );
- lbl.X = Pos.AnchorEnd( 1 ) + 5;
- lbl.Y = Pos.AnchorEnd( 4 ) - 3; // length of "Heya"
-
- new AddViewOperation( lbl, designOut, "label1" ).Do( );
-
- viewToCode.GenerateDesignerCs( designOut, typeof( Window ) );
-
- var codeToView = new CodeToView( designOut.SourceCode );
- var designBackIn = codeToView.CreateInstance( );
-
- var lblIn = designBackIn.View.GetActualSubviews( ).OfType( ).Single( );
-
- lblIn.X.GetPosType( designBackIn.GetAllDesigns( ).ToList( ), out var backInType, out var backInValue, out _, out _, out var backInOffset );
- Assert.That( backInOffset, Is.EqualTo( 5 ) );
- Assert.That( backInType, Is.EqualTo( PosType.AnchorEnd ) );
- Assert.That( backInValue, Is.EqualTo( 1 ) );
-
- lblIn.Y.GetPosType( designBackIn.GetAllDesigns( ).ToList( ), out backInType, out backInValue, out _, out _, out backInOffset );
- Assert.That( backInOffset, Is.EqualTo( -3 ) );
- Assert.That( backInType, Is.EqualTo( PosType.AnchorEnd ) );
- Assert.That( backInValue, Is.EqualTo( 4 ) );
- }
-
- [Test]
- [TestCase( Side.Left, -2, "X" )]
- [TestCase( Side.Right, 1, "X" )]
- [TestCase( Side.Top, -2, "Y" )]
- [TestCase( Side.Bottom, 5, "Y" )]
- [Ignore( "Code generation is tested in other tests here" )]
- public void TestRoundTrip_PosRelative( Side side, int offset, string property )
+ public void TestRoundTrip_PosRelative( [Values] Side side, [Values( -2, 1, 5 )] int offset, [Values( "X", "Y" )] string property )
{
- var viewToCode = new ViewToCode( );
+ ViewToCode viewToCode = new( );
- var file = new FileInfo( "TestRoundTrip_PosRelative.cs" );
- var designOut = viewToCode.GenerateNewView( file, "YourNamespace", typeof( Window ) );
+ FileInfo file = new( $"{nameof( TestRoundTrip_PosRelative )}.cs" );
+ Design designOut = viewToCode.GenerateNewView( file, "YourNamespace", typeof( Window ) );
designOut.View.Width = 100;
designOut.View.Height = 100;
- var lbl = ViewFactory.Create( );
+ using Label lbl = ViewFactory.Create( );
lbl.X = 50;
lbl.Y = 50;
- var btn = ViewFactory.Create( typeof( Button ) );
+ using Button btn = ViewFactory.Create( );
- new AddViewOperation( lbl, designOut, "label1" ).Do( );
- new AddViewOperation( btn, designOut, "btn" ).Do( );
+ Assert.That( ( ) => new AddViewOperation( lbl, designOut, "label1" ).Do( ), Throws.Nothing );
+ Assert.That( ( ) => new AddViewOperation( btn, designOut, "btn" ).Do( ), Throws.Nothing );
- if ( property == "X" )
- {
- btn.X = PosExtensions.CreatePosRelative( (Design)lbl.Data, side, offset );
- }
- else if ( property == "Y" )
- {
- btn.Y = PosExtensions.CreatePosRelative( (Design)lbl.Data, side, offset );
- }
- else
+ switch ( property )
{
- throw new ArgumentException( $"Unknown property for test '{property}'" );
+ case "X":
+ btn.X = ( (Design)lbl.Data ).CreatePosRelative( side, offset );
+ break;
+ case "Y":
+ btn.Y = ( (Design)lbl.Data ).CreatePosRelative( side, offset );
+ break;
+ default:
+ throw new ArgumentException( $"Unknown property for test '{property}'" );
}
viewToCode.GenerateDesignerCs( designOut, typeof( Window ) );
- var codeToView = new CodeToView( designOut.SourceCode );
- var designBackIn = codeToView.CreateInstance( );
+ CodeToView codeToView = new( designOut.SourceCode );
+ Design designBackIn = codeToView.CreateInstance( );
- var btnIn = designBackIn.View.GetActualSubviews( ).OfType( ).Single( );
+ using Button btnIn = designBackIn.View.GetActualSubviews( ).OfType( ).Single( );
PosType backInType;
Design? backInRelativeTo;
@@ -663,4 +581,4 @@ public void TestRoundTrip_PosRelative( Side side, int offset, string property )
Assert.That( backInRelativeTo!.View, Is.Not.Null.And.InstanceOf( ) );
} );
}
-}
+}
\ No newline at end of file
diff --git a/tests/PropertyTests.cs b/tests/PropertyTests.cs
index 61b0cc36..03fbd835 100644
--- a/tests/PropertyTests.cs
+++ b/tests/PropertyTests.cs
@@ -1,136 +1,129 @@
using System.CodeDom;
-using System.CodeDom.Compiler;
-using System.IO;
-using System.Linq;
using System.Text;
-using Microsoft.CSharp;
-using Terminal.Gui;
-using TerminalGuiDesigner;
-using TerminalGuiDesigner.ToCode;
-using Attribute = Terminal.Gui.Attribute;
+using TerminalGuiAttribute = Terminal.Gui.Attribute;
+using TerminalGuiConfigurationManager = Terminal.Gui.ConfigurationManager;
namespace UnitTests;
+[TestFixture]
+[TestOf( typeof( Property ) )]
+[Category( "Core" )]
internal class PropertyTests : Tests
{
[Test]
- public void TestPropertyOfType_Pos()
+ public void Changing_LineViewOrientation( )
{
- var d = new Design(new SourceCodeFile(nameof(this.TestPropertyOfType_Pos) + ".cs"), "FFF", new Label());
- var xProp = d.GetDesignableProperties().Single(p => p.PropertyInfo.Name.Equals(nameof(View.X)));
+ Design v = Get10By10View( );
+ using LineView lv = ViewFactory.Create( );
+ Design d = new( v.SourceCode, "lv", lv );
- xProp.SetValue(Pos.Center());
+ v.View.Add( lv );
+ lv.IsInitialized = true;
- var rhs = (CodeSnippetExpression)xProp.GetRhs();
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( lv.Orientation, Is.EqualTo( Orientation.Horizontal ) );
+ Assert.That( lv.LineRune, Is.EqualTo( new Rune( '─' ) ) );
+ } );
- // The code generated for a Property of Type Pos should be the function call
- ClassicAssert.AreEqual(rhs.Value, "Pos.Center()");
- }
+ Property? prop = d.GetDesignableProperty( nameof( LineView.Orientation ) );
- [Test]
- public void TestPropertyOfType_Size()
- {
- var d = new Design(new SourceCodeFile(nameof(this.TestPropertyOfType_Size) + ".cs"), "FFF", new ScrollView());
- var xProp = d.GetDesignableProperties().Single(p => p.PropertyInfo.Name.Equals(nameof(View.X)));
+ Assert.That( prop, Is.Not.Null );
+ prop?.SetValue( Orientation.Vertical );
+ Assert.That( lv.LineRune, Is.EqualTo( TerminalGuiConfigurationManager.Glyphs.VLine ) );
- xProp.SetValue(Pos.Center());
+ // now try with a dim fill
+ lv.Height = Dim.Fill( );
+ lv.Width = 1;
- var rhs = (CodeSnippetExpression)xProp.GetRhs();
+ prop?.SetValue( Orientation.Horizontal );
- // The code generated for a Property of Type Pos should be the function call
- ClassicAssert.AreEqual(rhs.Value, "Pos.Center()");
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( lv.Orientation, Is.EqualTo( Orientation.Horizontal ) );
+ Assert.That( lv.LineRune, Is.EqualTo( TerminalGuiConfigurationManager.Glyphs.HLine ) );
+ Assert.That( lv.Width, Is.EqualTo( Dim.Fill( ) ) );
+ Assert.That( lv.Height, Is.EqualTo( Dim.Sized( 1 ) ) );
+ } );
}
- [Test]
- public void TestPropertyOfType_Attribute()
+ [Test( ExpectedResult = "new Terminal.Gui.Attribute(Color.BrightMagenta,Color.Blue)" )]
+ public string PropertyOfType_Attribute( )
{
- var d = new Design(new SourceCodeFile(nameof(this.TestPropertyOfType_Attribute) + ".cs"), "FFF", new GraphView());
- var colorProp = d.GetDesignableProperties().Single(p => p.PropertyInfo.Name.Equals(nameof(GraphView.GraphColor)));
+ using GraphView graphView = new( );
+ Design d = new( new( $"{nameof( PropertyOfType_Attribute )}.cs" ), "FFF", graphView );
+ Property colorProp = d.GetDesignableProperties( ).Single( static p => p.PropertyInfo.Name.Equals( nameof( GraphView.GraphColor ) ) );
- colorProp.SetValue(null);
+ colorProp.SetValue( null );
- var rhs = (CodeSnippetExpression)colorProp.GetRhs();
- ClassicAssert.AreEqual(rhs.Value, "null");
+ CodeSnippetExpression? rhs = colorProp.GetRhs( ) as CodeSnippetExpression;
+ Assert.That( rhs, Is.Not.Null.And.InstanceOf( ) );
+ Assert.That( rhs!.Value, Is.Not.Null.And.EqualTo( "null" ) );
- colorProp.SetValue(new Attribute(Color.BrightMagenta, Color.Blue));
+ colorProp.SetValue( new TerminalGuiAttribute( Color.BrightMagenta, Color.Blue ) );
- rhs = (CodeSnippetExpression)colorProp.GetRhs();
- ClassicAssert.AreEqual(rhs.Value, "new Terminal.Gui.Attribute(Color.BrightMagenta,Color.Blue)");
+ rhs = (CodeSnippetExpression)colorProp.GetRhs( );
+ return rhs.Value;
}
[Test]
- public void TestPropertyOfType_PointF()
+ public void PropertyOfType_PointF( [Values( 4.5f, 10.1f )] float x, [Values( 4.5f, 10.1f )] float y )
{
- var d = new Design(new SourceCodeFile(nameof(this.TestPropertyOfType_PointF) + ".cs"), "FFF", new GraphView());
- var pointProp = d.GetDesignableProperties().Single(p => p.PropertyInfo.Name.Equals(nameof(GraphView.ScrollOffset)));
-
- pointProp.SetValue(new PointF(4.5f, 4.1f));
+ using GraphView graphView = new( );
+ Design d = new( new( $"{nameof( PropertyOfType_PointF )}.cs" ), "FFF", graphView );
+ Property pointProp = d.GetDesignableProperties( ).Single( static p => p.PropertyInfo.Name.Equals( nameof( GraphView.ScrollOffset ) ) );
- var rhs = (CodeObjectCreateExpression)pointProp.GetRhs();
+ PointF pointF = new( x, y );
+ pointProp.SetValue( pointF );
// The code generated should be a new PointF
- ClassicAssert.AreEqual(rhs.Parameters.Count, 2);
+ CodeObjectCreateExpression? rhs = pointProp.GetRhs( ) as CodeObjectCreateExpression;
+
+ Assert.That( rhs, Is.Not.Null.And.InstanceOf( ) );
+ Assert.That( rhs!.Parameters, Is.Not.Null.And.InstanceOf( ) );
+ Assert.That( rhs.Parameters, Has.Count.EqualTo( 2 ) );
}
- [Test]
- public void TestPropertyOfType_Rune()
+ [Test( ExpectedResult = "Pos.Center()" )]
+ [Category( "Code Generation" )]
+ public string PropertyOfType_Pos( )
{
- var viewToCode = new ViewToCode();
-
- var file = new FileInfo("TestPropertyOfType_Rune.cs");
- var lv = new LineView();
- var d = new Design(new SourceCodeFile(file), "lv", lv);
- var prop = d.GetDesignableProperties().Single(p => p.PropertyInfo.Name.Equals("LineRune"));
-
- prop.SetValue('F');
-
- ClassicAssert.AreEqual(new Rune('F'), lv.LineRune);
+ using Label label = new( );
+ Design d = new( new( $"{nameof( PropertyOfType_Pos )}.cs" ), "FFF", label );
+ Property xProp = d.GetDesignableProperties( ).Single( static p => p.PropertyInfo.Name.Equals( nameof( View.X ) ) );
- var code = ExpressionToCode(prop.GetRhs());
+ xProp.SetValue( Pos.Center( ) );
- ClassicAssert.AreEqual("new System.Text.Rune('F')", code);
+ return ( (CodeSnippetExpression)xProp.GetRhs( ) ).Value;
}
[Test]
- public void TestChanging_LineViewOrientation()
+ public void PropertyOfType_Rune( [Values( 'a', 'A', 'f', 'F' )] char runeCharacter )
{
- var v = Get10By10View();
- var lv = (LineView)ViewFactory.Create(typeof(LineView));
- var d = new Design(v.SourceCode, "lv", lv);
+ FileInfo file = new( $"{nameof( PropertyOfType_Rune )}_{runeCharacter}.cs" );
+ using LineView lv = new( );
+ Design d = new( new( file ), "lv", lv );
+ Property prop = d.GetDesignableProperties( ).Single( static p => p.PropertyInfo.Name.Equals( "LineRune" ) );
- v.View.Add(lv);
- lv.IsInitialized = true;
+ prop.SetValue( runeCharacter );
- ClassicAssert.AreEqual(Orientation.Horizontal, lv.Orientation);
- ClassicAssert.AreEqual(new Rune('─'), lv.LineRune);
- var prop = d.GetDesignableProperty(nameof(LineView.Orientation));
+ Assert.That( lv.LineRune, Is.EqualTo( new Rune( runeCharacter ) ) );
- ClassicAssert.IsNotNull(prop);
- prop?.SetValue(Orientation.Vertical);
- ClassicAssert.AreEqual(ConfigurationManager.Glyphs.VLine, lv.LineRune);
+ string code = Helpers.ExpressionToCode( prop.GetRhs( ) );
- // now try with a dim fill
- lv.Height = Dim.Fill();
- lv.Width = 1;
-
- prop?.SetValue(Orientation.Horizontal);
- ClassicAssert.AreEqual(Orientation.Horizontal, lv.Orientation);
- ClassicAssert.AreEqual(ConfigurationManager.Glyphs.HLine, lv.LineRune);
- ClassicAssert.AreEqual(Dim.Fill(), lv.Width);
- ClassicAssert.AreEqual(Dim.Sized(1), lv.Height);
+ Assert.That( code, Is.EqualTo( $"new System.Text.Rune('{runeCharacter}')" ) );
}
- public static string ExpressionToCode(CodeExpression expression)
+ [Test( ExpectedResult = "Pos.Center()" )]
+ [Category( "Code Generation" )]
+ public string PropertyOfType_Size( )
{
- CSharpCodeProvider provider = new ();
+ using ScrollView scrollView = new( );
+ Design d = new( new( $"{nameof( PropertyOfType_Size )}.cs" ), "FFF", scrollView );
+ Property xProp = d.GetDesignableProperties( ).Single( static p => p.PropertyInfo.Name.Equals( nameof( View.X ) ) );
- using (var sw = new StringWriter())
- {
- IndentedTextWriter tw = new IndentedTextWriter(sw, " ");
- provider.GenerateCodeFromExpression(expression, tw, new CodeGeneratorOptions());
- tw.Close();
+ xProp.SetValue( Pos.Center( ) );
- return sw.GetStringBuilder().ToString();
- }
+ return ( (CodeSnippetExpression)xProp.GetRhs( ) ).Value;
}
-}
+}
\ No newline at end of file
diff --git a/tests/RadioGroupTests.cs b/tests/RadioGroupTests.cs
index 8ae1986d..61666fd0 100644
--- a/tests/RadioGroupTests.cs
+++ b/tests/RadioGroupTests.cs
@@ -1,47 +1,46 @@
-using Terminal.Gui;
-
namespace UnitTests;
-class RadioGroupTests : Tests
+[TestFixture]
+[TestOf( typeof( OperationManager ) )]
+[TestOf( typeof( CodeToView ) )]
+[TestOf( typeof( ViewToCode ) )]
+[Category( "Code Generation" )]
+internal class RadioGroupTests : Tests
{
[Test]
- public void TestRoundTrip_PreserveRadioGroups()
+ public void RoundTrip_PreserveRadioGroups( )
{
+ var rgIn = RoundTrip( static ( _, _ ) => { }, out _ );
- var rgIn = RoundTrip((_, _) => { }, out _);
-
- ClassicAssert.AreEqual(2, rgIn.RadioLabels.Length);
+ Assert.That( rgIn.RadioLabels, Has.Length.EqualTo( 2 ) );
- ClassicAssert.AreEqual("Option 1", rgIn.RadioLabels[0]);
- ClassicAssert.AreEqual("Option 2", rgIn.RadioLabels[1]);
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( rgIn.RadioLabels[ 0 ], Is.EqualTo( "Option 1" ) );
+ Assert.That( rgIn.RadioLabels[ 1 ], Is.EqualTo( "Option 2" ) );
+ } );
}
[Test]
- public void TestRoundTrip_PreserveRadioGroups_Custom()
+ public void RoundTrip_PreserveRadioGroups_Custom( )
{
+ var rgIn = RoundTrip( static ( _, r ) => { r.RadioLabels = ["Fish", "Cat", "Balloon"]; }, out _ );
- var rgIn = RoundTrip(
- (_, r) =>
- {
- r.RadioLabels = new string[] { "Fish", "Cat", "Balloon" };
- }, out _);
-
- ClassicAssert.AreEqual(3, rgIn.RadioLabels.Length);
+ Assert.That( rgIn.RadioLabels, Has.Length.EqualTo( 3 ) );
- ClassicAssert.AreEqual("Fish", rgIn.RadioLabels[0]);
- ClassicAssert.AreEqual("Cat", rgIn.RadioLabels[1]);
- ClassicAssert.AreEqual("Balloon", rgIn.RadioLabels[2]);
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( rgIn.RadioLabels[ 0 ], Is.EqualTo( "Fish" ) );
+ Assert.That( rgIn.RadioLabels[ 1 ], Is.EqualTo( "Cat" ) );
+ Assert.That( rgIn.RadioLabels[ 2 ], Is.EqualTo( "Balloon" ) );
+ } );
}
[Test]
- public void TestRoundTrip_PreserveRadioGroups_Empty()
+ public void RoundTrip_PreserveRadioGroups_Empty( )
{
- var rgIn = RoundTrip(
- (_, r) =>
- {
- r.RadioLabels = new string[] { };
- }, out _);
+ var rgIn = RoundTrip( static ( _, r ) => { r.RadioLabels = []; }, out _ );
- ClassicAssert.IsEmpty(rgIn.RadioLabels);
+ Assert.That( rgIn.RadioLabels, Is.Empty );
}
}
diff --git a/tests/ScrollViewTests.cs b/tests/ScrollViewTests.cs
index 7c278cf8..0575396e 100644
--- a/tests/ScrollViewTests.cs
+++ b/tests/ScrollViewTests.cs
@@ -1,73 +1,70 @@
-using System.Linq;
-using Terminal.Gui;
-using TerminalGuiDesigner;
-using TerminalGuiDesigner.Operations;
-
namespace UnitTests;
-class ScrollViewTests : Tests
+[TestFixture]
+[Category( "Code Generation" )]
+internal class ScrollViewTests : Tests
{
[Test]
- public void TestRoundTrip_PreserveContentSize()
+ public void TestRoundTrip_PreserveContentSize( [Values( 1, 5, 25, 100 )] int width, [Values( 1, 5, 25, 100 )] int height )
{
- var scrollViewIn = RoundTrip(
- (d, s) =>
- s.ContentSize = new Size(10, 5),
- out var scrollViewOut);
+ using ScrollView scrollViewIn = RoundTrip(
+ ( _, s ) => { s.ContentSize = new( width, height ); }, out ScrollView? scrollViewOut );
- ClassicAssert.AreNotSame(scrollViewOut, scrollViewIn);
- ClassicAssert.AreEqual(10, scrollViewIn.ContentSize.Width);
- ClassicAssert.AreEqual(5, scrollViewIn.ContentSize.Height);
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( scrollViewIn, Is.Not.SameAs( scrollViewOut ) );
+ Assert.That( scrollViewIn.ContentSize.Width, Is.EqualTo( width ) );
+ Assert.That( scrollViewIn.ContentSize.Height, Is.EqualTo( height ) );
+ } );
}
[Test]
- public void TestRoundTrip_PreserveContentViews()
+ public void TestRoundTrip_PreserveContentViews( [Values( "blarggg" )] string text, [Values( "myLbl" )] string fieldName )
{
- var scrollViewIn = RoundTrip(
- (d, s) =>
- {
- var op = new AddViewOperation(new Label("blarggg"), d, "myLbl");
- op.Do();
- }, out _);
+ using Label lbl = new ( text );
+ using ScrollView scrollViewIn = RoundTrip(
+ ( d, _ ) =>
+ {
+ AddViewOperation op = new ( lbl, d, fieldName );
+ op.Do( );
+ }, out _ );
- var child = scrollViewIn.GetActualSubviews().Single();
- ClassicAssert.IsInstanceOf(child);
- ClassicAssert.IsInstanceOf(child.Data);
+ using View child = scrollViewIn.GetActualSubviews( ).Single( );
+ Assert.That( child, Is.InstanceOf( ) );
+ Assert.That( child.Data, Is.InstanceOf( ) );
- var lblIn = (Design)child.Data;
- ClassicAssert.AreEqual("myLbl", lblIn.FieldName);
- ClassicAssert.AreEqual("blarggg", lblIn.View.Text);
+ Design? lblIn = (Design)child.Data;
+ Assert.That( lblIn.FieldName, Is.EqualTo( fieldName ) );
+ Assert.That( lblIn.View.Text, Is.EqualTo( text ) );
}
[Test]
- public void TestRoundTrip_ScrollViewInsideTabView_PreserveContentViews()
+ public void TestRoundTrip_ScrollViewInsideTabView_PreserveContentViews( )
{
- var scrollOut = ViewFactory.Create(typeof(ScrollView));
- var buttonOut = ViewFactory.Create(typeof(Button));
+ using ScrollView scrollOut = ViewFactory.Create( );
+ using Button buttonOut = ViewFactory.Create( );
- var tabIn = RoundTrip(
- (d, tab) =>
- {
- // Add a ScrollView to the first Tab
- new AddViewOperation(scrollOut, d, "myTabView")
- .Do();
+ using TabView tabIn = RoundTrip(
+ ( d, _ ) =>
+ {
+ // Add a ScrollView to the first Tab
+ new AddViewOperation( scrollOut, d, "myTabView" ).Do( );
- // Add a Button to the ScrollView
- new AddViewOperation(buttonOut, (Design)scrollOut.Data, "myButton")
- .Do();
- }, out _);
+ // Add a Button to the ScrollView
+ new AddViewOperation( buttonOut, (Design)scrollOut.Data, "myButton" ).Do( );
+ }, out _ );
// The ScrollView should contain the Button
- ClassicAssert.Contains(buttonOut, scrollOut.GetActualSubviews().ToArray());
+ Assert.That( scrollOut.GetActualSubviews( ).ToArray( ), Does.Contain( buttonOut ) );
// The TabView read back in should contain the ScrollView
- var scrollIn = tabIn.Tabs.First()
- .View.GetActualSubviews()
- .OfType()
- .Single();
+ using ScrollView scrollIn = tabIn.Tabs.First( )
+ .View.GetActualSubviews( )
+ .OfType( )
+ .Single( );
- var butttonIn = scrollIn.GetActualSubviews().Single();
- ClassicAssert.IsInstanceOf(butttonIn);
- ClassicAssert.AreNotSame(buttonOut, butttonIn);
+ using View buttonIn = scrollIn.GetActualSubviews( ).Single( );
+ Assert.That( buttonIn, Is.InstanceOf( ) );
+ Assert.That( buttonIn, Is.Not.SameAs( buttonOut ) );
}
-}
+}
\ No newline at end of file
diff --git a/tests/SpinnerViewTests.cs b/tests/SpinnerViewTests.cs
index 22c4d824..5dccc974 100644
--- a/tests/SpinnerViewTests.cs
+++ b/tests/SpinnerViewTests.cs
@@ -1,57 +1,57 @@
-using System;
-using System.Linq;
-using Terminal.Gui;
-using TerminalGuiDesigner;
using static Terminal.Gui.SpinnerStyle;
-namespace UnitTests
+namespace UnitTests;
+
+[TestFixture]
+[Category( "Core" )]
+internal class SpinnerViewTests : Tests
{
- internal class SpinnerViewTests : Tests
+ [Test]
+ public void NewSpinnerAutoSpins()
{
- [Test]
- public void TestNewSpinnerAutoSpins()
- {
- ClassicAssert.Contains(typeof(SpinnerView), ViewFactory.SupportedViewTypes.ToArray());
+ Assume.That( ViewFactory.SupportedViewTypes, Does.Contain( typeof( SpinnerView ) ) );
- ClassicAssert.IsEmpty(Application.MainLoop.Timeouts);
+ Assume.That( Application.MainLoop.Timeouts, Is.Empty );
- var s = (SpinnerView)ViewFactory.Create(typeof(SpinnerView));
-
- ClassicAssert.IsNotEmpty(Application.MainLoop.Timeouts);
- s.Dispose();
-
- ClassicAssert.IsEmpty(Application.MainLoop.Timeouts);
- }
- [Test]
- public void TestNewSpinnerAutoSpins_AfterRoundTrip()
+ using ( SpinnerView s = ViewFactory.Create( ) )
{
- ClassicAssert.Contains(typeof(SpinnerView), ViewFactory.SupportedViewTypes.ToArray());
+ Assert.That( Application.MainLoop.Timeouts, Is.Not.Empty );
+ }
+
+ Assert.That( Application.MainLoop.Timeouts, Is.Empty );
+ }
- ClassicAssert.IsEmpty(Application.MainLoop.Timeouts);
+ [Test]
+ [Category("Code Generation")]
+ public void NewSpinnerAutoSpins_AfterRoundTrip()
+ {
+ Assume.That( ViewFactory.SupportedViewTypes.ToArray(), Does.Contain( typeof(SpinnerView) ) );
- RoundTrip((d,v) =>
- {
+ Assume.That( Application.MainLoop.Timeouts, Is.Empty );
- },out _);
+ using SpinnerView s = RoundTrip( static (_,_) =>
+ {
- // Autospin original and the one that is read back in
- ClassicAssert.AreEqual(2, Application.MainLoop.Timeouts.Count);
- }
+ },out _);
- [Test]
- public void TestNewSpinnerAutoSpins_ChangeStyle()
+ // Auto-spin original and the one that is read back in
+ Assert.That( Application.MainLoop.Timeouts, Has.Count.EqualTo( 2 ) );
+ }
+
+ [Test]
+ [Category("Code Generation")]
+ public void NewSpinnerAutoSpins_ChangeStyle()
+ {
+ using SpinnerView backIn = RoundTrip( static(d, v) =>
{
- var backIn = RoundTrip((d, v) =>
- {
- var prop = d.GetDesignableProperty(nameof(SpinnerView.Style))
- ?? throw new Exception("Property was unexpectedly not designable");
+ var prop = d.GetDesignableProperty(nameof(SpinnerView.Style))
+ ?? throw new ("Property was unexpectedly not designable");
- prop.SetValue(new Triangle());
- ClassicAssert.IsInstanceOf(v.Style);
- }, out _);
+ prop.SetValue(new Triangle());
+ Assert.That(v.Style, Is.InstanceOf( ) );
+ }, out _);
- // Autospin original and the one that is read back in
- ClassicAssert.IsInstanceOf(backIn.Style);
- }
+ // Auto-spin original and the one that is read back in
+ Assert.That( backIn.Style, Is.InstanceOf( ) );
}
-}
+}
\ No newline at end of file
diff --git a/tests/StatusBarTests.cs b/tests/StatusBarTests.cs
index 3256fdea..64c78edf 100644
--- a/tests/StatusBarTests.cs
+++ b/tests/StatusBarTests.cs
@@ -1,24 +1,23 @@
-using Terminal.Gui;
-using TerminalGuiDesigner;
+namespace UnitTests;
-namespace UnitTests
+[TestFixture]
+[Category( "Code Generation" )]
+[Parallelizable(ParallelScope.All)]
+internal class StatusBarTests : Tests
{
- internal class StatusBarTests : Tests
+ [Test]
+ public void ItemsArePreserved( )
{
- [Test]
- public void TestItemsArePreserved()
- {
- Key shortcutBefore = KeyCode.Null;
+ Key shortcutBefore = KeyCode.Null;
- var statusBarIn = RoundTrip((d, v) =>
- {
- ClassicAssert.AreEqual(1, v.Items.Length, $"Expected {nameof(ViewFactory)} to create a placeholder status item in new StatusBars it creates");
- shortcutBefore = v.Items[0].Shortcut;
+ using StatusBar statusBarIn = RoundTrip( ( _, v ) =>
+ {
+ Assert.That( v.Items, Has.Length.EqualTo( 1 ), $"Expected {nameof( ViewFactory )} to create a placeholder status item in new StatusBars it creates" );
+ shortcutBefore = v.Items[ 0 ].Shortcut;
- }, out _);
+ }, out _ );
- ClassicAssert.AreEqual(1, statusBarIn.Items.Length, "Expected reloading StatusBar to create the same number of StatusItems");
- ClassicAssert.AreEqual(shortcutBefore, statusBarIn.Items[0].Shortcut);
- }
+ Assert.That( statusBarIn.Items, Has.Length.EqualTo( 1 ), "Expected reloading StatusBar to create the same number of StatusItems" );
+ Assert.That( statusBarIn.Items[ 0 ].Shortcut, Is.EqualTo( shortcutBefore ) );
}
-}
+}
\ No newline at end of file
diff --git a/tests/StringExtensionTests.cs b/tests/StringExtensionTests.cs
index 44338b09..c0b75b95 100644
--- a/tests/StringExtensionTests.cs
+++ b/tests/StringExtensionTests.cs
@@ -1,36 +1,60 @@
-using TerminalGuiDesigner;
+using System.Diagnostics.CodeAnalysis;
+using StringExtensions = TerminalGuiDesigner.StringExtensions;
namespace UnitTests;
+[TestFixture]
+[TestOf( typeof( StringExtensions ) )]
+[Category( "Core" )]
+[Parallelizable( ParallelScope.All )]
+[SuppressMessage( "Performance", "CA1861:Avoid constant arrays as arguments", Justification = "We don't really care for unit tests..." )]
internal class StringExtensionTests
{
- [TestCase("bob", "bob", new[] {"fish"})]
- [TestCase("fish", "fish2", new[] { "fish" })]
- [TestCase("fish2", "fish3", new[] { "fish1","fish2" })]
- [TestCase(null, "blank", new[] { "fish1", "fish2" })]
- [TestCase(" ", "blank", new[] { "fish1", "fish2" })]
- [TestCase("bob", "bob3", new[] { "bob", "bob2" })]
- [TestCase("Fish1", "Fish1", new[] { "fish1"})] // default behavior is case sensitive
- public void TestMakeUnique_DefaultComparer(string? stringIn, string expectedOut, string[] whenCollection)
+ private static IEnumerable MakeUnique_DefaultComparer_Cases =>
+ [
+ new( "bob", "bob", new[] { "fish" } ),
+ new( "fish", "fish2", new[] { "fish" } ),
+ new( "fish2", "fish3", new[] { "fish1", "fish2" } ),
+ new( null, "blank", new[] { "fish1", "fish2" } ),
+ new( " ", "blank", new[] { "fish1", "fish2" } ),
+ new( "bob", "bob3", new[] { "bob", "bob2" } ),
+ new( "Fish1", "Fish1", new[] { "fish1" } )
+ ];
+
+ private static IEnumerable MakeUnique_IgnoreCase_Cases =>
+ [
+ new( "bob", "bob", new[] { "fish" } ),
+ new( "Fish", "Fish2", new[] { "fish" } ),
+ new( "fIsh2", "fIsh3", new[] { "fish1", "fish2" } ),
+ new( "fish2", "fish3", new[] { "fiSH1", "fiSH2" } )
+ ];
+
+ private static IEnumerable PadBoth_Cases =>
+ [
+ new( "a", 4, " a " ),
+ new( "a", 5, " a " ),
+ new( "b l", 5, " b l " ),
+ new( "sooooLooong", 2, "sooooLooong" )
+ ];
+
+ [Test]
+ [TestCaseSource( nameof( MakeUnique_DefaultComparer_Cases ) )]
+ public void MakeUnique_DefaultComparer( string? stringIn, string expectedOut, string[] whenCollection )
{
- ClassicAssert.AreEqual(expectedOut, stringIn.MakeUnique(whenCollection));
+ Assert.That( stringIn.MakeUnique( whenCollection ), Is.EqualTo( expectedOut ) );
}
- [TestCase("bob", "bob", new[] { "fish" })]
- [TestCase("Fish", "Fish2", new[] { "fish" })]
- [TestCase("fIsh2", "fIsh3", new[] { "fish1", "fish2" })]
- [TestCase("fish2", "fish3", new[] { "fiSH1", "fiSH2" })]
- public void TestMakeUnique_IgnoreCaps(string stringIn, string expectedOut, string[] whenCollection)
+ [Test]
+ [TestCaseSource( nameof( MakeUnique_IgnoreCase_Cases ) )]
+ public void MakeUnique_IgnoreCase( string stringIn, string expectedOut, string[] whenCollection )
{
- ClassicAssert.AreEqual(expectedOut, stringIn.MakeUnique(whenCollection,System.StringComparer.InvariantCultureIgnoreCase));
+ Assert.That( stringIn.MakeUnique( whenCollection, StringComparer.InvariantCultureIgnoreCase ), Is.EqualTo( expectedOut ) );
}
- [TestCase("a",4," a ")]
- [TestCase("a", 5, " a ")]
- [TestCase("b l", 5, " b l ")]
- [TestCase("sooooLooong", 2, "sooooLooong")]
- public void TestPadBoth(string input, int length, string expectedOutput)
+ [Test]
+ [TestCaseSource( nameof( PadBoth_Cases ) )]
+ public void PadBoth( string input, int length, string expectedOutput )
{
- ClassicAssert.AreEqual(expectedOutput, input.PadBoth(length));
+ Assert.That( input.PadBoth( length ), Is.EqualTo( expectedOutput ) );
}
}
diff --git a/tests/TabViewTests.cs b/tests/TabViewTests.cs
index fdf6d9ba..64be723a 100644
--- a/tests/TabViewTests.cs
+++ b/tests/TabViewTests.cs
@@ -1,234 +1,414 @@
-using System.IO;
-using System.Linq;
-using Terminal.Gui;
-using TerminalGuiDesigner;
-using TerminalGuiDesigner.FromCode;
-using TerminalGuiDesigner.Operations;
+using System.Runtime.CompilerServices;
using TerminalGuiDesigner.Operations.TabOperations;
-using TerminalGuiDesigner.ToCode;
namespace UnitTests;
-class TabViewTests : Tests
+[TestFixture]
+[Category( "Code Generation" )]
+internal class TabViewTests : Tests
{
+ private static IEnumerable TabView_Tab_SubViewTypes =>
+ [
+ (Label)RuntimeHelpers.GetUninitializedObject( typeof( Label ) ),
+ (Button)RuntimeHelpers.GetUninitializedObject( typeof( Button ) ),
+ (StatusBar)RuntimeHelpers.GetUninitializedObject( typeof( StatusBar ) )
+ ];
+
[Test]
- public void TestRoundTrip_PreserveTabs()
+ public void AddingSubControlToTab( [ValueSource( nameof( TabView_Tab_SubViewTypes ) )] T dummyObject )
+ where T : View, new( )
{
- TabView tabIn = RoundTrip(
- (d, t) =>
- ClassicAssert.IsNotEmpty(t.Tabs, "Expected default TabView created by ViewFactory to have some placeholder Tabs"),
- out TabView tabOut);
+ ViewToCode viewToCode = new( );
- ClassicAssert.AreEqual(2, tabIn.Tabs.Count());
+ FileInfo file = new( $"{nameof( AddingSubControlToTab )}.cs" );
+ Design designOut = viewToCode.GenerateNewView( file, "YourNamespace", typeof( Dialog ) );
- ClassicAssert.AreEqual("Tab1", tabIn.Tabs.ElementAt(0).Text);
- ClassicAssert.AreEqual("Tab2", tabIn.Tabs.ElementAt(1).Text);
- }
+ using TabView tvOut = ViewFactory.Create( );
- ///
- /// Creates a Dialog with a in it. Returns the
- ///
- ///
- private Design GetTabView()
- {
- var viewToCode = new ViewToCode();
+ AddViewOperation addOperation = new( tvOut, designOut, "myTabview" );
+ Assume.That( addOperation.IsImpossible, Is.False );
+ bool addOperationSucceeded = false;
+ Assume.That( ( ) => addOperationSucceeded = OperationManager.Instance.Do( addOperation ), Throws.Nothing );
+ Assume.That( addOperationSucceeded );
- var file = new FileInfo("TestGetTabView.cs");
- var designOut = viewToCode.GenerateNewView(file, "YourNamespace", typeof(Dialog));
+ using T subview = ViewFactory.Create( );
- var tvOut = ViewFactory.Create(typeof(TabView));
+ AddViewOperation addSubviewOperation = new( subview, (Design)tvOut.Data, $"my{typeof( T ).Name}" );
+ Assert.That( addSubviewOperation.IsImpossible, Is.False );
+ bool addSubviewOperationSucceeded = false;
+ Assert.That( ( ) => addSubviewOperationSucceeded = OperationManager.Instance.Do( addSubviewOperation ), Throws.Nothing );
+ Assert.That( addSubviewOperationSucceeded );
+ Assert.That( tvOut.SelectedTab.View.Subviews.ToArray( ), Does.Contain( subview ), "Expected currently selected tab to have the new view but it did not" );
- OperationManager.Instance.Do(new AddViewOperation(tvOut, designOut, "myTabview"));
+ viewToCode.GenerateDesignerCs( designOut, typeof( Dialog ) );
- return (Design)tvOut.Data;
+ CodeToView codeToView = new( designOut.SourceCode );
+ Design designBackIn = codeToView.CreateInstance( );
+
+ using TabView tabIn = designBackIn.View.GetActualSubviews( ).OfType( ).Single( );
+ using T tabInSubview = tabIn.SelectedTab.View.Subviews.OfType( ).Single( );
+
+ Assert.That( tabInSubview.Text, Is.EqualTo( subview.Text ) );
}
[Test]
- public void TestChangeTabViewOrder_MoveTabLeft()
+ [Category( "UI" )]
+ [TestOf( typeof( MoveTabOperation ) )]
+ public void ChangeTabViewOrder_MoveTabLeft( )
{
- var d = this.GetTabView();
- var tv = (TabView)d.View;
+ Design d = GetTabView( );
+ using TabView tv = (TabView)d.View;
- ClassicAssert.AreEqual("Tab1", tv.Tabs.ElementAt(0).Text);
- ClassicAssert.AreEqual("Tab2", tv.Tabs.ElementAt(1).Text);
+ Assume.That( tv.Tabs, Has.Exactly( 2 ).InstanceOf( ) );
+ Assume.That( tv.Tabs, Has.ItemAt( 0 ).Property( "Text" ).EqualTo( "Tab1" ) );
+ Assume.That( tv.Tabs, Has.ItemAt( 1 ).Property( "Text" ).EqualTo( "Tab2" ) );
+ Assume.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ Assume.That( OperationManager.Instance.UndoStackSize, Is.Zero );
// Select Tab1
- tv.SelectedTab = tv.Tabs.First();
+ tv.SelectedTab = tv.Tabs.First( );
// try to move tab 1 left
- var cmd = new MoveTabOperation(d, tv.SelectedTab, -1);
- ClassicAssert.IsFalse(cmd.Do(), "Expected not to be able to move tab left because selected is the first");
+ MoveTabOperation cmd = new( d, tv.SelectedTab, -1 );
+ Assert.That( cmd.IsImpossible );
+ Assert.That( cmd.SupportsUndo );
+ bool cmdSucceeded = false;
+ Assert.That( ( ) => cmdSucceeded = OperationManager.Instance.Do( cmd ), Throws.Nothing );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( cmdSucceeded, Is.False, "Expected not to be able to move tab left because selected is the first" );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ } );
// Select Tab2
- tv.SelectedTab = tv.Tabs.Last();
+ tv.SelectedTab = tv.Tabs.Last( );
- ClassicAssert.AreEqual(tv.SelectedTab.Text, "Tab2", "Tab2 should be selected before operation is applied");
+ Assert.That( tv.SelectedTab.Text, Is.EqualTo( "Tab2" ), "Tab2 should be selected before operation is applied" );
// try to move tab 2 left
- cmd = new MoveTabOperation(d, tv.SelectedTab, -1);
- ClassicAssert.IsFalse(cmd.IsImpossible);
- ClassicAssert.IsTrue(cmd.Do());
-
- ClassicAssert.AreEqual(tv.SelectedTab.Text, "Tab2", "Tab2 should still be selected after operation is applied");
+ MoveTabOperation cmd2 = new( d, tv.SelectedTab, -1 );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( cmd2.IsImpossible, Is.False );
+ Assert.That( cmd2.SupportsUndo );
+ } );
+ bool cmd2Succeeded = false;
+ Assert.That( ( ) => cmd2Succeeded = OperationManager.Instance.Do( cmd2 ), Throws.Nothing );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( cmd2Succeeded );
+ Assert.That( tv.SelectedTab.Text, Is.EqualTo( "Tab2" ), "Tab2 should still be selected after operation is applied" );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ } );
// tabs should now be in reverse order
- ClassicAssert.AreEqual("Tab2", tv.Tabs.ElementAt(0).Text);
- ClassicAssert.AreEqual("Tab1", tv.Tabs.ElementAt(1).Text);
+ Assert.That( tv.Tabs, Has.Exactly( 2 ).InstanceOf( ) );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( tv.Tabs, Has.ItemAt( 0 ).Property( "Text" ).EqualTo( "Tab2" ) );
+ Assert.That( tv.Tabs, Has.ItemAt( 1 ).Property( "Text" ).EqualTo( "Tab1" ) );
+ } );
- cmd.Undo();
+ Assert.That( OperationManager.Instance.Undo, Throws.Nothing );
+ Assert.Multiple( static ( ) =>
+ {
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.EqualTo( 1 ) );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ } );
// undoing command should revert to original tab order
- ClassicAssert.AreEqual("Tab1", tv.Tabs.ElementAt(0).Text);
- ClassicAssert.AreEqual("Tab2", tv.Tabs.ElementAt(1).Text);
+ Assert.That( tv.Tabs, Has.Exactly( 2 ).InstanceOf( ) );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( tv.Tabs, Has.ItemAt( 0 ).Property( "Text" ).EqualTo( "Tab1" ) );
+ Assert.That( tv.Tabs, Has.ItemAt( 1 ).Property( "Text" ).EqualTo( "Tab2" ) );
+ } );
+
+ // Now let's redo it
+ Assert.That( OperationManager.Instance.Redo, Throws.Nothing );
+ Assert.Multiple( static ( ) =>
+ {
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ } );
+
+ // Redoing command should put the tabs back in reversed order
+ Assert.That( tv.Tabs, Has.Exactly( 2 ).InstanceOf( ) );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( tv.Tabs, Has.ItemAt( 0 ).Property( "Text" ).EqualTo( "Tab2" ) );
+ Assert.That( tv.Tabs, Has.ItemAt( 1 ).Property( "Text" ).EqualTo( "Tab1" ) );
+ } );
}
[Test]
- public void TestRemoveTabOperation()
+ public void GetAllDesigns_TabView( [ValueSource( nameof( TabView_Tab_SubViewTypes ) )] T dummyObject )
+ where T : View, new( )
{
- var d = this.GetTabView();
- var tv = (TabView)d.View;
+ using TabView tv = new( );
- ClassicAssert.AreEqual(2, tv.Tabs.Count);
- ClassicAssert.AreEqual("Tab1", tv.Tabs.ElementAt(0).Text);
- ClassicAssert.AreEqual("Tab2", tv.Tabs.ElementAt(1).Text);
+ SourceCodeFile source = new( new FileInfo( $"{nameof( GetAllDesigns_TabView )}.cs" ) );
- // Select Tab1
- tv.SelectedTab = tv.Tabs.First();
+ using T subview1 = ViewFactory.Create( null, null, "fff" );
+ Design subview1Design = new( source, "subview1", subview1 );
+ subview1Design.View.Data = subview1Design;
- // try to remove the first tab
- ClassicAssert.IsTrue(OperationManager.Instance.Do(new RemoveTabOperation(d, tv.SelectedTab)));
-
- ClassicAssert.AreEqual(1, tv.Tabs.Count);
- ClassicAssert.AreEqual("Tab2", tv.Tabs.ElementAt(0).Text);
-
- // remove the last tab (tab2)
- ClassicAssert.IsTrue(OperationManager.Instance.Do(new RemoveTabOperation(d, tv.SelectedTab)));
- ClassicAssert.IsEmpty(tv.Tabs);
-
- OperationManager.Instance.Undo();
+ using T subview2 = ViewFactory.Create( null, null, "ddd" );
+ Design subview2Design = new( source, "subview2", subview2 );
+ subview2Design.View.Data = subview2Design;
- ClassicAssert.AreEqual(1, tv.Tabs.Count);
- ClassicAssert.AreEqual("Tab2", tv.Tabs.ElementAt(0).Text);
+ tv.AddTab( new( "Yay", subview1Design.View ), true );
+ tv.AddTab( new( "Yay", subview2Design.View ), false );
- OperationManager.Instance.Redo();
- ClassicAssert.IsEmpty(tv.Tabs);
+ Design tvDesign = new( source, "tv", tv );
- // undo removing both
- OperationManager.Instance.Undo();
- OperationManager.Instance.Undo();
-
- ClassicAssert.AreEqual(2, tv.Tabs.Count);
- ClassicAssert.AreEqual("Tab1", tv.Tabs.ElementAt(0).Text);
- ClassicAssert.AreEqual("Tab2", tv.Tabs.ElementAt(1).Text);
+ Design[] designs = tvDesign.GetAllDesigns( ).ToArray( );
+ Assert.That( designs, Is.EquivalentTo( (Design[]) [tvDesign, subview1Design, subview2Design] ) );
}
[Test]
- public void TestRoundTrip_DuplicateTabNames()
+ [Category( "UI" )]
+ [TestOf( typeof( RemoveTabOperation ) )]
+ public void RemoveTabOperation( )
{
- var viewToCode = new ViewToCode();
-
- var file = new FileInfo("TestRoundTrip_DuplicateTabNames.cs");
- var designOut = viewToCode.GenerateNewView(file, "YourNamespace", typeof(Dialog));
-
- var tvOut = ViewFactory.Create( );
-
- OperationManager.Instance.Do(new AddViewOperation(tvOut, designOut, "myTabview"));
-
- // Give both tabs the same name
- tvOut.Tabs.ElementAt(0).Text = "MyTab";
- tvOut.Tabs.ElementAt(1).Text = "MyTab";
+ Design d = GetTabView( );
+ using TabView tv = (TabView)d.View;
- viewToCode.GenerateDesignerCs(designOut, typeof(Dialog));
+ Assume.That( tv.Tabs, Has.Exactly( 2 ).InstanceOf( ) );
+ Assume.That( tv.Tabs, Has.ItemAt( 0 ).Property( "Text" ).EqualTo( "Tab1" ) );
+ Assume.That( tv.Tabs, Has.ItemAt( 1 ).Property( "Text" ).EqualTo( "Tab2" ) );
+ Assume.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ Assume.That( OperationManager.Instance.UndoStackSize, Is.Zero );
- var tabOut = designOut.View.GetActualSubviews().OfType().Single();
+ Tab tab1 = tv.Tabs.ElementAt( 0 );
+ Tab tab2 = tv.Tabs.ElementAt( 1 );
+ Assume.That( tab1, Is.Not.SameAs( tab2 ) );
- var codeToView = new CodeToView(designOut.SourceCode);
- var designBackIn = codeToView.CreateInstance();
+ // Select Tab1
+ tv.SelectedTab = tv.Tabs.First( );
- var tabIn = designBackIn.View.GetActualSubviews().OfType().Single();
+ // try to remove the first tab
+ RemoveTabOperation removeTab1 = new( d, tv.SelectedTab );
+ Assert.That( removeTab1.IsImpossible, Is.False );
+ Assert.That( removeTab1.SupportsUndo );
+ bool removeTab1Succeeded = false;
+ Assert.That( ( ) => removeTab1Succeeded = OperationManager.Instance.Do( removeTab1 ), Throws.Nothing );
+ Assert.That( removeTab1Succeeded );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( tv.Tabs, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.That( tv.Tabs, Does.Not.Contain( tab1 ) );
+ Assert.That( tv.Tabs, Does.Contain( tab2 ) );
+ Assert.That( tv.SelectedTab, Is.SameAs( tab2 ) );
+ } );
+
+ Assert.Multiple( static ( ) =>
+ {
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ } );
- ClassicAssert.AreEqual(2, tabIn.Tabs.Count());
+ // remove the last tab (tab2)
+ RemoveTabOperation removeTab2 = new( d, tv.SelectedTab );
+ bool removeTab2Succeeded = false;
+ Assert.That( ( ) => removeTab2Succeeded = OperationManager.Instance.Do( removeTab2 ), Throws.Nothing );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( removeTab2Succeeded );
+ Assert.That( tv.Tabs, Is.Empty );
+ Assert.That( tv.SelectedTab, Is.Null );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 2 ) );
+ } );
+
+ Assert.That( OperationManager.Instance.Undo, Throws.Nothing );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( tv.Tabs, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.That( tv.Tabs, Does.Not.Contain( tab1 ) );
+ Assert.That( tv.Tabs, Does.Contain( tab2 ) );
+ Assert.That( tv.SelectedTab, Is.SameAs( tab2 ) );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.EqualTo( 1 ) );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ } );
+
+ Assert.That( OperationManager.Instance.Redo, Throws.Nothing );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( tv.Tabs, Is.Empty );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 2 ) );
+ } );
- ClassicAssert.AreEqual("MyTab", tabIn.Tabs.ElementAt(0).Text);
- ClassicAssert.AreEqual("MyTab", tabIn.Tabs.ElementAt(1).Text);
+ // undo removing both
+ Assert.That( OperationManager.Instance.Undo, Throws.Nothing );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( tv.Tabs, Has.Exactly( 1 ).InstanceOf( ) );
+ Assert.That( tv.Tabs, Does.Not.Contain( tab1 ) );
+ Assert.That( tv.Tabs, Does.Contain( tab2 ) );
+ Assert.That( tv.SelectedTab, Is.SameAs( tab2 ) );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.EqualTo( 1 ) );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ } );
+
+ Assert.That( OperationManager.Instance.Undo, Throws.Nothing );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( tv.Tabs, Has.Exactly( 2 ).InstanceOf( ) );
+ Assert.That( tv.Tabs, Has.ItemAt( 0 ).SameAs( tab1 ) );
+ Assert.That( tv.Tabs, Has.ItemAt( 1 ).SameAs( tab2 ) );
+ // TODO: Shouldn't tab1 be selected now, after undoing its remove operation?
+ Assert.Warn( "Possible unintended behavior in tab selection after undo" );
+ // Assert.That( tv.SelectedTab, Is.SameAs( tab1 ) );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.EqualTo( 2 ) );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ } );
}
[Test]
- public void TestAddingSubcontrolToTab()
+ public void RoundTrip_DuplicateTabTitles( [Values( "TextTabTitle", "12345", "TextAndNumber5" )] string tabText )
{
- var viewToCode = new ViewToCode();
+ // TODO: I'm not sure this test is really necessary.
+ // Why would the title text be any different from any other property?
+ ViewToCode viewToCode = new( );
+
+ FileInfo file = new( $"{nameof( RoundTrip_DuplicateTabTitles )}.cs" );
+ Design designOut = viewToCode.GenerateNewView( file, "YourNamespace", typeof( Dialog ) );
- var file = new FileInfo("TestAddingSubcontrolToTab.cs");
- var designOut = viewToCode.GenerateNewView(file, "YourNamespace", typeof(Dialog));
+ using TabView tvOut = ViewFactory.Create( );
- var tvOut = ViewFactory.Create( );
+ AddViewOperation addOperation = new( tvOut, designOut, "myTabview" );
+ Assume.That( addOperation.IsImpossible, Is.False );
+ bool addOperationSucceeded = false;
+ Assume.That( ( ) => addOperationSucceeded = OperationManager.Instance.Do( addOperation ), Throws.Nothing );
+ Assume.That( addOperationSucceeded );
- OperationManager.Instance.Do(new AddViewOperation(tvOut, designOut, "myTabview"));
+ // Give both tabs the same title text
+ tvOut.Tabs.ElementAt( 0 ).Text = tabText;
+ tvOut.Tabs.ElementAt( 1 ).Text = tabText;
- var label = ViewFactory.Create( );
+ viewToCode.GenerateDesignerCs( designOut, typeof( Dialog ) );
- OperationManager.Instance.Do(new AddViewOperation(label, (Design)tvOut.Data, "myLabel"));
- ClassicAssert.Contains(label, tvOut.SelectedTab.View.Subviews.ToArray(), "Expected currently selected tab to have the new label but it did not");
+ Assume.That( designOut.View.GetActualSubviews( ).OfType( ).Single( ), Is.SameAs( tvOut ) );
- viewToCode.GenerateDesignerCs(designOut, typeof(Dialog));
+ CodeToView codeToView = new( designOut.SourceCode );
+ Design? designBackIn = null;
+ Assert.That( ( ) => designBackIn = codeToView.CreateInstance( ), Throws.Nothing );
+ Assert.That( designBackIn, Is.Not.Null.And.InstanceOf( ) );
- var codeToView = new CodeToView(designOut.SourceCode);
- var designBackIn = codeToView.CreateInstance();
+ using TabView tvIn = designBackIn!.View.GetActualSubviews( ).OfType( ).Single( );
- var tabIn = designBackIn.View.GetActualSubviews().OfType().Single();
- var tabInLabel = tabIn.SelectedTab.View.Subviews.Single();
+ Assert.That( tvIn.Tabs, Has.Exactly( 2 ).InstanceOf( ) );
- ClassicAssert.AreEqual(label.Text, tabInLabel.Text);
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( tvIn, Is.Not.SameAs( tvOut ) );
+ Assert.That( tvIn.Tabs.ElementAt( 0 ).Text, Is.EqualTo( tabText ) );
+ Assert.That( tvIn.Tabs.ElementAt( 1 ).Text, Is.EqualTo( tabText ) );
+ } );
}
[Test]
- public void TestGetAllDesigns_TabView()
+ public void RoundTrip_PreserveTabs( )
{
- var tv = new TabView();
-
- var source = new SourceCodeFile(new FileInfo("yarg.cs"));
-
- var lbl1 = new Design(source, "lbl1", new Label("fff"));
- lbl1.View.Data = lbl1;
-
- var lbl2 = new Design(source, "lbl2", new Label("ddd"));
- lbl2.View.Data = lbl2;
+ using TabView tabIn =
+ RoundTrip(
+ static ( _, t ) =>
+ {
+ Assume.That( t.Tabs, Is.Not.Empty, "Expected default TabView created by ViewFactory to have some placeholder Tabs" );
+ Assume.That( t.Tabs, Has.Exactly( 2 ).InstanceOf( ) );
+ Assume.That( t.Tabs, Has.ItemAt( 0 ).Property( "Text" ).EqualTo( "Tab1" ) );
+ Assume.That( t.Tabs, Has.ItemAt( 1 ).Property( "Text" ).EqualTo( "Tab2" ) );
+ },
+ out TabView tabOut );
+
+ Assert.That( tabIn.Tabs, Has.Exactly( 2 ).InstanceOf( ) );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( tabIn.Tabs, Has.ItemAt( 0 ).Property( "Text" ).EqualTo( "Tab1" ) );
+ Assert.That( tabIn.Tabs, Has.ItemAt( 1 ).Property( "Text" ).EqualTo( "Tab2" ) );
+ Assert.That( tabOut.Tabs, Has.ItemAt( 0 ).Property( "Text" ).EqualTo( "Tab1" ) );
+ Assert.That( tabOut.Tabs, Has.ItemAt( 1 ).Property( "Text" ).EqualTo( "Tab2" ) );
+
+ // Also prove they aren't the same objects
+ Assert.That( tabIn, Is.Not.SameAs( tabOut ) );
+ Assert.That( tabIn.Tabs, Has.ItemAt( 0 ).Not.SameAs( tabOut.Tabs.ElementAt( 0 ) ) );
+ Assert.That( tabIn.Tabs, Has.ItemAt( 1 ).Not.SameAs( tabOut.Tabs.ElementAt( 1 ) ) );
+ } );
+ tabOut.Dispose( );
+ }
- tv.AddTab(new Tab("Yay", lbl1.View), true);
- tv.AddTab(new Tab("Yay", lbl2.View), false);
+ [Test]
+ [Category( "Terminal.Gui Extensions" )]
+ [TestOf( typeof( TabViewExtensions ) )]
+ public void TabView_IsBorderless_DependsOnShowBorder( )
+ {
+ using TabView inst = new( );
- var tvDesign = new Design(source, "tv", tv);
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( inst.Style.ShowBorder );
+ Assert.That( inst.IsBorderlessContainerView( ), Is.False );
+ } );
- ClassicAssert.Contains(tvDesign, tvDesign.GetAllDesigns().ToArray());
- ClassicAssert.Contains(lbl1, tvDesign.GetAllDesigns().ToArray());
- ClassicAssert.Contains(lbl2, tvDesign.GetAllDesigns().ToArray());
+ inst.Style.ShowBorder = false;
- ClassicAssert.AreEqual(3, tvDesign.GetAllDesigns().Count(), $"Expected only 3 Designs but they were {string.Join(",", tvDesign.GetAllDesigns())}");
+ Assert.That( inst.IsBorderlessContainerView( ) );
}
[Test]
- public void TabView_IsBorderless_DependsOnShowBorder()
+ [Category( "Terminal.Gui Extensions" )]
+ [TestOf( typeof( TabViewExtensions ) )]
+ public void TabView_IsBorderless_DependsOnTabsOnBottom( )
{
- var inst = new TabView();
+ using TabView inst = new( );
- ClassicAssert.IsTrue(inst.Style.ShowBorder);
- ClassicAssert.False(inst.IsBorderlessContainerView());
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( inst.Style.TabsOnBottom, Is.False );
+ Assert.That( inst.IsBorderlessContainerView( ), Is.False );
+ } );
- inst.Style.ShowBorder = false;
+ inst.Style.TabsOnBottom = true;
- ClassicAssert.True(inst.IsBorderlessContainerView());
+ Assert.That( inst.IsBorderlessContainerView( ) );
}
- [Test]
- public void TabView_IsBorderless_DependsOnTabsOnBottom()
+ ///
+ /// Creates a Dialog with a in it. Returns the
+ ///
+ ///
+ private static Design GetTabView( )
{
- var inst = new TabView();
+ var viewToCode = new ViewToCode( );
- ClassicAssert.IsFalse(inst.Style.TabsOnBottom);
- ClassicAssert.False(inst.IsBorderlessContainerView());
+ var file = new FileInfo( "TestGetTabView.cs" );
+ var designOut = viewToCode.GenerateNewView( file, "YourNamespace", typeof( Dialog ) );
- inst.Style.TabsOnBottom = true;
+ using TabView tvOut = ViewFactory.Create( );
+
+ AddViewOperation addViewOperation = new( tvOut, designOut, "myTabview" );
+ Assume.That( addViewOperation, Is.Not.Null.And.InstanceOf( ) );
+ Assume.That( addViewOperation.IsImpossible, Is.False );
+ Assume.That( addViewOperation.SupportsUndo );
+
+ bool addViewOperationSucceeded = false;
+ Assume.That( ( ) => addViewOperationSucceeded = OperationManager.Instance.Do( addViewOperation ), Throws.Nothing );
+ Assume.That( addViewOperationSucceeded );
- ClassicAssert.True(inst.IsBorderlessContainerView());
+ // The above operation interferes with expected results in tests,
+ // so let's clear it out.
+ OperationManager.Instance.ClearUndoRedo( );
+
+ return (Design)tvOut.Data;
}
-}
\ No newline at end of file
+}
diff --git a/tests/TableViewTests.cs b/tests/TableViewTests.cs
index 9efc9d42..1b55a2f9 100644
--- a/tests/TableViewTests.cs
+++ b/tests/TableViewTests.cs
@@ -1,50 +1,63 @@
using System.Data;
-using System.Linq;
-using Terminal.Gui;
-using TerminalGuiDesigner;
-using TerminalGuiDesigner.Operations;
namespace UnitTests;
+[TestFixture]
+[Category( "Code Generation" )]
+[Parallelizable( ParallelScope.Children )]
internal class TableViewTests : Tests
{
[Test]
- public void TestRoundTrip_PreserveColumns()
+ [Category( "Terminal.Gui Extensions" )]
+ [TestOf( typeof( TableViewExtensions ) )]
+ public void RoundTrip_PreservesColumns( )
{
- TableView tableIn = RoundTrip(
-
- (d, v) => ClassicAssert.IsNotEmpty(v.GetDataTable().Columns, "Default ViewFactory should create some columns for new TableViews"),
- out TableView tableOut);
-
- ClassicAssert.IsNotNull(tableIn.Table);
-
- ClassicAssert.AreEqual(tableOut.Table.Columns, tableIn.Table.Columns);
- ClassicAssert.AreEqual(tableOut.Table.Rows, tableIn.Table.Rows);
+ using TableView tableIn =
+ RoundTrip( static ( _, v ) =>
+ {
+ Assume.That(
+ v.GetDataTable( ).Columns,
+ Is.Not.Empty,
+ "Default ViewFactory should create some columns for new TableViews" );
+ },
+ out TableView tableOut );
+
+ Assert.That( tableIn.Table, Is.Not.Null );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( tableIn.Table.Columns, Is.EqualTo( tableOut.Table.Columns ) );
+ Assert.That( tableIn.Table.Rows, Is.EqualTo( tableOut.Table.Rows ) );
+ } );
+ tableOut.Dispose( );
}
[Test]
- public void TestRoundTrip_TwoTablesWithDuplicatedColumns()
+ [Category( "Terminal.Gui Extensions" )]
+ [TestOf( typeof( TableViewExtensions ) )]
+ public void RoundTrip_TwoTablesWithDuplicatedColumns( )
{
// Create a TableView
- TableView tableIn = RoundTrip(
- (d, v) =>
- {
- // create a second TableView also on the root
- TableView tvOut2 = ViewFactory.Create( );
- OperationManager.Instance.Do(new AddViewOperation(tvOut2, d.GetRootDesign(), "myTable2"));
- },
- out TableView tableOut);
+ using TableView tableIn = RoundTrip( static ( d, _ ) =>
+ {
+ // create a second TableView also on the root
+ TableView tvOut2 = ViewFactory.Create( );
+ OperationManager.Instance.Do( new AddViewOperation( tvOut2, d.GetRootDesign( ), "myTable2" ) );
+ },
+ out TableView tableOut );
// Views should collide on column name but still compile
- var designBackIn = ((Design)tableIn.Data).GetRootDesign();
- var tables = designBackIn.View.GetActualSubviews().OfType().ToArray();
+ var designBackIn = ( (Design)tableIn.Data ).GetRootDesign( );
+ var tables = designBackIn.View.GetActualSubviews( ).OfType( ).ToArray( );
- ClassicAssert.AreEqual(2, tables.Length);
+ Assert.That( tables, Has.Exactly( 2 ).InstanceOf( ) );
- ClassicAssert.That(
- tables[0].GetDataTable().Columns.Cast().Select(c => c.ColumnName),
+ Assert.That(
+ tables[ 0 ].GetDataTable( ).Columns.Cast( ).Select( static c => c.ColumnName ),
Is.EquivalentTo(
- tables[1].GetDataTable().Columns.Cast().Select(c => c.ColumnName)),
- "Expected both TableViews to have columns with the same names");
+ tables[ 1 ].GetDataTable( ).Columns.Cast( ).Select( static c => c.ColumnName ) ),
+ "Expected both TableViews to have columns with the same names" );
+
+ tableOut.Dispose( );
}
}
diff --git a/tests/TestSupportTypes/Helpers.cs b/tests/TestSupportTypes/Helpers.cs
new file mode 100644
index 00000000..0fe2a867
--- /dev/null
+++ b/tests/TestSupportTypes/Helpers.cs
@@ -0,0 +1,24 @@
+using System.CodeDom;
+using System.CodeDom.Compiler;
+using System.Diagnostics.Contracts;
+using Microsoft.CSharp;
+
+namespace UnitTests.TestSupportTypes
+{
+ internal static class Helpers
+ {
+
+ [Pure]
+ internal static string ExpressionToCode(CodeExpression expression)
+ {
+ CSharpCodeProvider provider = new();
+
+ using StringWriter sw = new();
+ using IndentedTextWriter tw = new(sw, " ");
+ provider.GenerateCodeFromExpression(expression, tw, new());
+ tw.Close();
+
+ return sw.GetStringBuilder().ToString();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Tests.cs b/tests/Tests.cs
index 5a9230e7..c19fff8f 100644
--- a/tests/Tests.cs
+++ b/tests/Tests.cs
@@ -1,13 +1,6 @@
-using System;
-using System.IO;
-using System.Linq;
+
+
using System.Runtime.CompilerServices;
-using Terminal.Gui;
-using TerminalGuiDesigner;
-using TerminalGuiDesigner.FromCode;
-using TerminalGuiDesigner.Operations;
-using TerminalGuiDesigner.ToCode;
-using TerminalGuiDesigner.UI;
namespace UnitTests;
@@ -72,7 +65,7 @@ protected static Design Get100By100([CallerMemberName] string? caller = null)
return rootDesign;
}
-
+
///
/// Creates a new instance of using . Then calls the
/// provided action before writing out and reading back the code. Returns
@@ -115,7 +108,7 @@ protected static T2 RoundTrip(Action adjust, out T2 viewOut,
return designBackIn.View
.GetActualSubviews( )
.OfType( )
- .Single( v => v.Data is Design { FieldName: fieldName } );
+ .Single( static v => v.Data is Design { FieldName: fieldName } );
}
///
/// Performs a mouse drag from the first coordinates to the second (in screen space)
diff --git a/tests/UI/MouseManagerTests.cs b/tests/UI/MouseManagerTests.cs
new file mode 100644
index 00000000..fbb51842
--- /dev/null
+++ b/tests/UI/MouseManagerTests.cs
@@ -0,0 +1,495 @@
+using System.Runtime.CompilerServices;
+
+namespace UnitTests.UI;
+
+[TestFixture]
+[TestOf( typeof( MouseManager ) )]
+[Category( "UI" )]
+internal class MouseManagerTests : Tests
+{
+ [Test]
+ public void DragResizeView( [ValueSource( nameof( DragResizeView_Types ) )] T dummy )
+ where T : View, new( )
+ {
+ Design d = Get10By10View( );
+ Assume.That( dummy, Is.TypeOf( ) );
+
+ using T view = ViewFactory.Create( );
+ view.Width = 8;
+ view.Height = 1;
+
+ Design design = new( d.SourceCode, "myView", view );
+ view.Data = design;
+ d.View.Add( view );
+
+ Assert.That( view.Bounds.Width, Is.EqualTo( 8 ) );
+ MouseManager mgr = new( );
+
+ // we haven't done anything yet
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ Assert.That( view.Bounds.X, Is.Zero );
+ Assert.That( view.Bounds.Y, Is.Zero );
+ Assert.That( view.Bounds.Width, Is.EqualTo( 8 ) );
+ Assert.That( view.Bounds.Height, Is.EqualTo( 1 ) );
+ } );
+
+ // user presses down in the lower right of control
+ MouseEvent e = new( )
+ {
+ X = 6,
+ Y = 0,
+ Flags = MouseFlags.Button1Pressed
+ };
+
+ mgr.HandleMouse( e, d );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( view.Bounds.Y, Is.Zero );
+
+ // we still haven't committed to anything
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ } );
+
+ // user pulled view size +1 width and +1 height
+ e = new( )
+ {
+ X = 9,
+ Y = 0,
+ Flags = MouseFlags.Button1Pressed
+ };
+ mgr.HandleMouse( e, d );
+
+ // we still haven't committed to anything
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( view.Bounds.X, Is.Zero );
+ Assert.That( view.Bounds.Y, Is.Zero );
+ Assert.That( view.Bounds.Width, Is.EqualTo( 10 ), "Expected resize to increase Width when dragging" );
+ Assert.That( view.Bounds.Height, Is.EqualTo( 1 ), "Expected resize of button to ignore Y component" );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ } );
+
+ // user releases mouse (in place)
+ e = new( )
+ {
+ X = 9,
+ Y = 0
+ };
+ mgr.HandleMouse( e, d );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( view.Bounds.X, Is.Zero );
+ Assert.That( view.Bounds.Y, Is.Zero );
+ Assert.That( view.Bounds.Width, Is.EqualTo( 10 ), "Expected resize to increase Width when dragging" );
+ Assert.That( view.Bounds.Height, Is.EqualTo( 1 ) );
+
+ // we have now committed the drag so could undo
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ } );
+ }
+
+ [Test]
+ public void DragResizeView_CannotResize_1By1View( [Values( 0, 3 )] int locationOfViewX, [Values( 0, 3 )] int locationOfViewY )
+ {
+ Design d = Get10By10View( );
+
+ using View view = ViewFactory.Create( );
+ view.Width = Dim.Fill( );
+ view.X = locationOfViewX;
+ view.Y = locationOfViewY;
+ view.Width = 1;
+ view.Height = 1;
+
+ Design design = new( d.SourceCode, "myView", view );
+ view.Data = design;
+ d.View.Add( view );
+
+ d.View.LayoutSubviews( );
+
+ MouseManager mgr = new( );
+
+ // we haven't done anything yet
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ Assert.That( view.Frame.X, Is.EqualTo( locationOfViewX ) );
+ Assert.That( view.Frame.Y, Is.EqualTo( locationOfViewY ) );
+ Assert.That( view.Bounds.Width, Is.EqualTo( 1 ) );
+ Assert.That( view.Bounds.Height, Is.EqualTo( 1 ) );
+ } );
+
+ // user presses down in the lower right of control
+ MouseEvent e = new( )
+ {
+ X = locationOfViewX,
+ Y = locationOfViewY,
+ Flags = MouseFlags.Button1Pressed
+ };
+
+ View? hit = view.HitTest( e, out bool isBorder, out bool isLowerRight );
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( hit, Is.SameAs( view ) );
+ Assert.That( isBorder );
+ Assert.That( isLowerRight, Is.False, "Upper left should never be considered lower right even if view is 1x1" );
+ } );
+
+ mgr.HandleMouse( e, d );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( view.Frame.Y, Is.EqualTo( locationOfViewY ) );
+
+ // we still haven't committed to anything
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ } );
+
+ // user pulled view size down and left
+ e = new( )
+ {
+ X = 6,
+ Y = 3,
+ Flags = MouseFlags.Button1Pressed
+ };
+ mgr.HandleMouse( e, d );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( view.Bounds.Width, Is.EqualTo( 1 ) );
+ Assert.That( view.Bounds.Height, Is.EqualTo( 1 ) );
+ } );
+ }
+
+ [Test]
+ [TestCaseSource( nameof( DragResizeView_CannotResize_DimFill_Types ) )]
+ public void DragResizeView_CannotResize_DimFill( T dummy )
+ where T : View, new( )
+ {
+ Design d = Get10By10View( );
+ Assume.That( dummy, Is.TypeOf( ) );
+
+ using T view = ViewFactory.Create( );
+ view.Width = Dim.Fill( );
+ view.Height = 1;
+
+ Design design = new( d.SourceCode, "myView", view );
+ view.Data = design;
+ d.View.Add( view );
+
+ MouseManager mgr = new( );
+
+ // we haven't done anything yet
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ Assert.That( view.Bounds.X, Is.Zero );
+ Assert.That( view.Bounds.Y, Is.Zero );
+ Assert.That( view.Bounds.Width, Is.EqualTo( 10 ) );
+ Assert.That( view.Bounds.Height, Is.EqualTo( 1 ) );
+ } );
+
+ // user presses down in the lower right of control
+ MouseEvent e = new( )
+ {
+ X = 9,
+ Y = 0,
+ Flags = MouseFlags.Button1Pressed
+ };
+
+ mgr.HandleMouse( e, d );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( view.Bounds.Y, Is.Zero );
+
+ // we still haven't committed to anything
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ } );
+
+ // user pulled view size down and left
+ e = new( )
+ {
+ X = 6,
+ Y = 3,
+ Flags = MouseFlags.Button1Pressed
+ };
+ mgr.HandleMouse( e, d );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( view.Bounds.X, Is.Zero );
+ Assert.That( view.Bounds.Y, Is.Zero );
+ Assert.That( view.Bounds.Width, Is.EqualTo( 10 ), "Expected Width to remain constant because it is Dim.Fill()" );
+ Assert.That( view.Bounds.Height, Is.EqualTo( 4 ), "Expected resize to update Y component" );
+ Assert.That( view.Width, Is.EqualTo( Dim.Fill( ) ) );
+ Assert.That( view.Height, Is.EqualTo( Dim.Sized( 4 ) ) );
+ } );
+
+ // we still haven't committed to anything
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+
+ // user releases mouse (in place)
+ e = new( )
+ {
+ X = 6,
+ Y = 3
+ };
+ mgr.HandleMouse( e, d );
+
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( view.Bounds.X, Is.Zero );
+ Assert.That( view.Bounds.Y, Is.Zero );
+ Assert.That( view.Bounds.Width, Is.EqualTo( 10 ), "Expected Width to remain constant because it is Dim.Fill()" );
+ Assert.That( view.Bounds.Height, Is.EqualTo( 4 ), "Expected resize to update Y component" );
+ Assert.That( view.Width, Is.EqualTo( Dim.Fill( ) ) );
+ Assert.That( view.Height, Is.EqualTo( Dim.Sized( 4 ) ) );
+ } );
+
+ // we have now committed the drag so could undo
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+
+ OperationManager.Instance.Undo( );
+
+ // Should reset us to the initial state
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ Assert.That( view.Bounds.X, Is.Zero );
+ Assert.That( view.Bounds.Y, Is.Zero );
+ Assert.That( view.Bounds.Width, Is.EqualTo( 10 ) );
+ Assert.That( view.Bounds.Height, Is.EqualTo( 1 ) );
+ Assert.That( view.Width, Is.EqualTo( Dim.Fill( ) ) );
+ Assert.That( view.Height, Is.EqualTo( Dim.Sized( 1 ) ) );
+ } );
+ }
+
+ [Test]
+ [TestCaseSource( nameof( DragSelectionBox_Cases ) )]
+ public void DragSelectionBox( int xStart, int yStart, int xEnd, int yEnd, int[] expectSelected )
+ {
+ Design d = Get10By10View( );
+
+ /*
+ Hi
+ Hi
+ Hi
+ */
+
+ using Label lbl1 = new( 2, 1, "Hi" );
+ using Label lbl2 = new( 4, 2, "Hi" );
+ using Label lbl3 = new( 2, 3, "Hi" );
+
+ Design[] labels =
+ [
+ new( d.SourceCode, "lbl1", lbl1 ),
+ new( d.SourceCode, "lbl2", lbl2 ),
+ new( d.SourceCode, "lbl3", lbl3 )
+ ];
+
+ lbl1.Data = labels[ 0 ];
+ lbl2.Data = labels[ 1 ];
+ lbl3.Data = labels[ 2 ];
+
+ d.View.Add( lbl1 );
+ d.View.Add( lbl2 );
+ d.View.Add( lbl3 );
+
+ SelectionManager selection = SelectionManager.Instance;
+ selection.Clear( );
+ MouseManager mgr = new( );
+
+ // user presses down
+ MouseEvent e = new( )
+ {
+ X = xStart,
+ Y = yStart,
+ Flags = MouseFlags.Button1Pressed
+ };
+
+ mgr.HandleMouse( e, d );
+
+ // user pulled selection box to destination
+ e = new( )
+ {
+ X = xEnd,
+ Y = yEnd,
+ Flags = MouseFlags.Button1Pressed
+ };
+ mgr.HandleMouse( e, d );
+
+ // user releases mouse (in place)
+ e = new( )
+ {
+ X = xEnd,
+ Y = yEnd
+ };
+ mgr.HandleMouse( e, d );
+
+ // do not expect dragging selection box to change anything
+ // or be undoable
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+
+ // for each selectable thing
+ for ( int i = 0; i < labels.Length; i++ )
+ {
+ // its selection status should match the expectation
+ // passed in the test case
+ Assert.That(
+ selection.Selected.ToList( ).Contains( labels[ i ] ), Is.EqualTo( expectSelected.Contains( i ) ),
+ $"Expectation wrong for label index {i} (indexes are 0 based)" );
+ }
+ }
+
+ [Test]
+ public void DragView( [ValueSource( nameof( GetDummyViewsForDrag ) )] T dummy, [Values( -5, -1, 0, 1, 5 )] int deltaX, [Values( -5, -1, 0, 1, 5 )] int deltaY )
+ where T : View, new( )
+ {
+ const int initialViewXPos = 0;
+ const int initialViewYPos = 0;
+ const int startDragX = 1;
+ const int startDragY = 0;
+
+ Assume.That( dummy.GetType( ), Is.EqualTo( typeof( T ) ) );
+ Design d = Get10By10View( );
+ using T view = ViewFactory.Create( null, null, "Hi there buddy" );
+ Design viewDesign = new( d.SourceCode, $"my{typeof( T ).Name}", view );
+ view.Data = viewDesign;
+ d.View.Add( view );
+
+ MouseManager mgr = new( );
+
+ // we haven't done anything yet
+ Assume.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ Assume.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ Assume.That( view.X, Is.EqualTo( (Pos)initialViewXPos ) );
+ Assume.That( view.Y, Is.EqualTo( (Pos)initialViewYPos ) );
+
+ // user presses down over the control
+ MouseEvent firstClick = new( )
+ {
+ X = startDragX,
+ Y = startDragY,
+ Flags = MouseFlags.Button1Pressed
+ };
+
+ mgr.HandleMouse( firstClick, d );
+
+ // we still haven't committed to anything
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( view.X, Is.EqualTo( (Pos)initialViewXPos ) );
+ Assert.That( view.Y, Is.EqualTo( (Pos)initialViewYPos ) );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ } );
+
+ // user moved view but still has mouse down
+ MouseEvent dragWithMouseButton1Down = new( )
+ {
+ X = startDragX + deltaX,
+ Y = startDragY + deltaY,
+ Flags = MouseFlags.Button1Pressed
+ };
+ mgr.HandleMouse( dragWithMouseButton1Down, d );
+
+ // we still haven't committed to anything
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( view.X, Is.EqualTo( (Pos)( initialViewXPos + deltaX ) ) );
+ Assert.That( view.Y, Is.EqualTo( (Pos)( initialViewYPos + deltaY ) ) );
+ Assert.That( view.Bounds.X, Is.Zero );
+ Assert.That( view.Bounds.Y, Is.Zero );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ } );
+
+ // user releases mouse
+ MouseEvent releaseMouseButton1AtNewCoordinates = new( )
+ {
+ X = startDragX + deltaX,
+ Y = startDragY + deltaY
+ };
+ mgr.HandleMouse( releaseMouseButton1AtNewCoordinates, d );
+
+ // We have now committed the drag.
+ // Check position
+ Assert.Multiple( ( ) =>
+ {
+ Assert.That( OperationManager.Instance.RedoStackSize, Is.Zero );
+ Assert.That( view.X, Is.EqualTo( (Pos)( initialViewXPos + deltaX ) ) );
+ Assert.That( view.Y, Is.EqualTo( (Pos)( initialViewYPos + deltaY ) ) );
+ } );
+
+ // Now check that undo state was properly tracked.
+ // TODO: This should condense down after the bugs are fixed.
+ // Verbose for now for easier debugging.
+ switch ( deltaX, deltaY )
+ {
+ case (-1, 0): // Left only by exactly 1
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ break;
+ case (< -1, 0): // Left only by more than 1
+ Assert.Warn( "Bug in handling of drag events. Moving left by more than 1 moves the view but does not push to undo stack" );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ break;
+ case (0, -1): // Up only by exactly 1
+ Assert.Warn( "Bug in handling of drag events. Moving up by exactly 1 moves the view but does not push to undo stack" );
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ break;
+ case (0, < -1): // Up only by more than 1
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ break;
+ case (< 0, < 0): // Up and left
+ Assert.Warn( "Bug in handling of drag events. Moving up and left to outside bounds moves the view but does not push to undo stack" );
+ // TODO: Fix this after bug is fixed.
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ break;
+ case (0, 0): // Not actually moved
+ Assert.Warn( "Bug in handling of drag events. 0-pixel move still gets pushed onto the undo stack." );
+ // TODO: Fix this after bug is fixed.
+ // Assert.That( OperationManager.Instance.UndoStackSize, Is.Zero );
+ // The undo stack should be empty, as this was not actually a move.
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ break;
+ case (> 0, 0): // Right only
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ break;
+ case (0, > 0): // Down only
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ break;
+ case (> 0, > 0): // Down and right
+ Assert.That( OperationManager.Instance.UndoStackSize, Is.EqualTo( 1 ) );
+ break;
+ }
+ }
+
+ private static IEnumerable DragResizeView_CannotResize_DimFill_Types( )
+ {
+ yield return new( (View)RuntimeHelpers.GetUninitializedObject( typeof( View ) ) ) { TestName = "DragResizeView_CannotResize_DimFill" };
+ }
+
+ private static IEnumerable DragResizeView_Types( )
+ {
+ yield return (Label)RuntimeHelpers.GetUninitializedObject( typeof( Label ) );
+ yield return (Button)RuntimeHelpers.GetUninitializedObject( typeof( Button ) );
+ yield return (TabView)RuntimeHelpers.GetUninitializedObject( typeof( TabView ) );
+ yield return (TableView)RuntimeHelpers.GetUninitializedObject( typeof( TableView ) );
+ yield return (View)RuntimeHelpers.GetUninitializedObject( typeof( View ) );
+ }
+
+ private static IEnumerable DragSelectionBox_Cases( )
+ {
+ yield return new( 1, 1, 4, 6, new[] { 0, 2 } );
+ yield return new( 1, 1, 10, 10, new[] { 0, 1, 2 } );
+ }
+
+ private static IEnumerable GetDummyViewsForDrag( )
+ {
+ yield return (Label)RuntimeHelpers.GetUninitializedObject( typeof( Label ) );
+ }
+}
diff --git a/tests/UnitTests.csproj b/tests/UnitTests.csproj
index e4655727..58db9026 100644
--- a/tests/UnitTests.csproj
+++ b/tests/UnitTests.csproj
@@ -29,7 +29,7 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
@@ -48,6 +48,7 @@
+