<!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request **Root Cause:** The current pipeline builds the entire solution and runs all UI tests every time, which takes more than 2 hours to complete. **Fix** Make the PowerToys UI test pipeline provides flexible options for building and testing: ### Pipeline Options - **useLatestOfficialBuild**: When checked, downloads the latest official PowerToys build and installs it for testing. This skips the full solution build and only builds UI test projects. - **useCurrentBranchBuild**: When checked along with `useLatestOfficialBuild`, downloads the official build from the current branch instead of main. - **uiTestModules**: Specify which UI test modules to build and run. Examples: - `UITests-FancyZones` - Only FancyZones UI tests - `MouseUtils.UITests` - Only MouseUtils UI tests - `['UITests-FancyZones', 'MouseUtils.UITests']` - Multiple specific modules - Leave empty to build and run all UI test modules ### Build Modes 1. **Official Build + Selective Testing** (`useLatestOfficialBuild = true`) - Downloads and installs official PowerToys build - Builds only specified UI test projects - Runs specified UI tests against installed PowerToys - Controlled by `uiTestModules` parameter 2. **Full Build + Testing** (`useLatestOfficialBuild = false`) - Builds entire PowerToys solution - Builds UI test projects (all or specific based on `uiTestModules`) - Runs UI tests (all or specific based on `uiTestModules`) - Uses freshly built PowerToys for testing <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [ ] **Closes:** #xxx - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [x] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [x] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx <!-- Provide a more detailed description of the PR, other things fixed, or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed
6.4 KiB
UI tests framework
A specialized UI test framework for PowerToys that makes it easy to write UI tests for PowerToys modules or settings. Let's start writing UI tests!
Before running tests
-
Install Windows Application Driver v1.2.1 from https://github.com/microsoft/WinAppDriver/releases/tag/v1.2.1 to the default directory (
C:\Program Files (x86)\Windows Application Driver) -
Enable Developer Mode in Windows settings
Running tests
-
Exit PowerToys if it's running.
-
Open
PowerToys.slnin Visual Studio and build the solution. -
Run tests in the Test Explorer (
Test > Test ExplorerorCtrl+E, T).
Running tests in pipeline
The PowerToys UI test pipeline provides flexible options for building and testing:
Pipeline Options
-
useLatestOfficialBuild: When checked, downloads the latest official PowerToys build and installs it for testing. This skips the full solution build and only builds UI test projects.
-
useCurrentBranchBuild: When checked along with
useLatestOfficialBuild, downloads the official build from the current branch instead of main.Default value:
false(downloads from main branch)When to use this:
- Default scenario: The pipeline tests against the latest signed PowerToys build from the
mainbranch, regardless of which branch your test code changes are from - Custom branch testing: Only specify
truewhen:- Your branch has produced its own signed PowerToys build via the official build pipeline
- You want to test against that specific branch's PowerToys build instead of main
- You are testing PowerToys functionality changes that are only available in your branch's build
Important notes:
- The test pipeline itself runs from your specified branch, but by default tests against the main branch's PowerToys build
- Not all branches have signed builds available - only use this if you're certain your branch has a signed build
- If enabled but no build exists for your branch, the pipeline may fail or fall back to main
- Default scenario: The pipeline tests against the latest signed PowerToys build from the
-
uiTestModules: Specify which UI test modules to build and run. This parameter controls both the
.csprojprojects to build and the.dlltest assemblies to execute. Examples:['UITests-FancyZones']- Only FancyZones UI tests['MouseUtils.UITests']- Only MouseUtils UI tests['UITests-FancyZones', 'MouseUtils.UITests']- Multiple specific modules- Leave empty to build and run all UI test modules
Important: The
uiTestModulesparameter values must match both the test project names (for.csprojselection during build) and the test assembly names (for.dllexecution during testing).
Build Modes
-
Official Build + Selective Testing (
useLatestOfficialBuild = true)- Downloads and installs official PowerToys build
- Builds only specified UI test projects
- Runs specified UI tests against installed PowerToys
- Controlled by
uiTestModulesparameter
-
Full Build + Testing (
useLatestOfficialBuild = false)- Builds entire PowerToys solution
- Builds UI test projects (all or specific based on
uiTestModules) - Runs UI tests (all or specific based on
uiTestModules) - Uses freshly built PowerToys for testing
Note
: Both modes support the
uiTestModulesparameter to control which specific UI test modules to build and run.
Pipeline Access
How to add the first UI tests for your modules
-
Create a new project and add the following references to the project file. Change the OutputPath to your own module's path.
<Project Sdk="Microsoft.NET.Sdk"> <!-- Look at Directory.Build.props in root for common stuff as well --> <Import Project="..\..\..\Common.Dotnet.CsWinRT.props" /> <PropertyGroup> <ProjectGuid>{4E0AE3A4-2EE0-44D7-A2D0-8769977254A0}</ProjectGuid> <RootNamespace>PowerToys.Hosts.UITests</RootNamespace> <AssemblyName>PowerToys.Hosts.UITests</AssemblyName> <IsPackable>false</IsPackable> <IsTestProject>true</IsTestProject> <Nullable>enable</Nullable> <OutputType>Library</OutputType> <!-- This is a UI test, so don't run as part of MSBuild --> <RunVSTest>false</RunVSTest> </PropertyGroup> <PropertyGroup> <OutputPath>$(SolutionDir)$(Platform)\$(Configuration)\tests\Hosts.UITests\</OutputPath> </PropertyGroup> <ItemGroup> <PackageReference Include="MSTest" /> <ProjectReference Include="..\..\..\common\UITestAutomation\UITestAutomation.csproj" /> </ItemGroup> </Project> -
Inherit your test class from UITestBase.
Set Scope: The default scope starts from the PowerToys settings UI. If you want to start from your own module, set the constructor as shown below:
Specify Scope:
[TestClass] public class HostModuleTests : UITestBase { public HostModuleTests() : base(PowerToysModule.Hosts, WindowSize.Small_Vertical) { } } -
Then you can start performing the UI operations.
Example
[TestMethod("Hosts.Basic.EmptyViewShouldWork")]
[TestCategory("Hosts File Editor #4")]
public void TestEmptyView()
{
this.CloseWarningDialog();
this.RemoveAllEntries();
// 'Add an entry' button (only show-up when list is empty) should be visible
Assert.IsTrue(this.HasOne<HyperlinkButton>("Add an entry"), "'Add an entry' button should be visible in the empty view");
VisualAssert.AreEqual(this.TestContext, this.Find("Entries"), "EmptyView");
// Click 'Add an entry' from empty-view for adding Host override rule
this.Find<HyperlinkButton>("Add an entry").Click();
this.AddEntry("192.168.0.1", "localhost", false, false);
// Should have one row now and not more empty view
Assert.IsTrue(this.Has<Button>("Delete"), "Should have one row now");
Assert.IsFalse(this.Has<HyperlinkButton>("Add an entry"), "'Add an entry' button should be invisible if not empty view");
VisualAssert.AreEqual(this.TestContext, this.Find("Entries"), "NonEmptyView");
}
Extra tools and information
Accessibility Tools: While working on tests, you may need a tool that helps you to view the element's accessibility data, e.g. for finding the button to click. For this purpose, you could use AccessibilityInsights.