Add fixture to enable existing tests for screen reader mode

Tests that depend on supported features such as menu completions, list
view, inline predictions, continuation prompt, and colors are skipped.

The "helper" module for running tests from the command-line and in
AppVeyor is minimally updated to not skip the new fixture.

A `BlankRestOfBuffer()` function implements the semantics of the `0J`
control sequence in the mock console.
This commit is contained in:
Andy Jordan
2025-08-27 17:46:47 -07:00
parent 7b6f602405
commit 06e3b4bcb6
11 changed files with 158 additions and 6 deletions
+18
View File
@@ -177,6 +177,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_Navigation1()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// Test 'RightArrow' and 'LeftArrow' with the following menu:
// Get-Many0 Get-Many3 Get-Many6 Get-Many9 Get-Many12
// Get-Many1 Get-Many4 Get-Many7 Get-Many10 Get-Many13
@@ -304,6 +306,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_Navigation2()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// Test 'RightArrow' with the following menu:
// Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12
// Get-Less1 Get-Less4 Get-Less7 Get-Less10
@@ -387,6 +391,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_Navigation3()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// Test 'LeftArrow' with the following menu:
// Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12
// Get-Less1 Get-Less4 Get-Less7 Get-Less10
@@ -462,6 +468,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_Navigation4()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// Test 'UpArrow' and 'DownArrow' with the following menu:
// Get-Less0 Get-Less3 Get-Less6 Get-Less9 Get-Less12
// Get-Less1 Get-Less4 Get-Less7 Get-Less10
@@ -633,6 +641,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_Navigation6()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// Test 'UpArrow', 'DownArrow', 'LeftArrow', and 'RightArrow' with the following menu:
// Get-NewDynamicParameters Get-NewStyle
// Get-NewIdea
@@ -736,6 +746,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_Navigation7()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// Trigger the menu completion from the last line in the screen buffer, which will cause the screen
// to scroll up. Then test 'DownArrow' and 'UpArrow' with the following menu to verify if scrolling
// was handled correctly:
@@ -968,6 +980,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_WorkWithListView()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
int listWidth = CheckWindowSize();
@@ -1032,6 +1046,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_HandleScrolling1()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// This test case covers the fix to https://github.com/PowerShell/PSReadLine/issues/2928.
var basicScrollingConsole = new BasicScrollingConsole(keyboardLayout: _, width: 133, height: 10);
TestSetup(basicScrollingConsole, KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
@@ -1127,6 +1143,8 @@ namespace Test
[SkippableFact]
public void MenuCompletions_HandleScrolling2()
{
Skip.If(ScreenReaderModeEnabled, "Menu completions are not supported in screen reader mode.");
// This test case covers the fix to https://github.com/PowerShell/PSReadLine/issues/2948.
var basicScrollingConsole = new BasicScrollingConsole(keyboardLayout: _, width: 133, height: 10);
TestSetup(basicScrollingConsole, KeyMode.Cmd, new KeyHandler("Ctrl+Spacebar", PSConsoleReadLine.MenuComplete));
+22
View File
@@ -14,6 +14,8 @@ namespace Test
[SkippableFact]
public void Inline_RenderSuggestion()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Cmd,
new KeyHandler("Ctrl+f", PSConsoleReadLine.ForwardWord));
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
@@ -92,6 +94,8 @@ namespace Test
[SkippableFact]
public void Inline_CustomKeyBindingsToAcceptSuggestion()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Cmd,
new KeyHandler("Alt+g", PSConsoleReadLine.AcceptSuggestion),
new KeyHandler("Alt+f", PSConsoleReadLine.AcceptNextSuggestionWord));
@@ -156,6 +160,8 @@ namespace Test
[SkippableFact]
public void Inline_AcceptNextSuggestionWordCanAcceptMoreThanOneWords()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Cmd,
new KeyHandler("Ctrl+f", PSConsoleReadLine.ForwardWord),
new KeyHandler("Alt+f", PSConsoleReadLine.AcceptNextSuggestionWord));
@@ -192,6 +198,8 @@ namespace Test
[SkippableFact]
public void Inline_AcceptSuggestionWithSelection()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Cmd,
new KeyHandler("Ctrl+f", PSConsoleReadLine.ForwardWord));
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
@@ -261,6 +269,8 @@ namespace Test
[SkippableFact]
public void Inline_SetPredictionColor()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
var predictionColor = MakeCombinedColor(ConsoleColor.DarkYellow, ConsoleColor.Yellow);
var predictionColorToCheck = Tuple.Create(ConsoleColor.DarkYellow, ConsoleColor.Yellow);
@@ -288,6 +298,8 @@ namespace Test
[SkippableFact]
public void Inline_HistoryEditsCanUndoProperly()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Cmd,
new KeyHandler("Ctrl+f", PSConsoleReadLine.ForwardWord));
SetHistory("git checkout -b branch origin/bbbb");
@@ -320,6 +332,8 @@ namespace Test
[SkippableFact]
public void Inline_AcceptSuggestionInVIMode()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Vi);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
@@ -367,6 +381,8 @@ namespace Test
[SkippableFact]
public void ViDefect2408()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(KeyMode.Vi);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
@@ -460,6 +476,8 @@ namespace Test
[SkippableFact]
public void Inline_PluginSource_Acceptance()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
// Using the 'Plugin' source will make PSReadLine get prediction from the plugin only.
TestSetup(KeyMode.Cmd,
new KeyHandler("Ctrl+f", PSConsoleReadLine.ForwardWord));
@@ -534,6 +552,8 @@ namespace Test
[SkippableFact]
public void Inline_HistoryAndPluginSource_Acceptance()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
// Using the 'HistoryAndPlugin' source will make PSReadLine get prediction from the plugin and history,
// and plugin takes precedence.
TestSetup(KeyMode.Cmd,
@@ -798,6 +818,8 @@ namespace Test
[SkippableFact]
public void Inline_TruncateVeryLongSuggestion()
{
Skip.If(ScreenReaderModeEnabled, "Inline predictions are not supported in screen reader mode.");
TestSetup(new TestConsole(keyboardLayout: _, width: 10, height: 2), KeyMode.Cmd);
using var disp = SetPrediction(PredictionSource.History, PredictionViewStyle.InlineView);
+2
View File
@@ -672,6 +672,8 @@ namespace Test
[SkippableFact]
public void SelectCommandArgument_HereStringArgs()
{
Skip.If(ScreenReaderModeEnabled, "We're still investigating exactly why this test fails in screen reader mode.");
TestSetup(KeyMode.Cmd);
var continuationPrompt = PSConsoleReadLine.GetOptions().ContinuationPrompt;
+22
View File
@@ -96,6 +96,8 @@ namespace Test
[SkippableFact]
public void List_RenderSuggestion_ListUpdatesWhileTyping()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
// The font effect sequences of the dimmed color used in list view metadata line
@@ -184,6 +186,8 @@ namespace Test
[SkippableFact]
public void List_RenderSuggestion_NavigateInList_DefaultUpArrowDownArrow()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -401,6 +405,8 @@ namespace Test
[SkippableFact]
public void List_RenderSuggestion_NavigateInList_HistorySearchBackwardForward()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd,
new KeyHandler("Ctrl+p", PSConsoleReadLine.HistorySearchBackward),
new KeyHandler("Ctrl+l", PSConsoleReadLine.HistorySearchForward));
@@ -620,6 +626,8 @@ namespace Test
[SkippableFact]
public void List_RenderSuggestion_Escape()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -783,6 +791,8 @@ namespace Test
[SkippableFact]
public void List_RenderSuggestion_DigitArgument()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -943,6 +953,8 @@ namespace Test
[SkippableFact]
public void List_RenderSuggestion_CtrlZ()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -1076,6 +1088,8 @@ namespace Test
[SkippableFact]
public void List_RenderSuggestion_Selection()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -1276,6 +1290,8 @@ namespace Test
[SkippableFact]
public void List_HistorySource_NoAcceptanceCallback()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -1340,6 +1356,8 @@ namespace Test
[SkippableFact]
public void List_PluginSource_Acceptance()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -1661,6 +1679,8 @@ namespace Test
[SkippableFact]
public void List_HistoryAndPluginSource_Acceptance()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
@@ -1994,6 +2014,8 @@ namespace Test
[SkippableFact]
public void List_HistoryAndPluginSource_Deduplication()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
int listWidth = CheckWindowSize();
var dimmedColors = Tuple.Create(ConsoleColor.White, _console.BackgroundColor);
+6
View File
@@ -9,6 +9,8 @@ namespace Test
[SkippableFact]
public void List_MetaLine_And_Paging_Navigation()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
int listWidth = 100;
TestSetup(new TestConsole(keyboardLayout: _, width: listWidth, height: 15), KeyMode.Cmd);
@@ -514,6 +516,8 @@ namespace Test
[SkippableFact]
public void ListView_AdapteTo_ConsoleSize()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
// Console size is very small (h: 6, w: 50), and thus the list view will adjust to use 3-line height,
// and the metadata line will be reduced to only show the (index/total) info.
int listWidth = 50;
@@ -850,6 +854,8 @@ namespace Test
[SkippableFact]
public void ListView_TermSize_Warning()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
// Console size is very small (h: 6, w: 50), and thus the list view will adjust to use 3-line height,
// and the metadata line will be reduced to only show the (index/total) info.
int listWidth = 40;
+6
View File
@@ -9,6 +9,8 @@ namespace Test
[SkippableFact]
public void List_Item_Tooltip_4_Lines()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
// Set the terminal height to 22 and width to 60, so the metadata line will be fully rendered
// and maximum 4 lines can be used for tooltip for a selected list item.
int listWidth = 60;
@@ -206,6 +208,8 @@ namespace Test
[SkippableFact]
public void List_Item_Tooltip_2_Lines()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
// Set the terminal height to 15 and width to 60, so the metadata line will be fully rendered
// and maximum 2 lines can be used for tooltip for a selected list item.
int listWidth = 60;
@@ -363,6 +367,8 @@ namespace Test
[SkippableFact]
public void List_Item_Tooltip_1_Line()
{
Skip.If(ScreenReaderModeEnabled, "List view is not supported in screen reader mode.");
// Set the terminal height to 6 and width to 60, so the metadata line will be fully rendered
// and maximum 2 lines can be used for tooltip for a selected list item.
int listWidth = 60;
+25 -1
View File
@@ -300,6 +300,17 @@ namespace Test
buffer[writePos + i].ForegroundColor = ForegroundColor;
}
}
public virtual void BlankRestOfBuffer()
{
var writePos = CursorTop * BufferWidth + CursorLeft;
for (; writePos < buffer.Length; writePos++)
{
buffer[writePos].UnicodeChar = ' ';
buffer[writePos].BackgroundColor = BackgroundColor;
buffer[writePos].ForegroundColor = ForegroundColor;
}
}
public virtual void Clear()
{
@@ -334,6 +345,7 @@ namespace Test
c.BackgroundColor = (ConsoleColor)((int)c.BackgroundColor ^ 7);
c._negative = b;
}
protected static readonly Dictionary<string, Action<TestConsole>> EscapeSequenceActions = new()
{
{"7", c => ToggleNegative(c, true) },
@@ -376,7 +388,8 @@ namespace Test
c.ForegroundColor = DefaultForeground;
c.BackgroundColor = DefaultBackground;
}},
{"2J", c => c.SetCursorPosition(0, 0) }
{ "0J", c => c.BlankRestOfBuffer() },
{ "2J", c => c.SetCursorPosition(0, 0) },
};
}
@@ -520,6 +533,17 @@ namespace Test
}
}
public override void BlankRestOfBuffer()
{
var writePos = (_offset + CursorTop) * BufferWidth + CursorLeft;
for (; writePos < buffer.Length; writePos++)
{
buffer[writePos].UnicodeChar = ' ';
buffer[writePos].BackgroundColor = BackgroundColor;
buffer[writePos].ForegroundColor = ForegroundColor;
}
}
public override void Clear()
{
_offset = 0;
+16
View File
@@ -11,6 +11,8 @@ namespace Test
[SkippableFact]
public void ContinuationPrompt()
{
Skip.If(ScreenReaderModeEnabled, "Continuation prompt is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
Test("", Keys(
@@ -55,6 +57,20 @@ namespace Test
));
}
[SkippableFact]
public void ContinuationPromptForScreenReader()
{
Skip.IfNot(ScreenReaderModeEnabled);
TestSetup(KeyMode.Cmd);
Test("", Keys(
"{\n}",
CheckThat(() => AssertScreenIs(2, TokenClassification.None, '{', NextLine, '}' )),
_.Ctrl_c,
InputAcceptedNow
));
}
[SkippableFact]
public void GetKeyHandlers()
{
+10
View File
@@ -205,6 +205,8 @@ namespace Test
[SkippableFact]
public void MultiLine_ScreenCheck()
{
Skip.If(ScreenReaderModeEnabled, "Continuation prompt is not supported in screen reader mode.");
TestSetup(KeyMode.Cmd);
var defaultContinuationPrompt = PSConsoleReadLineOptions.DefaultContinuationPrompt;
@@ -305,6 +307,14 @@ namespace Test
CheckThat(() => AssertScreenIs(1,
Tuple.Create(_console.ForegroundColor, _console.BackgroundColor), "PSREADLINE> ",
TokenClassification.Command, "dir"))));
}
[SkippableFact]
public void InvokeTrickyPrompt()
{
Skip.If(ScreenReaderModeEnabled, "We can't test the colors written in this prompt in screen reader mode.");
TestSetup(KeyMode.Cmd, new KeyHandler("Ctrl+z", PSConsoleReadLine.InvokePrompt));
// Tricky prompt - writes to console directly with colors, uses ^H trick to eliminate trailing space.
using (var ps = PowerShell.Create(RunspaceMode.CurrentRunspace))
+22 -3
View File
@@ -130,6 +130,7 @@ namespace Test
internal virtual bool KeyboardHasGreaterThan => true;
internal virtual bool KeyboardHasCtrlRBracket => true;
internal virtual bool KeyboardHasCtrlAt => true;
internal virtual bool ScreenReaderModeEnabled => false;
internal int ContinuationPromptLength => _continuationPromptLength;
@@ -446,8 +447,13 @@ namespace Test
// that shouldn't be and aren't ever set by any code in PSReadLine, so we'll
// ignore those bits and just check the stuff we do set.
Assert.Equal(expectedBuffer[i].UnicodeChar, consoleBuffer[i].UnicodeChar);
Assert.Equal(expectedBuffer[i].ForegroundColor, consoleBuffer[i].ForegroundColor);
Assert.Equal(expectedBuffer[i].BackgroundColor, consoleBuffer[i].BackgroundColor);
if (!ScreenReaderModeEnabled)
{
// Changing colors is not supported in screen reader mode,
// and this is the simplest way to disable checking that in all the tests.
Assert.Equal(expectedBuffer[i].ForegroundColor, consoleBuffer[i].ForegroundColor);
Assert.Equal(expectedBuffer[i].BackgroundColor, consoleBuffer[i].BackgroundColor);
}
}
}
@@ -585,6 +591,7 @@ namespace Test
ContinuationPrompt = PSConsoleReadLineOptions.DefaultContinuationPrompt,
DingDuration = 1, // Make tests virtually silent when they ding
DingTone = 37, // Make tests virtually silent when they ding
EnableScreenReaderMode = ScreenReaderModeEnabled,
ExtraPromptLineCount = PSConsoleReadLineOptions.DefaultExtraPromptLineCount,
HistoryNoDuplicates = PSConsoleReadLineOptions.DefaultHistoryNoDuplicates,
HistorySaveStyle = HistorySaveStyle.SaveNothing,
@@ -637,7 +644,9 @@ namespace Test
PSConsoleReadLine.SetOptions(colorOptions);
// Cache the continuation prompt length for use in tests
_continuationPromptLength = PSConsoleReadLine.GetOptions().ContinuationPrompt.Length;
_continuationPromptLength = ScreenReaderModeEnabled
? 0
: PSConsoleReadLine.GetOptions().ContinuationPrompt.Length;
if (!_oneTimeInitCompleted)
{
@@ -674,4 +683,14 @@ namespace Test
internal override bool KeyboardHasCtrlRBracket => false;
internal override bool KeyboardHasCtrlAt => false;
}
public class ScreenReader : Test.ReadLine, IClassFixture<ConsoleFixture>
{
public ScreenReader(ConsoleFixture fixture, ITestOutputHelper output)
: base(fixture, output, "en-US", "windows")
{
}
internal override bool ScreenReaderModeEnabled => true;
}
}
+9 -2
View File
@@ -210,7 +210,14 @@ function Start-TestRun
function RunXunitTestsInNewProcess ([string] $Layout, [string] $OperatingSystem)
{
$filter = "FullyQualifiedName~Test.{0}_{1}" -f ($Layout -replace '-','_'), $OperatingSystem
$filter = if ($Layout) {
Write-Log "Testing $Layout on $OperatingSystem...`n"
"FullyQualifiedName~Test.{0}_{1}" -f ($Layout -replace '-','_'), $OperatingSystem
} else {
## Today, tests for screen-reader mode only run on Windows with the 'en-US' layout.
Write-Log "Testing screen reader mode...`n"
"FullyQualifiedName~Test.ScreenReader"
}
$testResultFile = "xUnitTestResults.{0}.xml" -f $Layout
$testResultFile = Join-Path $testResultFolder $testResultFile
@@ -263,7 +270,6 @@ function Start-TestRun
{
if (Test-Path "KeyInfo-${layout}-windows.json")
{
Write-Log "Testing $layout ..."
$null = [KeyboardLayoutHelper]::SetKeyboardLayout($layout)
# We have to use Start-Process so it creates a new window, because the keyboard
@@ -283,6 +289,7 @@ function Start-TestRun
$null = [KeyboardLayoutHelper]::SetKeyboardLayout($savedLayout)
}
}
RunXunitTestsInNewProcess
}
else
{