Skip to content

Commit

Permalink
add PowerToys Module Config
Browse files Browse the repository at this point in the history
  • Loading branch information
Zhaopeng Wang (from Dev Box) committed Jan 26, 2025
1 parent d432866 commit 09d4322
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 61 deletions.
27 changes: 27 additions & 0 deletions src/UITestAPI/src/ModuleConfigData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.UITests.API
{
public enum PowerToysModule
{
None,
Fancyzone,
KeyboardManagerKeys,
KeyboardManagerShortcuts,
Hosts,
}

public struct ModuleConfigData(string moduleName, string windowName)
{
public string ModuleName = moduleName;
public string WindowName = windowName;
}
}
99 changes: 55 additions & 44 deletions src/UITestAPI/src/UITestAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,48 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;

using System.Xml.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
using static Microsoft.ApplicationInsights.MetricDimensionNames.TelemetryContext;
using static Microsoft.UITests.API.APPManager;
using static Microsoft.UITests.API.ModuleConfigData;

namespace Microsoft.UITests.API
{
public class UITestAPI
{
protected const string PowerToysPath = @"\..\..\..\WinUI3Apps\PowerToys.Settings.exe";

public Dictionary<PowerToysModule, ModuleConfigData> ModuleConfig { get; private set; }

public APPManager APPManager { get; private set; }

private static Process? appDriver;

public UITestAPI()
{
ModuleConfig = new Dictionary<PowerToysModule, ModuleConfigData>();
ModuleConfig[PowerToysModule.Fancyzone] = new ModuleConfigData("Fancyzone", "FancyZones Layout");
ModuleConfig[PowerToysModule.KeyboardManagerKeys] = new ModuleConfigData("KeyboardManagerKeys", "Remap keys");
ModuleConfig[PowerToysModule.KeyboardManagerShortcuts] = new ModuleConfigData("KeyboardManagerShortcuts", "Remap shortcuts");
ModuleConfig[PowerToysModule.Hosts] = new ModuleConfigData("Hosts", "Hosts File Editor");

APPManager = new APPManager();
}

[UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", Justification = "<Pending>")]
public void Init(string appName, string exePath, string windowName, string winAppDriverPath = "C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe")
public void Init(string winAppDriverPath = "C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe")
{
appDriver = Process.Start(winAppDriverPath);

// Launch Exe
string? path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
path += exePath;
APPManager.StartExe(appName, windowName, path);
path += PowerToysPath;
APPManager.StartExe("PowerToys", "PowerToys Settings", path);

var session = APPManager.GetCurrentWindow();
Assert.IsNotNull(session, "Session not initialized");
Expand Down Expand Up @@ -76,78 +87,78 @@ public void StartExe(string appName, string windowName, string appPath)
}

// Take control of an application that already exists
public void LaunchModule(string appName, string windowName)
public void LaunchModule(PowerToysModule module)
{
APPManager.LaunchModule(appName, windowName);
APPManager.LaunchModule(ModuleConfig[module].ModuleName, ModuleConfig[module].WindowName);
}

// Use the name to switch the current driver
public void SwitchApp(string appName)
public void SwitchModule(PowerToysModule module)
{
APPManager.SwitchApp(appName);
APPManager.SwitchApp(ModuleConfig[module].ModuleName);
}

public void CloseApp(string appName)
public void CloseModule(PowerToysModule module)
{
APPManager.CloseApp(appName);
APPManager.CloseApp(ModuleConfig[module].ModuleName);
}

public WindowsDriver<WindowsElement>? GetWindowInList(string appName)
public WindowsDriver<WindowsElement>? GetWindowInList(PowerToysModule module)
{
return APPManager.GetWindowInList(appName);
return APPManager.GetWindowInList(ModuleConfig[module].ModuleName);
}

public WindowsDriver<WindowsElement>? GetSession(string? appName = null)
public WindowsDriver<WindowsElement>? GetSession(PowerToysModule module = PowerToysModule.None)
{
if (appName == null)
if (module == PowerToysModule.None)
{
return APPManager.GetCurrentWindow();
}
else
{
return APPManager.GetWindowInList(appName);
return APPManager.GetWindowInList(ModuleConfig[module].ModuleName);
}
}

// ===================================Control API================================================
private WindowsElement? GetElement(string elementName, string? appName = null)
private WindowsElement? GetElement(string elementName, PowerToysModule module = PowerToysModule.None)
{
WindowsDriver<WindowsElement>? session = GetSession(appName);
WindowsDriver<WindowsElement>? session = GetSession(module);
var item = session?.FindElementByName(elementName);
Assert.IsNotNull(item, "ElementName " + elementName + " not found");
return item;
}

private ReadOnlyCollection<WindowsElement> GetElements(string elementName, string? appName = null)
private ReadOnlyCollection<WindowsElement> GetElements(string elementName, PowerToysModule module = PowerToysModule.None)
{
WindowsDriver<WindowsElement>? session = GetSession(appName);
WindowsDriver<WindowsElement>? session = GetSession(module);
var listItem = session?.FindElementsByName(elementName);
Assert.IsNotNull(listItem, "ElementName " + elementName + " not found");
return listItem;
}

public WindowsElement? NewOpenContextMenu(string elementName, string? appName = null)
public WindowsElement? NewOpenContextMenu(string elementName, PowerToysModule module = PowerToysModule.None)
{
WindowsDriver<WindowsElement>? session = GetSession(appName);
WindowsDriver<WindowsElement>? session = GetSession(module);
RightClick_Element(elementName);
var menu = session?.FindElementByClassName("ContextMenu");
Assert.IsNotNull(menu, "Context menu not found");
return menu;
}

public void Click_Element(string elementName, string? appName = null)
public void Click_Element(string elementName, PowerToysModule module = PowerToysModule.None)
{
WindowsDriver<WindowsElement>? session = GetSession(appName);
WindowsDriver<WindowsElement>? session = GetSession(module);
var element = GetElement(elementName);
Actions actions = new Actions(session);
actions.MoveToElement(element);
actions.Click();
actions.Build().Perform();
}

public void Click_Elements(string elementName, string? appName = null)
public void Click_Elements(string elementName, PowerToysModule module = PowerToysModule.None)
{
WindowsDriver<WindowsElement>? session = GetSession(appName);
WindowsDriver<WindowsElement>? session = GetSession(module);
var elements = GetElements(elementName);
Actions actions = new Actions(session);
foreach (var element in elements)
Expand All @@ -160,9 +171,9 @@ public void Click_Elements(string elementName, string? appName = null)
}
}

public void Click_Element(string elementName, string helpText, string? appName = null)
public void Click_Element(string elementName, string helpText, PowerToysModule module = PowerToysModule.None)
{
WindowsDriver<WindowsElement>? session = GetSession(appName);
WindowsDriver<WindowsElement>? session = GetSession(module);
var elements = GetElements(elementName);
Actions actions = new Actions(session);
bool buttonClicked = false;
Expand All @@ -182,9 +193,9 @@ public void Click_Element(string elementName, string helpText, string? appName =
Assert.IsTrue(buttonClicked, $"No button with elementName '{elementName}' and HelpText '{helpText}' was found.");
}

public void Enable_Module_from_Dashboard(string moduleName, string? appName = null)
public void Enable_Module_from_Dashboard(string moduleName, PowerToysModule module = PowerToysModule.None)
{
WindowsDriver<WindowsElement>? session = GetSession(appName);
WindowsDriver<WindowsElement>? session = GetSession(module);
var elements = GetElements("Enable module");
Actions actions = new Actions(session);
bool buttonFound = false;
Expand All @@ -208,9 +219,9 @@ public void Enable_Module_from_Dashboard(string moduleName, string? appName = nu
Assert.IsTrue(buttonFound, $"No button with elementName '{moduleName}' and HelpText '{moduleName}' was found.");
}

public void Disable_Module_from_Dashboard(string moduleName, string? appName = null)
public void Disable_Module_from_Dashboard(string moduleName, PowerToysModule module = PowerToysModule.None)
{
WindowsDriver<WindowsElement>? session = GetSession(appName);
WindowsDriver<WindowsElement>? session = GetSession(module);
var elements = GetElements("Enable module");
Actions actions = new Actions(session);
bool buttonFound = false;
Expand All @@ -234,9 +245,9 @@ public void Disable_Module_from_Dashboard(string moduleName, string? appName = n
Assert.IsTrue(buttonFound, $"No button with elementName '{moduleName}' and HelpText '{moduleName}' was found.");
}

public void RightClick_Element(string elementName, string? appName = null)
public void RightClick_Element(string elementName, PowerToysModule module = PowerToysModule.None)
{
WindowsDriver<WindowsElement>? session = GetSession(appName);
WindowsDriver<WindowsElement>? session = GetSession(module);
var element = GetElement(elementName);
Actions actions = new Actions(session);
actions.MoveToElement(element);
Expand All @@ -245,43 +256,43 @@ public void RightClick_Element(string elementName, string? appName = null)
actions.Build().Perform();
}

private WindowsElement? GetLayout(string layoutName, string? appName = null)
private WindowsElement? GetLayout(string layoutName, PowerToysModule module = PowerToysModule.None)
{
WindowsDriver<WindowsElement>? session = GetSession(appName);
WindowsDriver<WindowsElement>? session = GetSession(module);
var listItem = session?.FindElementByName(layoutName);
Assert.IsNotNull(listItem, "Layout " + layoutName + " not found");
return listItem;
}

public WindowsElement? OpenContextMenu(string layoutName, string? appName = null)
public WindowsElement? OpenContextMenu(string layoutName, PowerToysModule module = PowerToysModule.None)
{
WindowsDriver<WindowsElement>? session = GetSession(appName);
WindowsDriver<WindowsElement>? session = GetSession(module);
RightClick_Layout(layoutName);
var menu = session?.FindElementByClassName("ContextMenu");
Assert.IsNotNull(menu, "Context menu not found");
return menu;
}

public void Click_CreateNewLayout(string? appName = null)
public void Click_CreateNewLayout(PowerToysModule module = PowerToysModule.None)
{
WindowsDriver<WindowsElement>? session = GetSession(appName);
WindowsDriver<WindowsElement>? session = GetSession(module);
var button = session?.FindElementByAccessibilityId("NewLayoutButton");
Assert.IsNotNull(button, "Create new layout button not found");
button?.Click();
}

public void Click_EditLayout(string layoutName, string? appName = null)
public void Click_EditLayout(string layoutName, PowerToysModule module = PowerToysModule.None)
{
var layout = GetLayout(layoutName, appName);
var layout = GetLayout(layoutName, module);
var editButton = layout?.FindElementByAccessibilityId("EditLayoutButton");
Assert.IsNotNull(editButton, "Edit button not found");
editButton.Click();
}

public void RightClick_Layout(string layoutName, string? appName = null)
public void RightClick_Layout(string layoutName, PowerToysModule module = PowerToysModule.None)
{
WindowsDriver<WindowsElement>? session = GetSession(appName);
var layout = GetLayout(layoutName, appName);
WindowsDriver<WindowsElement>? session = GetSession(module);
var layout = GetLayout(layoutName, module);
Actions actions = new Actions(session);
actions.MoveToElement(layout);
actions.MoveByOffset(30, 30);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ namespace UITests_FancyZones
[TestClass]
public class RunFancyZonesTest
{
private const string PowerToysPath = @"\..\..\..\WinUI3Apps\PowerToys.Settings.exe";
private static UITestAPI? mUITestAPI;

private static TestContext? _context;
Expand All @@ -35,7 +34,7 @@ public static void ClassCleanup()
public void TestInitialize()
{
mUITestAPI = new UITestAPI();
mUITestAPI.Init("PowerToys.Settings", PowerToysPath, "PowerToys.Settings");
mUITestAPI.Init();
}

[TestCleanup]
Expand Down Expand Up @@ -88,9 +87,8 @@ public void RunFancyZones()

Assert.IsNotNull(mUITestAPI);
mUITestAPI.Click_Element("Launch layout editor");

Thread.Sleep(5000);
mUITestAPI.LaunchModule("PowerToys.FancyZonesEditor", "FancyZones Layout");
Thread.Sleep(4000);
mUITestAPI.LaunchModule(PowerToysModule.Fancyzone);
mUITestAPI?.Click_CreateNewLayout();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.Threading;

using FancyZonesEditorCommon.Data;
using Microsoft.FancyZonesEditor.UITests;
Expand All @@ -15,7 +16,6 @@ namespace UITests_FancyZonesEditor
[TestClass]
public class RunFancyZonesEditorTest
{
private const string FancyZonesEditorPath = @"\..\..\..\PowerToys.FancyZonesEditor.exe";
private static FancyZonesEditorFiles? _files;
private static UITestAPI? mUITestAPI;

Expand Down Expand Up @@ -142,6 +142,14 @@ public static void ClassInitialize(TestContext testContext)
AppliedLayouts = new List<AppliedLayouts.AppliedLayoutWrapper> { },
};
_files.AppliedLayoutsIOHelper.WriteData(appliedLayouts.Serialize(appliedLayoutsWrapper));

// Start Powertoys
mUITestAPI = new UITestAPI();
mUITestAPI.Init();
Assert.IsNotNull(mUITestAPI);
mUITestAPI.Click_Element("Launch layout editor");
Thread.Sleep(4000);
mUITestAPI.LaunchModule(PowerToysModule.Fancyzone);
}

[ClassCleanup]
Expand All @@ -151,24 +159,23 @@ public static void ClassCleanup()
{
_files.Restore();
}

if (mUITestAPI != null && _context != null)
{
mUITestAPI.Close(_context);
}

_context = null;
}

[TestInitialize]
public void TestInitialize()
{
mUITestAPI = new UITestAPI();
mUITestAPI.Init("PowerToys.FancyZonesEditor", FancyZonesEditorPath, "PowerToys.FancyZonesEditor");
}

[TestCleanup]
public void TestCleanup()
{
if (mUITestAPI != null && _context != null)
{
mUITestAPI.Close(_context);
}

_context = null;
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static void CleanupAll()
public static void ClassInitialize(TestContext testContext)
{
mUITestAPI = new UITestAPI();
mUITestAPI.Init("PowerToys.Settings", PowerToysSettingsPath, "PowerToys.Settings");
mUITestAPI.Init();
Debug.WriteLine("ClassInitialize executed");
}

Expand Down Expand Up @@ -72,10 +72,10 @@ public void EnableKeyboardManager() // verify the session is initialized
mUITestAPI.Enable_Module_from_Dashboard("Keyboard Manager");
mUITestAPI.Click_Element("Remap a key");
Thread.Sleep(5000);
mUITestAPI.LaunchModule("PowerToys.KeyboardManagerEditor", "Remap keys");
mUITestAPI.LaunchModule(PowerToysModule.KeyboardManagerKeys);
mUITestAPI.Click_Element("Add key remapping");
mUITestAPI.Click_Element("Cancel");
mUITestAPI.CloseApp("PowerToys.KeyboardManagerEditor");
mUITestAPI.CloseModule(PowerToysModule.KeyboardManagerKeys);
mUITestAPI.Disable_Module_from_Dashboard("Keyboard Manager");
}
}
Expand Down

1 comment on commit 09d4322

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@check-spelling-bot Report

🔴 Please review

See the 📜action log or 📝 job summary for details.

Unrecognized words (1)

Fancyzone

These words are not needed and should be removed accctrl aclapi AMPROPERTY AMPROPSETID appdata Appium appmodel atlbase atlcom atlfile atlstr bootstrapper caniuse CDEF ceq cguid Cmds cne codicon comdef commandline commctrl commdlg comutil consts contentdialog cppwinrt CRSEL crx dcommon dcomp DCs ddf desktopwindowxamlsource devenum DEVMON devpkey DEVSOURCE DIIRFLAG dshow DVH DVHD DVSD DVSL dxgidebug dxgiformat EData emmintrin Emoji endpointvolume ERole evntrace exdisp fdw FILEINFOSIG Filtergraph Filterx Functiondiscoverykeys guiddef HCERTSTORE hinstance hstring IKs iljxck Intelli ipreviewhandlervisualssetfont IYUV junja Knownfolders KSPROPERTY lcb ldx lld lmcons LONGLONG lpt LTRB majortype makecab MEDIASUBTYPE mediatype mfapi mfidl mfobjects mfplat mftransform mic Minimatch mjpg mmdeviceapi mmsystem msedge Msimg msiquery newdev nodoc notlike ntfs Objbase objidl ORAW outpin outputtype overlaywindow pathcch PAUDIO PINDIR Pnp ppmt Preinstalled previouscamera processthreadsapi PROPBAG propkey propvarutil redistributable reencode reencoded REFGUID REGFILTER REGFILTERPINS REGPINTYPES regsvr Renamer reparse restrictederrorinfo roadmap ruleset runtimes shellapi shellscalingapi shldisp shlobj shmem sizeread stl strsafe strutil subquery SWC SYNCMFT tailwindcss tapp thumbcache tlhelp TMPVAR Toolset touchpad Tsd uninstantiated uniquifier Unknwn unregistering urlmon USERDATA vcdl vdi verrsrc vid VIDCAP VIDEOINFOHEADER vih wcautil webcam wincodec Wincodecsdk windef windowsapp windowsx winerror winevt winexe winforms winsdkver winternl wistd wsl wtsapi WVC

To accept these unrecognized words as correct and remove the previously acknowledged and now absent words, you could run the following commands

... in a clone of the [email protected]:microsoft/PowerToys.git repository
on the zhaopengwang/UITestAPI branch (ℹ️ how do I use this?):

curl -s -S -L 'https://raw.githubusercontent.com/check-spelling/check-spelling/v0.0.24/apply.pl' |
perl - 'https://github.com/microsoft/PowerToys/actions/runs/12972293857/attempts/1'
If the flagged items are 🤯 false positives

If items relate to a ...

  • binary file (or some other file you wouldn't want to check at all).

    Please add a file path to the excludes.txt file matching the containing file.

    File paths are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your files.

    ^ refers to the file's path from the root of the repository, so ^README\.md$ would exclude README.md (on whichever branch you're using).

  • well-formed pattern.

    If you can write a pattern that would match it,
    try adding it to the patterns.txt file.

    Patterns are Perl 5 Regular Expressions - you can test yours before committing to verify it will match your lines.

    Note that patterns can't match multiline strings.

Please sign in to comment.