Compare commits

...

2 Commits

Author SHA1 Message Date
Carlos Zamora
a40be88466 remove unnecessary automation ids 2023-10-02 13:13:21 -07:00
Carlos Zamora
56a4445fb9 Add tests for Accessibility Insights' FastPass 2023-09-28 15:25:00 -07:00
5 changed files with 419 additions and 5 deletions

View File

@@ -0,0 +1,358 @@
//----------------------------------------------------------------------------------------------------------------------
// <copyright file="AccessibilityTests.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
// </copyright>
// <summary>UI Automation tests for Axe.Windows to perform an accessibility pass on different scenarios.</summary>
//----------------------------------------------------------------------------------------------------------------------
namespace WindowsTerminal.UIA.Tests
{
using Axe.Windows.Automation;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using System.Linq;
using WEX.TestExecution.Markup;
using WEX.Logging.Interop;
using WindowsTerminal.UIA.Tests.Common;
using WindowsTerminal.UIA.Tests.Elements;
using WEX.TestExecution;
using OpenQA.Selenium.Interactions;
using System.Runtime.CompilerServices;
[TestClass]
public class AccessibilityTests
{
public TestContext TestContext { get; set; }
private AppiumWebElement OpenSUIPage(TerminalApp app, string elementName)
{
var root = app.GetRoot();
root.SendKeys(Keys.LeftControl + ",");
Globals.WaitForTimeout();
// Navigate to the settings page
root.FindElementByName(elementName).Click();
Globals.WaitForTimeout();
return root;
}
private AppiumWebElement OpenTabContextMenu(TerminalApp app)
{
var root = app.GetRoot();
// Open tab context menu
var elem = root.FindElementByClassName("ListViewItem");
new Actions(app.Session).ContextClick(elem).Perform();
Globals.WaitForTimeout();
return root;
}
private IScanner BuildScanner(int processId)
{
// Output Directory: bin\x64\Debug\AxeWindowsOutputFiles
var builder = Config.Builder.ForProcessId(processId);
builder.WithOutputFileFormat(OutputFileFormat.A11yTest);
builder.WithOutputDirectory(TestContext.TestRunDirectory);
var config = builder.Build();
return ScannerFactory.CreateScanner(config);
}
private int ScanForErrors(TerminalApp app, IScanner scanner = null)
{
// Use the provided scanner if one is provided
scanner = scanner == null ? BuildScanner(app.ProcessId) : scanner;
var scanOutput = scanner.Scan(null);
var errorCount = scanOutput.WindowScanOutputs.First().ErrorCount;
Log.Comment($"Errors Found: {errorCount}");
return errorCount;
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunSUIStartup()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
OpenSUIPage(app, "Startup");
var errorCount = ScanForErrors(app);
Verify.AreEqual(errorCount, 0);
}
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunSUIInteraction()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
OpenSUIPage(app, "Interaction");
var errorCount = ScanForErrors(app);
Verify.AreEqual(errorCount, 0);
}
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunSUIAppearance()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
OpenSUIPage(app, "Appearance");
var errorCount = ScanForErrors(app);
Verify.AreEqual(errorCount, 0);
}
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunSUIColorSchemes()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
int totalErrorCount = 0;
var root = OpenSUIPage(app, "Color Schemes");
var scanner = BuildScanner(app.ProcessId);
totalErrorCount += ScanForErrors(app, scanner);
// Navigate to the Campbell scheme and scan it
root.FindElementByName("Campbell (default)").Click();
Globals.WaitForTimeout();
totalErrorCount += ScanForErrors(app, scanner);
Globals.WaitForLongTimeout();
Verify.AreEqual(totalErrorCount, 0);
}
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunSUIRendering()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
OpenSUIPage(app, "Rendering");
var errorCount = ScanForErrors(app);
Verify.AreEqual(errorCount, 0);
}
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunSUIActions()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
OpenSUIPage(app, "Actions");
var errorCount = ScanForErrors(app);
Verify.AreEqual(errorCount, 0);
}
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunSUIBaseLayer()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
int totalErrorCount = 0;
var root = OpenSUIPage(app, "Defaults");
var scanner = BuildScanner(app.ProcessId);
totalErrorCount += ScanForErrors(app, scanner);
// Navigate to the Appearance page and scan it
root.FindElementByName("Appearance").Click();
Globals.WaitForTimeout();
totalErrorCount += ScanForErrors(app, scanner);
// Navigate back to the Advanced page and scan it
root.FindElementByName("Defaults").Click();
Globals.WaitForTimeout();
root.FindElementByName("Advanced").Click();
Globals.WaitForTimeout();
totalErrorCount += ScanForErrors(app, scanner);
// Error expected: default icon is a unicode symbol that is not readable by a screen reader
Verify.AreEqual(totalErrorCount, 1);
}
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunSUIAddProfile()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
OpenSUIPage(app, "Add a new profile");
var errorCount = ScanForErrors(app);
Verify.AreEqual(errorCount, 0);
}
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunNewTabFlyout()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
var root = app.GetRoot();
// Open dropdown
root.SendKeys(Keys.Control + Keys.Shift + Keys.Space);
Globals.WaitForTimeout();
// Error expected: the flyout menu itself has no name
var errorCount = ScanForErrors(app);
Verify.AreEqual(errorCount, 1);
}
}
public void RunAboutMenu()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
var root = app.GetRoot();
// Open dropdown
root.FindElementByName("More Options").Click();
Globals.WaitForTimeout();
// Open About menu
root.FindElementByName("About").Click();
Globals.WaitForTimeout();
var errorCount = ScanForErrors(app);
Verify.AreEqual(errorCount, 0);
}
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunCommandPalette()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
var root = app.GetRoot();
// Open Command Palette
root.SendKeys(Keys.LeftControl + Keys.Shift + "P");
var errorCount = ScanForErrors(app);
Verify.AreEqual(errorCount, 0);
}
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunTabContextMenu()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
var root = app.GetRoot();
// Open a new tab so that the "close" nested menu items are enabled
root.SendKeys(Keys.Control + Keys.Shift + "T");
root = OpenTabContextMenu(app);
// Expand the "Close..." nested menu
root.FindElementByName("Close...").Click();
Globals.WaitForTimeout();
// Errors expected: the flyout menu itself (and the nested menu) has no name
var errorCount = ScanForErrors(app);
Verify.AreEqual(errorCount, 2);
}
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunColorTabMenu()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
var root = app.GetRoot();
// Open color tab menu
root = OpenTabContextMenu(app);
root.FindElementByName("Color...").Click();
Globals.WaitForTimeout();
// Open custom sub menu
root.FindElementByName("Custom...").Click();
Globals.WaitForTimeout();
// Expand more options in Custom sub menu
root.FindElementByName("More").Click();
Globals.WaitForTimeout();
var errorCount = ScanForErrors(app);
Verify.AreEqual(errorCount, 0);
}
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunRenameTab()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
var root = app.GetRoot();
// Begin renaming tab
root = OpenTabContextMenu(app);
root.FindElementByName("Rename Tab").Click();
Globals.WaitForTimeout();
// Error expected: the text box itself has no name
var errorCount = ScanForErrors(app);
Verify.AreEqual(errorCount, 1);
}
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunFind()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
var root = app.GetRoot();
// Open search box
root.SendKeys(Keys.Control + Keys.Shift + "F");
Globals.WaitForTimeout();
var errorCount = ScanForErrors(app);
Verify.AreEqual(errorCount, 0);
}
}
[TestMethod]
[TestProperty("A11yInsights", "true")]
public void RunTerminalContextMenu()
{
using (TerminalApp app = new TerminalApp(TestContext))
{
var root = app.GetRoot();
// Open command palette and find the context menu action
root.SendKeys(Keys.Control + Keys.Shift + "P");
root.SendKeys("Show context menu");
root.FindElementByName("Show context menu").Click();
Globals.WaitForTimeout();
var errorCount = ScanForErrors(app);
Verify.AreEqual(errorCount, 0);
}
}
}
}

