mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
- Tests that don't call Draw() don't need View.Driver set - Removed CreateFakeDriver() and Driver assignment from all 11 tests - Simplified UnwrappedCursorPosition test to not use SetRelativeLayout - All 181 parallelizable tests still pass Co-authored-by: tig <585482+tig@users.noreply.github.com>
476 lines
15 KiB
C#
476 lines
15 KiB
C#
using UnitTests;
|
|
|
|
namespace UnitTests_Parallelizable.ViewsTests;
|
|
|
|
/// <summary>
|
|
/// Tests for TextView navigation, tabs, and cursor positioning.
|
|
/// These replace the old non-parallelizable tests that had hard-coded viewport dimensions.
|
|
/// </summary>
|
|
public class TextViewNavigationTests : FakeDriverBase
|
|
{
|
|
[Fact]
|
|
public void Tab_And_BackTab_Navigation_Without_Text ()
|
|
{
|
|
var textView = new TextView
|
|
{
|
|
Width = 30,
|
|
Height = 10,
|
|
Text = ""
|
|
};
|
|
textView.BeginInit ();
|
|
textView.EndInit ();
|
|
|
|
// Add 100 tabs
|
|
for (var i = 0; i < 100; i++)
|
|
{
|
|
textView.Text += "\t";
|
|
}
|
|
|
|
// Move to end
|
|
textView.MoveEnd ();
|
|
Assert.Equal (new Point (100, 0), textView.CursorPosition);
|
|
|
|
// Test BackTab (Shift+Tab) navigation backwards
|
|
for (var i = 99; i >= 0; i--)
|
|
{
|
|
Assert.True (textView.NewKeyDownEvent (Key.Tab.WithShift));
|
|
Assert.Equal (new Point (i, 0), textView.CursorPosition);
|
|
}
|
|
|
|
// Test Tab navigation forwards
|
|
for (var i = 1; i <= 100; i++)
|
|
{
|
|
Assert.True (textView.NewKeyDownEvent (Key.Tab));
|
|
Assert.Equal (new Point (i, 0), textView.CursorPosition);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Tab_And_BackTab_Navigation_With_Text ()
|
|
{
|
|
var textView = new TextView
|
|
{
|
|
Width = 30,
|
|
Height = 10,
|
|
Text = "TAB to jump between text fields."
|
|
};
|
|
textView.BeginInit ();
|
|
textView.EndInit ();
|
|
|
|
Assert.Equal (new Point (0, 0), textView.CursorPosition);
|
|
|
|
// Navigate forward with Tab
|
|
for (var i = 1; i <= 100; i++)
|
|
{
|
|
Assert.True (textView.NewKeyDownEvent (Key.Tab));
|
|
Assert.Equal (new Point (i, 0), textView.CursorPosition);
|
|
}
|
|
|
|
// Navigate backward with BackTab
|
|
for (var i = 99; i >= 0; i--)
|
|
{
|
|
Assert.True (textView.NewKeyDownEvent (Key.Tab.WithShift));
|
|
Assert.Equal (new Point (i, 0), textView.CursorPosition);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Tab_With_CursorLeft_And_CursorRight_Without_Text ()
|
|
{
|
|
var textView = new TextView
|
|
{
|
|
Width = 30,
|
|
Height = 10,
|
|
Text = ""
|
|
};
|
|
textView.BeginInit ();
|
|
textView.EndInit ();
|
|
|
|
// Navigate forward with Tab
|
|
for (var i = 1; i <= 100; i++)
|
|
{
|
|
Assert.True (textView.NewKeyDownEvent (Key.Tab));
|
|
Assert.Equal (new Point (i, 0), textView.CursorPosition);
|
|
}
|
|
|
|
// Navigate backward with CursorLeft
|
|
for (var i = 99; i >= 0; i--)
|
|
{
|
|
Assert.True (textView.NewKeyDownEvent (Key.CursorLeft));
|
|
Assert.Equal (new Point (i, 0), textView.CursorPosition);
|
|
}
|
|
|
|
// Navigate forward with CursorRight
|
|
for (var i = 1; i <= 100; i++)
|
|
{
|
|
Assert.True (textView.NewKeyDownEvent (Key.CursorRight));
|
|
Assert.Equal (new Point (i, 0), textView.CursorPosition);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Tab_With_CursorLeft_And_CursorRight_With_Text ()
|
|
{
|
|
var textView = new TextView
|
|
{
|
|
Width = 30,
|
|
Height = 10,
|
|
Text = "TAB to jump between text fields."
|
|
};
|
|
textView.BeginInit ();
|
|
textView.EndInit ();
|
|
|
|
Assert.Equal (32, textView.Text.Length);
|
|
Assert.Equal (new Point (0, 0), textView.CursorPosition);
|
|
|
|
// Navigate forward with Tab
|
|
for (var i = 1; i <= 100; i++)
|
|
{
|
|
Assert.True (textView.NewKeyDownEvent (Key.Tab));
|
|
Assert.Equal (new Point (i, 0), textView.CursorPosition);
|
|
}
|
|
|
|
// Navigate backward with CursorLeft
|
|
for (var i = 99; i >= 0; i--)
|
|
{
|
|
Assert.True (textView.NewKeyDownEvent (Key.CursorLeft));
|
|
Assert.Equal (new Point (i, 0), textView.CursorPosition);
|
|
}
|
|
|
|
// Navigate forward with CursorRight
|
|
for (var i = 1; i <= 100; i++)
|
|
{
|
|
Assert.True (textView.NewKeyDownEvent (Key.CursorRight));
|
|
Assert.Equal (new Point (i, 0), textView.CursorPosition);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Tab_With_Home_End_And_BackTab ()
|
|
{
|
|
var textView = new TextView
|
|
{
|
|
Width = 30,
|
|
Height = 10,
|
|
Text = "TAB to jump between text fields."
|
|
};
|
|
textView.BeginInit ();
|
|
textView.EndInit ();
|
|
|
|
Assert.Equal (32, textView.Text.Length);
|
|
Assert.Equal (new Point (0, 0), textView.CursorPosition);
|
|
|
|
// Navigate forward with Tab to column 100
|
|
for (var i = 1; i <= 100; i++)
|
|
{
|
|
Assert.True (textView.NewKeyDownEvent (Key.Tab));
|
|
Assert.Equal (new Point (i, 0), textView.CursorPosition);
|
|
}
|
|
|
|
// Test Length increased due to tabs
|
|
Assert.Equal (132, textView.Text.Length);
|
|
|
|
// Press Home to go to beginning
|
|
Assert.True (textView.NewKeyDownEvent (Key.Home));
|
|
Assert.Equal (new Point (0, 0), textView.CursorPosition);
|
|
|
|
// Press End to go to end
|
|
Assert.True (textView.NewKeyDownEvent (Key.End));
|
|
Assert.Equal (132, textView.Text.Length);
|
|
Assert.Equal (new Point (132, 0), textView.CursorPosition);
|
|
|
|
// Find the position just before the last tab
|
|
string txt = textView.Text;
|
|
var col = txt.Length;
|
|
|
|
// Find the last tab position
|
|
while (col > 1 && txt [col - 1] != '\t')
|
|
{
|
|
col--;
|
|
|
|
// Safety check to prevent infinite loop
|
|
if (col == 0)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Set cursor to that position
|
|
textView.CursorPosition = new Point (col, 0);
|
|
|
|
// Navigate backward with BackTab (removes tabs, going back to original text)
|
|
while (col > 0)
|
|
{
|
|
col--;
|
|
Assert.True (textView.NewKeyDownEvent (Key.Tab.WithShift));
|
|
Assert.Equal (new Point (col, 0), textView.CursorPosition);
|
|
}
|
|
|
|
// Should be back at the original text
|
|
Assert.Equal ("TAB to jump between text fields.", textView.Text);
|
|
Assert.Equal (32, textView.Text.Length);
|
|
}
|
|
|
|
[Fact]
|
|
public void BackTab_Then_Tab_Navigation ()
|
|
{
|
|
var textView = new TextView
|
|
{
|
|
Width = 30,
|
|
Height = 10,
|
|
Text = ""
|
|
};
|
|
textView.BeginInit ();
|
|
textView.EndInit ();
|
|
|
|
// Add 100 tabs at end
|
|
for (var i = 0; i < 100; i++)
|
|
{
|
|
textView.Text += "\t";
|
|
}
|
|
|
|
textView.MoveEnd ();
|
|
Assert.Equal (new Point (100, 0), textView.CursorPosition);
|
|
|
|
// Navigate backward with BackTab
|
|
for (var i = 99; i >= 0; i--)
|
|
{
|
|
Assert.True (textView.NewKeyDownEvent (Key.Tab.WithShift));
|
|
Assert.Equal (new Point (i, 0), textView.CursorPosition);
|
|
}
|
|
|
|
// Navigate forward with Tab
|
|
for (var i = 1; i <= 100; i++)
|
|
{
|
|
Assert.True (textView.NewKeyDownEvent (Key.Tab));
|
|
Assert.Equal (new Point (i, 0), textView.CursorPosition);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void TabWidth_Setting_To_Zero_Keeps_AllowsTab ()
|
|
{
|
|
var textView = new TextView
|
|
{
|
|
Width = 30,
|
|
Height = 10,
|
|
Text = "TAB to jump between text fields."
|
|
};
|
|
textView.BeginInit ();
|
|
textView.EndInit ();
|
|
|
|
// Verify initial state
|
|
Assert.Equal (4, textView.TabWidth);
|
|
Assert.True (textView.AllowsTab);
|
|
Assert.True (textView.AllowsReturn);
|
|
Assert.True (textView.Multiline);
|
|
|
|
// Set TabWidth to -1 (should clamp to 0)
|
|
textView.TabWidth = -1;
|
|
Assert.Equal (0, textView.TabWidth);
|
|
Assert.True (textView.AllowsTab);
|
|
Assert.True (textView.AllowsReturn);
|
|
Assert.True (textView.Multiline);
|
|
|
|
// Insert a tab
|
|
Assert.True (textView.NewKeyDownEvent (Key.Tab));
|
|
Assert.Equal ("\tTAB to jump between text fields.", textView.Text);
|
|
|
|
// Change TabWidth back to 4
|
|
textView.TabWidth = 4;
|
|
Assert.Equal (4, textView.TabWidth);
|
|
|
|
// Remove the tab with BackTab
|
|
Assert.True (textView.NewKeyDownEvent (Key.Tab.WithShift));
|
|
Assert.Equal ("TAB to jump between text fields.", textView.Text);
|
|
}
|
|
|
|
[Fact]
|
|
public void KeyBindings_Command_Navigation ()
|
|
{
|
|
var text = "This is the first line.\nThis is the second line.\nThis is the third line.";
|
|
var textView = new TextView
|
|
{
|
|
Width = 10,
|
|
Height = 2,
|
|
Text = text
|
|
};
|
|
textView.BeginInit ();
|
|
textView.EndInit ();
|
|
|
|
Assert.Equal (
|
|
$"This is the first line.{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.",
|
|
textView.Text
|
|
);
|
|
Assert.Equal (3, textView.Lines);
|
|
Assert.Equal (Point.Empty, textView.CursorPosition);
|
|
Assert.False (textView.ReadOnly);
|
|
Assert.True (textView.CanFocus);
|
|
Assert.False (textView.IsSelecting);
|
|
|
|
// Test that CursorLeft doesn't move when at beginning
|
|
textView.CanFocus = false;
|
|
Assert.True (textView.NewKeyDownEvent (Key.CursorLeft));
|
|
Assert.False (textView.IsSelecting);
|
|
|
|
textView.CanFocus = true;
|
|
Assert.False (textView.NewKeyDownEvent (Key.CursorLeft));
|
|
Assert.False (textView.IsSelecting);
|
|
|
|
// Move right
|
|
Assert.True (textView.NewKeyDownEvent (Key.CursorRight));
|
|
Assert.Equal (new Point (1, 0), textView.CursorPosition);
|
|
Assert.False (textView.IsSelecting);
|
|
|
|
// Move to end of document
|
|
Assert.True (textView.NewKeyDownEvent (Key.End.WithCtrl));
|
|
Assert.Equal (2, textView.CurrentRow);
|
|
Assert.Equal (23, textView.CurrentColumn);
|
|
Assert.Equal (textView.CurrentColumn, textView.GetCurrentLine ().Count);
|
|
Assert.Equal (new Point (23, 2), textView.CursorPosition);
|
|
Assert.False (textView.IsSelecting);
|
|
|
|
// Try to move right (should fail, at end)
|
|
Assert.False (textView.NewKeyDownEvent (Key.CursorRight));
|
|
Assert.False (textView.IsSelecting);
|
|
|
|
// Type a character
|
|
Assert.True (textView.NewKeyDownEvent (Key.F.WithShift));
|
|
Assert.Equal (
|
|
$"This is the first line.{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.F",
|
|
textView.Text
|
|
);
|
|
Assert.Equal (new Point (24, 2), textView.CursorPosition);
|
|
Assert.False (textView.IsSelecting);
|
|
|
|
// Undo
|
|
Assert.True (textView.NewKeyDownEvent (Key.Z.WithCtrl));
|
|
Assert.Equal (
|
|
$"This is the first line.{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.",
|
|
textView.Text
|
|
);
|
|
Assert.Equal (new Point (23, 2), textView.CursorPosition);
|
|
Assert.False (textView.IsSelecting);
|
|
|
|
// Redo
|
|
Assert.True (textView.NewKeyDownEvent (Key.R.WithCtrl));
|
|
Assert.Equal (
|
|
$"This is the first line.{Environment.NewLine}This is the second line.{Environment.NewLine}This is the third line.F",
|
|
textView.Text
|
|
);
|
|
Assert.Equal (new Point (24, 2), textView.CursorPosition);
|
|
Assert.False (textView.IsSelecting);
|
|
}
|
|
|
|
[Fact]
|
|
public void UnwrappedCursorPosition_Event_Fires_Correctly ()
|
|
{
|
|
Point unwrappedPosition = Point.Empty;
|
|
|
|
var textView = new TextView
|
|
{
|
|
Width = 25,
|
|
Height = 25,
|
|
Text = "This is the first line.\nThis is the second line.\n"
|
|
};
|
|
|
|
textView.UnwrappedCursorPosition += (s, e) => { unwrappedPosition = e; };
|
|
|
|
textView.BeginInit ();
|
|
textView.EndInit ();
|
|
|
|
// Initially no word wrap
|
|
Assert.False (textView.WordWrap);
|
|
Assert.Equal (Point.Empty, textView.CursorPosition);
|
|
Assert.Equal (Point.Empty, unwrappedPosition);
|
|
|
|
// Enable word wrap and move cursor
|
|
textView.WordWrap = true;
|
|
textView.CursorPosition = new Point (12, 0);
|
|
Assert.Equal (new Point (12, 0), textView.CursorPosition);
|
|
Assert.Equal (new Point (12, 0), unwrappedPosition);
|
|
|
|
// Move right and verify unwrapped position updates
|
|
var currentUnwrapped = unwrappedPosition;
|
|
Assert.True (textView.NewKeyDownEvent (Key.CursorRight));
|
|
// The unwrapped position should have updated
|
|
Assert.True (unwrappedPosition.X >= currentUnwrapped.X);
|
|
|
|
// Move several more times to verify tracking continues to work
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
Assert.True (textView.NewKeyDownEvent (Key.CursorRight));
|
|
}
|
|
|
|
// Unwrapped position should track the actual position in the text
|
|
Assert.True (unwrappedPosition.X > 12);
|
|
}
|
|
|
|
[Fact]
|
|
public void Horizontal_Scrolling_Adjusts_LeftColumn ()
|
|
{
|
|
var textView = new TextView
|
|
{
|
|
Width = 20,
|
|
Height = 5,
|
|
Text = "This is a very long line that will require horizontal scrolling to see all of it",
|
|
WordWrap = false
|
|
};
|
|
textView.BeginInit ();
|
|
textView.EndInit ();
|
|
|
|
// Initially at the start
|
|
Assert.Equal (0, textView.LeftColumn);
|
|
Assert.Equal (new Point (0, 0), textView.CursorPosition);
|
|
|
|
// Move to the end of the line
|
|
textView.MoveEnd ();
|
|
|
|
// LeftColumn should have adjusted to show the cursor
|
|
Assert.True (textView.LeftColumn > 0);
|
|
Assert.Equal (textView.Text.Length, textView.CurrentColumn);
|
|
|
|
// Move back to the start
|
|
textView.MoveHome ();
|
|
|
|
// LeftColumn should be back to 0
|
|
Assert.Equal (0, textView.LeftColumn);
|
|
Assert.Equal (0, textView.CurrentColumn);
|
|
}
|
|
|
|
[Fact]
|
|
public void Vertical_Scrolling_Adjusts_TopRow ()
|
|
{
|
|
var lines = string.Join ("\n", Enumerable.Range (1, 100).Select (i => $"Line {i}"));
|
|
var textView = new TextView
|
|
{
|
|
Width = 20,
|
|
Height = 5,
|
|
Text = lines
|
|
};
|
|
textView.BeginInit ();
|
|
textView.EndInit ();
|
|
|
|
// Initially at the top
|
|
Assert.Equal (0, textView.TopRow);
|
|
Assert.Equal (new Point (0, 0), textView.CursorPosition);
|
|
|
|
// Move down many lines
|
|
for (var i = 0; i < 50; i++)
|
|
{
|
|
textView.NewKeyDownEvent (Key.CursorDown);
|
|
}
|
|
|
|
// TopRow should have adjusted to show the cursor
|
|
Assert.True (textView.TopRow > 0);
|
|
Assert.Equal (50, textView.CurrentRow);
|
|
|
|
// Move back to the top
|
|
textView.NewKeyDownEvent (Key.Home.WithCtrl);
|
|
|
|
// TopRow should be back to 0
|
|
Assert.Equal (0, textView.TopRow);
|
|
Assert.Equal (0, textView.CurrentRow);
|
|
}
|
|
}
|