UITestAutomation Framework (#37461)

* Add UITestAutomation framework

* add code comments

* Optimized code format

* Optimized code format

* Update commons and add keyboard manager ui test project

* Optimized code format

* test scope and fix fancyzone exe path

* Add readme

* Optimize helper functions and UI test method

* Fix spelling errors and restore module UI tests

* Restore Indent

* Update NOTICE.md

* Update comments to Session and Elements

* Update comments for Button and Window

* delete unnecessary code

* change FindElementByName to FindElmenet

* Update comments for ModuleConfigData

* Update readme and comments

* Remove extra comments

* change public property

* Optimize code readability

* add default Attach Function

* change attach function name

* Update comments to XML format

* Hide by internal functions

* Update readme

* Refine the framework

* Fix process start position and update readme

* Remove Enum PowerToysModuleWindow

* Update attach comments

* Update ModuleConfigData comments

---------

Co-authored-by: Zhaopeng Wang (from Dev Box) <zhaopengwang@microsoft.com>
Co-authored-by: Xiaofeng Wang (from Dev Box) <xiaofengwang@microsoft.com>
Co-authored-by: urnotdfs <709586527@qq.com>
This commit is contained in:
dreamstart
2025-02-20 13:25:20 +08:00
committed by GitHub
parent a1a02889d5
commit 9453e38881
12 changed files with 889 additions and 0 deletions

View File

@@ -0,0 +1,130 @@
// 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.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Reflection;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Interactions;
namespace Microsoft.PowerToys.UITest
{
/// <summary>
/// Base class that should be inherited by all Test Classes.
/// </summary>
public class UITestBase
{
public Session Session { get; set; }
private readonly TestInit testInit = new TestInit();
public UITestBase(PowerToysModule scope = PowerToysModule.PowerToysSettings)
{
this.testInit.SetScope(scope);
this.testInit.Init();
this.Session = new Session(this.testInit.GetRoot(), this.testInit.GetDriver());
}
~UITestBase()
{
this.testInit.Cleanup();
}
/// <summary>
/// Nested class for test initialization.
/// </summary>
private sealed class TestInit
{
private WindowsDriver<WindowsElement> Root { get; set; }
private WindowsDriver<WindowsElement>? Driver { get; set; }
private static Process? appDriver;
// Default session path is PowerToys settings dashboard
private static string sessionPath = ModuleConfigData.Instance.GetModulePath(PowerToysModule.PowerToysSettings);
public TestInit()
{
appDriver = Process.Start(new ProcessStartInfo
{
FileName = "C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe",
Verb = "runas",
});
var desktopCapabilities = new AppiumOptions();
desktopCapabilities.AddAdditionalCapability("app", "Root");
this.Root = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), desktopCapabilities);
// Set default timeout to 5 seconds
this.Root.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
}
/// <summary>
/// Initializes the test environment.
/// </summary>
[UnconditionalSuppressMessage("SingleFile", "IL3000:Avoid accessing Assembly file path when publishing as a single file", Justification = "<Pending>")]
public void Init()
{
string? path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
this.StartExe(path + sessionPath);
Assert.IsNotNull(this.Driver, $"Failed to initialize the test environment. Driver is null.");
// Set default timeout to 5 seconds
this.Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
}
/// <summary>
/// Cleans up the test environment.
/// </summary>
public void Cleanup()
{
try
{
appDriver?.Kill();
}
catch (Exception ex)
{
// Handle exceptions if needed
Debug.WriteLine($"Exception during Cleanup: {ex.Message}");
}
}
/// <summary>
/// Starts a new exe and takes control of it.
/// </summary>
/// <param name="appPath">The path to the application executable.</param>
public void StartExe(string appPath)
{
var opts = new AppiumOptions();
opts.AddAdditionalCapability("app", appPath);
this.Driver = new WindowsDriver<WindowsElement>(new Uri(ModuleConfigData.Instance.GetWindowsApplicationDriverUrl()), opts);
}
/// <summary>
/// Sets scope to the Test Class.
/// </summary>
/// <param name="scope">The PowerToys module to start.</param>
public void SetScope(PowerToysModule scope)
{
sessionPath = ModuleConfigData.Instance.GetModulePath(scope);
}
public WindowsDriver<WindowsElement> GetRoot() => this.Root;
public WindowsDriver<WindowsElement> GetDriver()
{
Assert.IsNotNull(this.Driver, $"Failed to get driver. Driver is null.");
return this.Driver;
}
}
}
}