View File

@@ -34,6 +34,7 @@ namespace WindowsTerminal.UIA.Tests.Elements
public IOSDriver<IOSElement> Session { get; private set; }
public Actions Actions { get; private set; }
public AppiumWebElement UIRoot { get; private set; }
public int ProcessId { get; private set; }
private bool isDisposed = false;
@@ -137,7 +138,8 @@ namespace WindowsTerminal.UIA.Tests.Elements
out pi),
"Attempting to create child host window process.");
Log.Comment($"Host window PID: {pi.dwProcessId}");
ProcessId = pi.dwProcessId;
Log.Comment($"Host window PID: {ProcessId}");
NativeMethods.Win32BoolHelper(WinBase.AssignProcessToJobObject(job, pi.hProcess), "Assigning new host window (suspended) to job object.");
NativeMethods.Win32BoolHelper(-1 != WinBase.ResumeThread(pi.hThread), "Resume host window process now that it is attached and its launch of the child application will be caught in the job object.");

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{F19DACD5-0C6E-40DC-B6E4-767A3200542C}</ProjectGuid>
@@ -50,14 +50,56 @@
<Reference Include="appium-dotnet-driver, Version=3.0.0.2, Culture=neutral, processorArchitecture=MSIL">
<HintPath>$(AppiumWebDriverPathRoot)\lib\net45\appium-dotnet-driver.dll</HintPath>
</Reference>
<Reference Include="Axe.Windows.Actions, Version=2.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Axe.Windows.2.1.3\lib\netstandard20\Axe.Windows.Actions.dll</HintPath>
</Reference>
<Reference Include="Axe.Windows.Automation, Version=2.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Axe.Windows.2.1.3\lib\netstandard20\Axe.Windows.Automation.dll</HintPath>
</Reference>
<Reference Include="Axe.Windows.Core, Version=2.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Axe.Windows.2.1.3\lib\netstandard20\Axe.Windows.Core.dll</HintPath>
</Reference>
<Reference Include="Axe.Windows.Desktop, Version=2.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Axe.Windows.2.1.3\lib\netstandard20\Axe.Windows.Desktop.dll</HintPath>
</Reference>
<Reference Include="Axe.Windows.Rules, Version=2.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Axe.Windows.2.1.3\lib\netstandard20\Axe.Windows.Rules.dll</HintPath>
</Reference>
<Reference Include="Axe.Windows.RuleSelection, Version=2.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Axe.Windows.2.1.3\lib\netstandard20\Axe.Windows.RuleSelection.dll</HintPath>
</Reference>
<Reference Include="Axe.Windows.SystemAbstractions, Version=2.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Axe.Windows.2.1.3\lib\netstandard20\Axe.Windows.SystemAbstractions.dll</HintPath>
</Reference>
<Reference Include="Axe.Windows.Telemetry, Version=2.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Axe.Windows.2.1.3\lib\netstandard20\Axe.Windows.Telemetry.dll</HintPath>
</Reference>
<Reference Include="Axe.Windows.Win32, Version=2.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Axe.Windows.2.1.3\lib\netstandard20\Axe.Windows.Win32.dll</HintPath>
</Reference>
<Reference Include="Castle.Core, Version=4.1.1.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>$(CastleCorePathRoot)\lib\net45\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Win32.Registry, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\Microsoft.Win32.Registry.5.0.0\lib\net461\Microsoft.Win32.Registry.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>$(NewtonsoftJSONPathRoot)\lib\net45\Newtonsoft.Json.dll</HintPath>
<HintPath>..\..\..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.Drawing.Common, Version=7.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\System.Drawing.Common.7.0.0\lib\net462\System.Drawing.Common.dll</HintPath>
</Reference>
<Reference Include="System.IO.Packaging, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\System.IO.Packaging.7.0.0\lib\net462\System.IO.Packaging.dll</HintPath>
</Reference>
<Reference Include="System.Security.AccessControl, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\System.Security.AccessControl.5.0.0\lib\net461\System.Security.AccessControl.dll</HintPath>
</Reference>
<Reference Include="System.Security.Principal.Windows, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\..\..\packages\System.Security.Principal.Windows.5.0.0\lib\net461\System.Security.Principal.Windows.dll</HintPath>
</Reference>
<Reference Include="TE.Managed, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>$(TAEFPackagePathRoot)\lib\net45\TE.Managed.dll</HintPath>
</Reference>
@@ -93,6 +135,7 @@
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="AccessibilityTests.cs" />
<Compile Include="Common\Globals.cs" />
<Compile Include="Common\NativeMethods.cs" />
<Compile Include="Common\PgoManager.cs" />
@@ -102,6 +145,7 @@
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
@@ -13,6 +13,6 @@
</assemblyBinding>
</runtime>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
</configuration>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Axe.Windows" version="2.1.3" targetFramework="net48" />
<package id="Microsoft.Win32.Registry" version="5.0.0" targetFramework="net48" />
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
<package id="System.Drawing.Common" version="7.0.0" targetFramework="net48" />
<package id="System.IO.Packaging" version="7.0.0" targetFramework="net48" />
<package id="System.Security.AccessControl" version="5.0.0" targetFramework="net48" />
<package id="System.Security.Principal.Windows" version="5.0.0" targetFramework="net48" />
</packages>