mirror of
https://github.com/PowerShell/PSReadLine
synced 2026-06-08 12:12:13 +00:00
468 lines
19 KiB
C#
468 lines
19 KiB
C#
using Microsoft.PowerShell;
|
|
using System;
|
|
using System.Linq;
|
|
using System.Runtime.InteropServices;
|
|
using Xunit;
|
|
|
|
namespace Test
|
|
{
|
|
public partial class ReadLine
|
|
{
|
|
[SkippableFact]
|
|
public void Input()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
|
|
Test("exit", Keys(
|
|
"exit",
|
|
_.Enter,
|
|
CheckThat(() => AssertCursorLeftIs(0))));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void RevertLine()
|
|
{
|
|
// Add one test for chords
|
|
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+x,Escape", PSConsoleReadLine.RevertLine));
|
|
|
|
Test("ls", Keys("di", _.Escape, "ls"));
|
|
Test("ls", Keys("di", _.Ctrl_x, _.Escape, "ls"));
|
|
|
|
TestSetup(KeyMode.Emacs);
|
|
Test("ls", Keys("di", _.Escape, _.r, "ls"));
|
|
Test("ls", Keys("di", _.Alt_r, "ls"));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void CancelLine()
|
|
{
|
|
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+C", PSConsoleReadLine.CancelLine));
|
|
|
|
Test("", Keys("oops", _.Ctrl_C,
|
|
CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, "oops",
|
|
Tuple.Create(ConsoleColor.Red, _console.BackgroundColor), "^C")),
|
|
InputAcceptedNow));
|
|
|
|
Test("", Keys("exit", _.Ctrl_C,
|
|
CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Keyword, "exit",
|
|
Tuple.Create(ConsoleColor.Red, _console.BackgroundColor), "^C")),
|
|
InputAcceptedNow));
|
|
|
|
// Test near/at/over buffer width input
|
|
var width = _console.BufferWidth;
|
|
var line1 = new string('a', width - 2);
|
|
Test("", Keys(line1, _.Ctrl_C,
|
|
CheckThat(() => AssertScreenIs(1,
|
|
TokenClassification.Command, line1,
|
|
Tuple.Create(ConsoleColor.Red, _console.BackgroundColor), "^C")),
|
|
InputAcceptedNow));
|
|
var line2 = new string('a', width - 1);
|
|
Test("", Keys(line2, _.Ctrl_C,
|
|
CheckThat(() => AssertScreenIs(2,
|
|
TokenClassification.Command, line2,
|
|
Tuple.Create(ConsoleColor.Red, _console.BackgroundColor), "^C")),
|
|
InputAcceptedNow));
|
|
var line3 = new string('a', width);
|
|
Test("", Keys(line3, _.Ctrl_C,
|
|
CheckThat(() => AssertScreenIs(2,
|
|
TokenClassification.Command, line3,
|
|
Tuple.Create(ConsoleColor.Red, _console.BackgroundColor), "^C")),
|
|
InputAcceptedNow));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void ForwardDeleteInput()
|
|
{
|
|
ConsoleKeyInfo deleteToEnd;
|
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
deleteToEnd = _.Ctrl_End;
|
|
}
|
|
else
|
|
{
|
|
TestSetup(KeyMode.Emacs);
|
|
deleteToEnd = _.Ctrl_k;
|
|
}
|
|
|
|
// Empty input (does nothing but don't crash)
|
|
Test("", Keys("", deleteToEnd));
|
|
|
|
// at end of input - doesn't change anything
|
|
Test("abc", Keys("abc", deleteToEnd));
|
|
|
|
// More normal usage - actually delete stuff
|
|
Test("a", Keys("abc", _.LeftArrow, _.LeftArrow, deleteToEnd));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void ForwardDeleteLine()
|
|
{
|
|
TestSetup(KeyMode.Emacs);
|
|
|
|
PSConsoleReadLine.SetKeyHandler(new[] { "Shift+Tab" }, PSConsoleReadLine.ForwardDeleteLine, "", "");
|
|
|
|
Test("\"H\nWorld\"", Keys(
|
|
_.DQuote, "Hello", _.Enter,
|
|
"World", _.DQuote,
|
|
_.Home, _.Home, _.RightArrow, _.RightArrow,
|
|
_.Shift_Tab // delete to the end of the current logical line
|
|
));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void BackwardDeleteInput()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
|
|
// Empty input (does nothing but don't crash)
|
|
Test("", Keys(_.Ctrl_Home));
|
|
|
|
// at beginning of input - doesn't change anything
|
|
Test("abc", Keys("abc", _.Home, _.Ctrl_Home));
|
|
|
|
// More typical usage
|
|
Test("c", Keys("abc", _.LeftArrow, _.Ctrl_Home));
|
|
Test("", Keys("abc", _.Ctrl_Home));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void BackwardDeleteChar()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
|
|
// Empty input (does nothing but don't crash)
|
|
Test("", Keys("", _.Backspace));
|
|
|
|
// At end, delete all input
|
|
Test("", Keys("a", _.Backspace));
|
|
|
|
// At end, delete all input with extra backspaces
|
|
Test("", Keys("a", _.Backspace, _.Backspace));
|
|
|
|
// Delete first character
|
|
Test("b", Keys("ab", _.LeftArrow, _.Backspace));
|
|
|
|
// Delete first character with extra backspaces
|
|
Test("b", Keys("ab", _.LeftArrow, _.Backspace, _.Backspace));
|
|
|
|
// Delete middle character
|
|
Test("ac", Keys("abc", _.LeftArrow, _.Backspace));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void DeleteChar()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
|
|
// Empty input (does nothing, but don't crash)
|
|
Test("", Keys(_.Delete));
|
|
|
|
// At end but input not empty (does nothing, but don't crash)
|
|
Test("a", Keys('a', _.Delete));
|
|
|
|
// Delete last character
|
|
Test("a", Keys("ab", _.LeftArrow, _.Delete));
|
|
|
|
// Delete first character
|
|
Test("b", Keys("ab", _.Home, _.Delete));
|
|
|
|
// Delete middle character
|
|
Test("ac", Keys("abc", _.Home, _.RightArrow, _.Delete));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void DeleteCharAfterDigitArgument()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
|
|
Test("abc", Keys(
|
|
"ab", _.LeftArrow, _.Alt_2, _.Delete,
|
|
CheckThat(() => AssertLineIs("a")),
|
|
CheckThat(() => AssertCursorLeftIs(1)),
|
|
_.Delete, // 'Delete' again does nothing, but doesn't crash
|
|
CheckThat(() => AssertLineIs("a")),
|
|
CheckThat(() => AssertCursorLeftIs(1)),
|
|
"bc"));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void DeleteCharOrExit()
|
|
{
|
|
TestSetup(KeyMode.Emacs);
|
|
|
|
Test("exit", Keys(_.Ctrl_d, InputAcceptedNow));
|
|
|
|
Test("foo", Keys("foo", _.Ctrl_d));
|
|
Test("oo", Keys("foo", _.Home, _.Ctrl_d));
|
|
Test("exit", Keys("foo", _.Home, Enumerable.Repeat(_.Ctrl_d, 4), InputAcceptedNow));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void UpcaseWord()
|
|
{
|
|
TestSetup(KeyMode.Emacs);
|
|
Test("FOO", Keys("foo", _.Alt_b, _.Alt_u));
|
|
Test("FOO bar", Keys("foo bar", _.Home, _.Alt_u));
|
|
Test("foo BAR", Keys("foo bar", _.Alt_b, _.Alt_u));
|
|
Test("FOO BAR", Keys("foo bar", _.Home, Enumerable.Repeat(_.Alt_u, 2)));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void DowncaseWord()
|
|
{
|
|
TestSetup(KeyMode.Emacs);
|
|
Test("foo", Keys("fOO", _.Alt_b, _.Alt_l));
|
|
Test("FOO bar", Keys("FOO BAR", _.Alt_b, _.Alt_l));
|
|
Test("foo BAR", Keys("FOO BAR", _.Home, _.Alt_l));
|
|
Test("foo bar", Keys("FOO BAR", _.Home, Enumerable.Repeat(_.Alt_l, 2)));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void CapitalizeWord()
|
|
{
|
|
TestSetup(KeyMode.Emacs);
|
|
Test("Foo", Keys("fOO", _.Alt_b, _.Alt_c));
|
|
Test("fOO Bar", Keys("fOO bAR", _.Alt_b, _.Alt_c));
|
|
Test("Foo BAR", Keys("fOO BAR", _.Home, _.Alt_c));
|
|
Test("Foo Bar", Keys("fOO BAR", _.Home, Enumerable.Repeat(_.Alt_c, 2)));
|
|
Test("Foo ^&*() Bar", Keys("Foo ^&*() bar", _.Home, Enumerable.Repeat(_.Alt_c, 2)));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void SelectAndDelete()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
|
|
Test("abcde", Keys(
|
|
"abcde",
|
|
CheckThat(() => AssertCursorLeftIs(5)),
|
|
_.Shift_LeftArrow, _.Shift_LeftArrow, _.Shift_LeftArrow,
|
|
_.Backspace,
|
|
CheckThat(() => AssertLineIs("ab")),
|
|
CheckThat(() => AssertCursorLeftIs(2)),
|
|
_.Ctrl_z,
|
|
CheckThat(() => AssertLineIs("abcde")),
|
|
CheckThat(() => AssertCursorLeftIs(5))));
|
|
|
|
Test("abcde", Keys(
|
|
"abcde", _.Home,
|
|
CheckThat(() => AssertCursorLeftIs(0)),
|
|
_.RightArrow, _.RightArrow,
|
|
CheckThat(() => AssertCursorLeftIs(2)),
|
|
_.Shift_RightArrow, _.Shift_RightArrow,
|
|
_.Delete,
|
|
CheckThat(() => AssertLineIs("abe")),
|
|
CheckThat(() => AssertCursorLeftIs(2)),
|
|
_.Ctrl_z,
|
|
CheckThat(() => AssertLineIs("abcde")),
|
|
CheckThat(() => AssertCursorLeftIs(4))));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void SwapCharacters()
|
|
{
|
|
TestSetup(KeyMode.Emacs);
|
|
|
|
TestMustDing("", Keys(_.Ctrl_t));
|
|
TestMustDing("a", Keys("a", _.Ctrl_t));
|
|
TestMustDing("abc", Keys("abc", _.Home, _.Ctrl_t));
|
|
|
|
Test("abc", Keys(
|
|
"abc", CheckThat(() => AssertLineIs("abc")),
|
|
_.Ctrl_t, CheckThat(() => AssertLineIs("acb")),
|
|
_.Ctrl_Underbar, CheckThat(() => AssertCursorLeftIs(3))
|
|
));
|
|
|
|
Test("abcd", Keys(
|
|
"abcd", CheckThat(() => AssertLineIs("abcd")),
|
|
_.Ctrl_a, _.Ctrl_t, CheckThat(() => AssertLineIs("abcd")),
|
|
_.Ctrl_f, Enumerable.Repeat(_.Ctrl_t, 3), CheckThat(() => AssertLineIs("bcda")),
|
|
_.Ctrl_t, CheckThat(() => AssertLineIs("bcad")),
|
|
Enumerable.Repeat(_.Ctrl_Underbar, 4)
|
|
));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void AcceptAndGetNext()
|
|
{
|
|
TestSetup(KeyMode.Emacs);
|
|
|
|
// No history
|
|
Test("", Keys(_.Ctrl_o, InputAcceptedNow));
|
|
|
|
// One item in history
|
|
SetHistory("echo 1");
|
|
Test("", Keys(_.Ctrl_o, InputAcceptedNow));
|
|
|
|
// Two items in history, make sure after Ctrl+O, second history item
|
|
// is recalled.
|
|
SetHistory("echo 1", "echo 2");
|
|
Test("echo 1", Keys(_.UpArrow, _.UpArrow, _.Ctrl_o, InputAcceptedNow));
|
|
Test("echo 2", Keys(_.Enter));
|
|
|
|
// Test that the current saved line is saved after AcceptAndGetNext
|
|
SetHistory("echo 1", "echo 2");
|
|
Test("echo 1", Keys("e", _.UpArrow, _.UpArrow, _.Ctrl_o, InputAcceptedNow));
|
|
Test("e", Keys(_.DownArrow, _.DownArrow, _.Enter));
|
|
|
|
// Test that we can edit after recalling the current line
|
|
SetHistory("echo 1", "echo 2");
|
|
Test("echo 1", Keys("e", _.UpArrow, _.UpArrow, _.Ctrl_o, InputAcceptedNow));
|
|
Test("eee", Keys(_.DownArrow, _.DownArrow, "ee", _.Enter));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void AcceptAndGetNextWithHistorySearch()
|
|
{
|
|
TestSetup(KeyMode.Emacs,
|
|
new KeyHandler("UpArrow", PSConsoleReadLine.HistorySearchBackward),
|
|
new KeyHandler("DownArrow", PSConsoleReadLine.HistorySearchForward));
|
|
|
|
// Test that after AcceptAndGetNext, the previous search is not applied
|
|
SetHistory("echo 1", "echo 2", "zzz");
|
|
Test("echo 1", Keys("e", _.UpArrow, _.UpArrow, _.Ctrl_o, InputAcceptedNow));
|
|
Test("zzz", Keys(_.DownArrow, _.Enter));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void AddLine()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
|
|
Test("1\n2", Keys('1', _.Shift_Enter, '2'));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void InsertLineAbove()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
|
|
var continutationPromptLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
|
|
|
|
// Test case - start with single line, cursor at end
|
|
Test("56\n1234", Keys("1234", _.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(0, 0)), "56"));
|
|
|
|
// Test case - start with single line, cursor in home position
|
|
Test("56\n1234", Keys("1234", _.Home, _.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(0, 0)), "56"));
|
|
|
|
// Test case - start with single line, cursor in middle
|
|
Test("56\n1234", Keys("1234",
|
|
_.LeftArrow, _.LeftArrow, _.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(0, 0)),
|
|
"56"));
|
|
|
|
|
|
// Test case - start with multi-line, cursor at end of second line (end of input)
|
|
Test("1234\n9ABC\n5678", Keys("1234", _.Shift_Enter, "5678",
|
|
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
|
|
"9ABC"));
|
|
|
|
// Test case - start with multi-line, cursor at beginning of second line
|
|
Test("1234\n9ABC\n5678", Keys("1234", _.Shift_Enter, "5678",
|
|
_.LeftArrow, _.Home, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
|
|
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
|
|
"9ABC"));
|
|
|
|
// Test case - start with multi-line, cursor at end of first line
|
|
Test("9ABC\n1234\n5678", Keys("1234", _.Shift_Enter, "5678",
|
|
_.UpArrow, _.LeftArrow, _.End, CheckThat(() => AssertCursorLeftTopIs(4, 0)),
|
|
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(0, 0)),
|
|
"9ABC"));
|
|
|
|
// Test case - start with multi-line, cursor at beginning of first line - temporarily having to press Home twice to
|
|
// work around bug in home handler.
|
|
Test("9ABC\n1234\n5678", Keys("1234", _.Shift_Enter, "5678",
|
|
_.UpArrow, _.LeftArrow, _.Home, _.Home, CheckThat(() => AssertCursorLeftTopIs(0, 0)),
|
|
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(0, 0)),
|
|
"9ABC"));
|
|
|
|
// Test case - insert multiple blank lines
|
|
Test("1234\n9ABC\n\n5678", Keys("1234", _.Shift_Enter, "5678",
|
|
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
|
|
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
|
|
"9ABC"));
|
|
|
|
// Test case - create leading blank line, cursor to stay on same line
|
|
Test("\n\n1234", Keys("1234",
|
|
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(0,0)),
|
|
_.DownArrow, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
|
|
_.Ctrl_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1))));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void InsertLineBelow()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
|
|
var continutationPromptLength = PSConsoleReadLineOptions.DefaultContinuationPrompt.Length;
|
|
|
|
// Test case - start with single line, cursor at end
|
|
Test("1234\n56", Keys("1234",
|
|
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
|
|
"56"));
|
|
|
|
// Test case - start with single line, cursor in home position
|
|
Test("1234\n56", Keys("1234",
|
|
_.Home, _.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
|
|
"56"));
|
|
|
|
// Test case - start with single line, cursor in middle
|
|
Test("1234\n56", Keys("1234",
|
|
_.LeftArrow, _.LeftArrow, _.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
|
|
"56"));
|
|
|
|
// Test case - start with multi-line, cursor at end of second line (end of input)
|
|
Test("1234\n5678\n9ABC", Keys("1234", _.Shift_Enter, "5678",
|
|
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 2)),
|
|
"9ABC"));
|
|
|
|
// Test case - start with multi-line, cursor at beginning of second line
|
|
Test("1234\n5678\n9ABC", Keys("1234", _.Shift_Enter, "5678",
|
|
_.LeftArrow, _.Home, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
|
|
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 2)),
|
|
"9ABC"));
|
|
|
|
// Test case - start with multi-line, cursor at end of first line
|
|
Test("1234\n9ABC\n5678", Keys("1234", _.Shift_Enter, "5678",
|
|
_.UpArrow, _.LeftArrow, _.End, CheckThat(() => AssertCursorLeftTopIs(4, 0)),
|
|
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
|
|
"9ABC"));
|
|
|
|
// Test case - start with multi-line, cursor at beginning of first line - temporarily having to press Home twice to
|
|
// work around bug in home handler.
|
|
Test("1234\n9ABC\n5678", Keys("1234", _.Shift_Enter, "5678",
|
|
_.UpArrow, _.LeftArrow, _.Home, _.Home, CheckThat(() => AssertCursorLeftTopIs(0, 0)),
|
|
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 1)),
|
|
"9ABC"));
|
|
|
|
// Test case - insert multiple blank lines
|
|
Test("1234\n5678\n\n9ABC", Keys("1234", _.Shift_Enter, "5678",
|
|
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 2)),
|
|
_.Ctrl_Shift_Enter, CheckThat(() => AssertCursorLeftTopIs(continutationPromptLength, 3)),
|
|
"9ABC"));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void MultilineHomeBugFixed()
|
|
{
|
|
TestSetup(KeyMode.Cmd);
|
|
|
|
// Going from second line to first line, press left arrow and then home.
|
|
// That puts cursor in column 1 instead of 0. Bug? This could have something
|
|
// to do with BeginningOfLine testing against i > 1 in multiline edit instead
|
|
// of i > 0.
|
|
Test("1234\n9ABC", Keys("1234", _.Shift_Enter, "9ABC", _.UpArrow, _.LeftArrow, _.Home, CheckThat(() => AssertCursorLeftTopIs(0, 0))));
|
|
}
|
|
|
|
[SkippableFact]
|
|
public void Ignore()
|
|
{
|
|
TestSetup(KeyMode.Emacs);
|
|
|
|
Test("ab", Keys("a", _.VolumeDown, _.VolumeMute, _.VolumeUp, "b"));
|
|
}
|
|
}
|
|
}
|